Merge branch 'be/feat/44-image' into 'be/develop'
Feat: 이미지 관련 권한 및 상태 변화 추가 - S11P21S002-44 See merge request s11-s-project/S11P21S002!57
This commit is contained in:
commit
bcc1519407
@ -4,6 +4,8 @@ import com.worlabel.domain.comment.entity.Comment;
|
|||||||
import com.worlabel.domain.comment.entity.dto.CommentRequest;
|
import com.worlabel.domain.comment.entity.dto.CommentRequest;
|
||||||
import com.worlabel.domain.comment.entity.dto.CommentResponse;
|
import com.worlabel.domain.comment.entity.dto.CommentResponse;
|
||||||
import com.worlabel.domain.comment.repository.CommentRepository;
|
import com.worlabel.domain.comment.repository.CommentRepository;
|
||||||
|
import com.worlabel.domain.folder.entity.Folder;
|
||||||
|
import com.worlabel.domain.folder.repository.FolderRepository;
|
||||||
import com.worlabel.domain.image.entity.Image;
|
import com.worlabel.domain.image.entity.Image;
|
||||||
import com.worlabel.domain.image.repository.ImageRepository;
|
import com.worlabel.domain.image.repository.ImageRepository;
|
||||||
import com.worlabel.domain.member.entity.Member;
|
import com.worlabel.domain.member.entity.Member;
|
||||||
@ -26,10 +28,11 @@ public class CommentService {
|
|||||||
private final ParticipantRepository participantRepository;
|
private final ParticipantRepository participantRepository;
|
||||||
private final MemberRepository memberRepository;
|
private final MemberRepository memberRepository;
|
||||||
private final ImageRepository imageRepository;
|
private final ImageRepository imageRepository;
|
||||||
|
private final FolderRepository folderRepository;
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<CommentResponse> getAllComments(final Integer memberId, final Integer projectId, final Long imageId) {
|
public List<CommentResponse> getAllComments(final Integer memberId, final Integer projectId, final Long imageId) {
|
||||||
checkAuthorized(memberId, projectId);
|
checkAuthorizedAndImageProjectRelation(memberId, projectId, imageId);
|
||||||
|
|
||||||
return commentRepository.findByImageId(imageId).stream()
|
return commentRepository.findByImageId(imageId).stream()
|
||||||
.map(CommentResponse::from)
|
.map(CommentResponse::from)
|
||||||
@ -38,14 +41,14 @@ public class CommentService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public CommentResponse getCommentById(final Integer memberId, final Integer projectId, final Integer commentId) {
|
public CommentResponse getCommentById(final Integer memberId, final Integer projectId, final Integer commentId) {
|
||||||
checkAuthorized(memberId, projectId);
|
checkAuthorizedAndCommentProjectRelation(memberId, projectId, commentId);
|
||||||
Comment comment = getComment(commentId);
|
Comment comment = getComment(commentId);
|
||||||
|
|
||||||
return CommentResponse.from(comment);
|
return CommentResponse.from(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommentResponse createComment(final CommentRequest commentRequest, Integer memberId, final Integer projectId, final Long imageId) {
|
public CommentResponse createComment(final CommentRequest commentRequest, Integer memberId, final Integer projectId, final Long imageId) {
|
||||||
checkAuthorized(memberId, projectId);
|
checkAuthorizedAndImageProjectRelation(memberId, projectId, imageId);
|
||||||
Member member = getMember(memberId);
|
Member member = getMember(memberId);
|
||||||
Image image = getImage(imageId);
|
Image image = getImage(imageId);
|
||||||
|
|
||||||
@ -67,6 +70,36 @@ public class CommentService {
|
|||||||
commentRepository.delete(comment);
|
commentRepository.delete(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 프로젝트에 속한 멤버인지 검증하고, 이미지가 해당 프로젝트에 속하는지 검증
|
||||||
|
*/
|
||||||
|
private void checkAuthorizedAndImageProjectRelation(final Integer memberId, final Integer projectId, final Long imageId) {
|
||||||
|
checkAuthorized(memberId, projectId);
|
||||||
|
Image image = getImage(imageId);
|
||||||
|
Folder folder = image.getFolder(); // 이미지가 속한 폴더를 가져옴
|
||||||
|
|
||||||
|
if (!folderRepository.existsByIdAndProjectId(folder.getId(), projectId)) {
|
||||||
|
throw new CustomException(ErrorCode.IMAGE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 프로젝트에 속한 멤버인지 검증하고, 코멘트가 속한 이미지가 해당 프로젝트에 속하는지 검증
|
||||||
|
*/
|
||||||
|
private void checkAuthorizedAndCommentProjectRelation(final Integer memberId, final Integer projectId, final Integer commentId) {
|
||||||
|
checkAuthorized(memberId, projectId);
|
||||||
|
Comment comment = getComment(commentId);
|
||||||
|
Image image = comment.getImage();
|
||||||
|
Folder folder = image.getFolder(); // 코멘트가 속한 이미지의 폴더를 가져옴
|
||||||
|
|
||||||
|
if (!folderRepository.existsByIdAndProjectId(folder.getId(), projectId)) {
|
||||||
|
throw new CustomException(ErrorCode.COMMENT_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 멤버가 해당 프로젝트에 참여하고 있는지, 권한이 있는지 검증
|
||||||
|
*/
|
||||||
private void checkAuthorized(final Integer memberId, final Integer projectId) {
|
private void checkAuthorized(final Integer memberId, final Integer projectId) {
|
||||||
if (!participantRepository.existsByMemberIdAndProjectId(memberId, projectId)) {
|
if (!participantRepository.existsByMemberIdAndProjectId(memberId, projectId)) {
|
||||||
throw new CustomException(ErrorCode.UNAUTHORIZED);
|
throw new CustomException(ErrorCode.UNAUTHORIZED);
|
||||||
|
@ -47,6 +47,18 @@ public class FolderController {
|
|||||||
return SuccessResponse.of(folderResponse);
|
return SuccessResponse.of(folderResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "폴더 하위 리뷰해야할 목록만 조회", description = "폴더하위 리뷰해야할 목록을 조회합니다.")
|
||||||
|
@SwaggerApiSuccess(description = "폴더 하위 리뷰해야할 목록을 성공적으로 조회합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.EMPTY_REQUEST_PARAMETER, ErrorCode.SERVER_ERROR})
|
||||||
|
@GetMapping("/{folder_id}/review")
|
||||||
|
public BaseResponse<FolderResponse> getFolderByIdWithNeedReview(
|
||||||
|
@CurrentUser final Integer memberId,
|
||||||
|
@PathVariable("project_id") final Integer projectId,
|
||||||
|
@PathVariable("folder_id") final Integer folderId) {
|
||||||
|
FolderResponse folderResponse = folderService.getFolderByIdWithNeedReview(memberId, projectId, folderId);
|
||||||
|
return SuccessResponse.of(folderResponse);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "폴더 수정", description = "폴더 정보를 수정합니다.")
|
@Operation(summary = "폴더 수정", description = "폴더 정보를 수정합니다.")
|
||||||
@SwaggerApiSuccess(description = "폴더를 성공적으로 수정합니다.")
|
@SwaggerApiSuccess(description = "폴더를 성공적으로 수정합니다.")
|
||||||
@SwaggerApiError({ErrorCode.EMPTY_REQUEST_PARAMETER, ErrorCode.SERVER_ERROR})
|
@SwaggerApiError({ErrorCode.EMPTY_REQUEST_PARAMETER, ErrorCode.SERVER_ERROR})
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.worlabel.domain.folder.entity.dto;
|
package com.worlabel.domain.folder.entity.dto;
|
||||||
|
|
||||||
import com.worlabel.domain.folder.entity.Folder;
|
import com.worlabel.domain.folder.entity.Folder;
|
||||||
|
import com.worlabel.domain.image.entity.LabelStatus;
|
||||||
import com.worlabel.domain.image.entity.dto.ImageResponse;
|
import com.worlabel.domain.image.entity.dto.ImageResponse;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@ -42,6 +43,24 @@ public class FolderResponse {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FolderResponse fromWithNeedReview(final Folder folder) {
|
||||||
|
List<ImageResponse> images = folder.getImageList().stream()
|
||||||
|
.filter(image -> image.getStatus() == LabelStatus.NEED_REVIEW)
|
||||||
|
.map(ImageResponse::from)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<FolderIdResponse> children = folder.getChildren().stream()
|
||||||
|
.map(FolderIdResponse::from)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return new FolderResponse(
|
||||||
|
folder.getId(),
|
||||||
|
folder.getTitle(),
|
||||||
|
images,
|
||||||
|
children
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static FolderResponse from(final List<Folder> topFolders) {
|
public static FolderResponse from(final List<Folder> topFolders) {
|
||||||
List<FolderIdResponse> list = topFolders.stream()
|
List<FolderIdResponse> list = topFolders.stream()
|
||||||
.map(FolderIdResponse::from)
|
.map(FolderIdResponse::from)
|
||||||
|
@ -33,7 +33,7 @@ public class FolderService {
|
|||||||
|
|
||||||
Folder parent = null;
|
Folder parent = null;
|
||||||
if (folderRequest.getParentId() != 0) {
|
if (folderRequest.getParentId() != 0) {
|
||||||
parent = getFolder(folderRequest.getParentId(),projectId);
|
parent = getFolder(folderRequest.getParentId(), projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Folder folder = Folder.of(folderRequest.getTitle(), parent, project);
|
Folder folder = Folder.of(folderRequest.getTitle(), parent, project);
|
||||||
@ -53,7 +53,7 @@ public class FolderService {
|
|||||||
if (folderId == 0) {
|
if (folderId == 0) {
|
||||||
return FolderResponse.from(folderRepository.findAllByProjectIdAndParentIsNull(projectId));
|
return FolderResponse.from(folderRepository.findAllByProjectIdAndParentIsNull(projectId));
|
||||||
} else {
|
} else {
|
||||||
return FolderResponse.from(getFolder(folderId,projectId));
|
return FolderResponse.from(getFolder(folderId, projectId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ public class FolderService {
|
|||||||
*/
|
*/
|
||||||
public FolderResponse updateFolder(final Integer memberId, final Integer projectId, final Integer folderId, final FolderRequest updatedFolderRequest) {
|
public FolderResponse updateFolder(final Integer memberId, final Integer projectId, final Integer folderId, final FolderRequest updatedFolderRequest) {
|
||||||
checkUnauthorized(memberId, projectId);
|
checkUnauthorized(memberId, projectId);
|
||||||
Folder folder = getFolder(folderId,projectId);
|
Folder folder = getFolder(folderId, projectId);
|
||||||
|
|
||||||
Folder parentFolder = folderRepository.findById(updatedFolderRequest.getParentId())
|
Folder parentFolder = folderRepository.findById(updatedFolderRequest.getParentId())
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
@ -77,17 +77,31 @@ public class FolderService {
|
|||||||
*/
|
*/
|
||||||
public void deleteFolder(final Integer memberId, final Integer projectId, final Integer folderId) {
|
public void deleteFolder(final Integer memberId, final Integer projectId, final Integer folderId) {
|
||||||
checkUnauthorized(memberId, projectId);
|
checkUnauthorized(memberId, projectId);
|
||||||
Folder folder = getFolder(folderId,projectId);
|
Folder folder = getFolder(folderId, projectId);
|
||||||
folderRepository.delete(folder);
|
folderRepository.delete(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 리뷰 목록만 조회
|
||||||
|
*/
|
||||||
|
public FolderResponse getFolderByIdWithNeedReview(final Integer memberId, final Integer projectId, final Integer folderId) {
|
||||||
|
checkExistParticipant(memberId, projectId);
|
||||||
|
|
||||||
|
// 최상위 폴더
|
||||||
|
if (folderId == 0) {
|
||||||
|
return FolderResponse.from(folderRepository.findAllByProjectIdAndParentIsNull(projectId));
|
||||||
|
} else {
|
||||||
|
return FolderResponse.fromWithNeedReview(getFolder(folderId, projectId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Project getProject(final Integer projectId) {
|
private Project getProject(final Integer projectId) {
|
||||||
return projectRepository.findById(projectId)
|
return projectRepository.findById(projectId)
|
||||||
.orElseThrow(() -> new CustomException(ErrorCode.PROJECT_NOT_FOUND));
|
.orElseThrow(() -> new CustomException(ErrorCode.PROJECT_NOT_FOUND));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Folder getFolder(final Integer folderId, final Integer projectId) {
|
private Folder getFolder(final Integer folderId, final Integer projectId) {
|
||||||
return folderRepository.findAllByProjectIdAndId(projectId,folderId)
|
return folderRepository.findAllByProjectIdAndId(projectId, folderId)
|
||||||
.orElseThrow(() -> new CustomException(ErrorCode.FOLDER_NOT_FOUND));
|
.orElseThrow(() -> new CustomException(ErrorCode.FOLDER_NOT_FOUND));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package com.worlabel.domain.image.controller;
|
|||||||
|
|
||||||
import com.worlabel.domain.image.entity.dto.ImageMoveRequest;
|
import com.worlabel.domain.image.entity.dto.ImageMoveRequest;
|
||||||
import com.worlabel.domain.image.entity.dto.ImageResponse;
|
import com.worlabel.domain.image.entity.dto.ImageResponse;
|
||||||
|
import com.worlabel.domain.image.entity.dto.ImageStatusRequest;
|
||||||
import com.worlabel.domain.image.service.ImageService;
|
import com.worlabel.domain.image.service.ImageService;
|
||||||
import com.worlabel.global.annotation.CurrentUser;
|
import com.worlabel.global.annotation.CurrentUser;
|
||||||
import com.worlabel.global.config.swagger.SwaggerApiError;
|
import com.worlabel.global.config.swagger.SwaggerApiError;
|
||||||
@ -88,4 +89,23 @@ public class ImageController {
|
|||||||
imageService.deleteImage(projectId, folderId, imageId, memberId);
|
imageService.deleteImage(projectId, folderId, imageId, memberId);
|
||||||
return SuccessResponse.empty();
|
return SuccessResponse.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{image_id}/status")
|
||||||
|
@SwaggerApiSuccess(description = "이미지 상태 변경.")
|
||||||
|
@Operation(summary = "이미지 상태 변경", description = "특정 이미지의 상태를 변경합니다.")
|
||||||
|
@SwaggerApiError({ErrorCode.BAD_REQUEST, ErrorCode.NOT_AUTHOR, ErrorCode.SERVER_ERROR, ErrorCode.PARTICIPANT_UNAUTHORIZED, ErrorCode.FOLDER_NOT_FOUND, ErrorCode.IMAGE_NOT_FOUND})
|
||||||
|
public BaseResponse<ImageResponse> changeImageStatus(
|
||||||
|
@CurrentUser final Integer memberId,
|
||||||
|
@PathVariable("folder_id") final Integer folderId,
|
||||||
|
@PathVariable("project_id") final Integer projectId,
|
||||||
|
@PathVariable("image_id") final Long imageId,
|
||||||
|
@RequestBody final ImageStatusRequest imageStatusRequest
|
||||||
|
) {
|
||||||
|
log.debug("project: {} , folder: {}, 수정하려는 이미지: {}, 현재 로그인 중인 사용자 : {}", projectId, folderId, imageId, memberId);
|
||||||
|
ImageResponse imageResponse = imageService.changeImageStatus(projectId, folderId, imageId, memberId, imageStatusRequest);
|
||||||
|
return SuccessResponse.of(imageResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,9 +47,9 @@ public class Image extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 이미지 레이블링 상태
|
* 이미지 레이블링 상태
|
||||||
*/
|
*/
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
private LabelStatus status = LabelStatus.Pending;
|
@Enumerated(EnumType.STRING)
|
||||||
|
private LabelStatus status = LabelStatus.PENDING;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 속한 폴더
|
* 속한 폴더
|
||||||
@ -73,10 +73,14 @@ public class Image extends BaseEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Image of(final String imageTitle, final String imageUrl, final Integer order, final Folder folder) {
|
public static Image of(final String imageTitle, final String imageUrl, final Integer order, final Folder folder) {
|
||||||
return new Image(imageTitle,imageUrl, order, folder);
|
return new Image(imageTitle, imageUrl, order, folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveFolder(final Folder moveFolder) {
|
public void moveFolder(final Folder moveFolder) {
|
||||||
this.folder = moveFolder;
|
this.folder = moveFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateStatus(final LabelStatus labelStatus) {
|
||||||
|
this.status = labelStatus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,22 @@
|
|||||||
package com.worlabel.domain.image.entity;
|
package com.worlabel.domain.image.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
public enum LabelStatus {
|
public enum LabelStatus {
|
||||||
Pending, IN_PROGRESS, NEED_REVIEW, COMPLETED
|
PENDING,
|
||||||
|
IN_PROGRESS,
|
||||||
|
NEED_REVIEW,
|
||||||
|
COMPLETED;
|
||||||
|
|
||||||
|
// 입력 값을 enum 값과 일치시키기 위해 대소문자 구분 없이 변환
|
||||||
|
@JsonCreator
|
||||||
|
public static LabelStatus from(String value) {
|
||||||
|
return LabelStatus.valueOf(value.toUpperCase()); // 입력된 값을 대문자로 변환
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
public String toValue() {
|
||||||
|
return name();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.worlabel.domain.image.entity.dto;
|
package com.worlabel.domain.image.entity.dto;
|
||||||
|
|
||||||
import com.worlabel.domain.image.entity.Image;
|
import com.worlabel.domain.image.entity.Image;
|
||||||
|
import com.worlabel.domain.image.entity.LabelStatus;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -15,15 +16,19 @@ public class ImageResponse {
|
|||||||
|
|
||||||
@Schema(description = "이미지 파일 제목", example = "image.jpg")
|
@Schema(description = "이미지 파일 제목", example = "image.jpg")
|
||||||
private String imageTitle;
|
private String imageTitle;
|
||||||
|
|
||||||
@Schema(description = "이미지 URL", example = "https://example.com/image.jpg")
|
@Schema(description = "이미지 URL", example = "https://example.com/image.jpg")
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
|
|
||||||
|
@Schema(description = "이미지 상태", example = "PENDING")
|
||||||
|
private LabelStatus status;
|
||||||
|
|
||||||
public static ImageResponse from(final Image image) {
|
public static ImageResponse from(final Image image) {
|
||||||
return new ImageResponse(
|
return new ImageResponse(
|
||||||
image.getId(),
|
image.getId(),
|
||||||
image.getTitle(),
|
image.getTitle(),
|
||||||
image.getImageUrl()
|
image.getImageUrl(),
|
||||||
|
image.getStatus()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.worlabel.domain.image.entity.dto;
|
||||||
|
|
||||||
|
import com.worlabel.domain.image.entity.LabelStatus;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Schema(name = "이미지 상태변경 요청 DTO", description = "이미지의 상태를 변경할때 사용되는 요청 DTO")
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ImageStatusRequest {
|
||||||
|
|
||||||
|
@Schema(description = "상태", example = "NEED_REVIEW")
|
||||||
|
@NotNull(message = "상태를 입력하세요.")
|
||||||
|
private LabelStatus labelStatus;
|
||||||
|
}
|
@ -4,7 +4,9 @@ import com.worlabel.domain.folder.entity.Folder;
|
|||||||
import com.worlabel.domain.folder.repository.FolderRepository;
|
import com.worlabel.domain.folder.repository.FolderRepository;
|
||||||
import com.worlabel.domain.image.entity.Image;
|
import com.worlabel.domain.image.entity.Image;
|
||||||
import com.worlabel.domain.image.entity.dto.ImageResponse;
|
import com.worlabel.domain.image.entity.dto.ImageResponse;
|
||||||
|
import com.worlabel.domain.image.entity.dto.ImageStatusRequest;
|
||||||
import com.worlabel.domain.image.repository.ImageRepository;
|
import com.worlabel.domain.image.repository.ImageRepository;
|
||||||
|
import com.worlabel.domain.participant.entity.Participant;
|
||||||
import com.worlabel.domain.participant.entity.PrivilegeType;
|
import com.worlabel.domain.participant.entity.PrivilegeType;
|
||||||
import com.worlabel.domain.participant.repository.ParticipantRepository;
|
import com.worlabel.domain.participant.repository.ParticipantRepository;
|
||||||
import com.worlabel.global.exception.CustomException;
|
import com.worlabel.global.exception.CustomException;
|
||||||
@ -33,6 +35,7 @@ public class ImageService {
|
|||||||
* 이미지 리스트 업로드
|
* 이미지 리스트 업로드
|
||||||
*/
|
*/
|
||||||
public void uploadImageList(final List<MultipartFile> imageList, final Integer folderId, final Integer projectId, final Integer memberId) {
|
public void uploadImageList(final List<MultipartFile> imageList, final Integer folderId, final Integer projectId, final Integer memberId) {
|
||||||
|
// 권한이 편집자 이상인지 확인
|
||||||
checkEditorParticipant(memberId, projectId);
|
checkEditorParticipant(memberId, projectId);
|
||||||
Folder folder = getFolder(folderId);
|
Folder folder = getFolder(folderId);
|
||||||
for (int order = 0; order < imageList.size(); order++) {
|
for (int order = 0; order < imageList.size(); order++) {
|
||||||
@ -48,8 +51,11 @@ public class ImageService {
|
|||||||
*/
|
*/
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ImageResponse getImageById(final Integer projectId, final Integer folderId, final Long imageId, final Integer memberId) {
|
public ImageResponse getImageById(final Integer projectId, final Integer folderId, final Long imageId, final Integer memberId) {
|
||||||
|
// 참가에 존재하는지 확인
|
||||||
checkExistParticipant(memberId, projectId);
|
checkExistParticipant(memberId, projectId);
|
||||||
Image image = getImage(folderId, imageId);
|
|
||||||
|
// 이미지가 해당 프로젝트에 속하는지 확인
|
||||||
|
Image image = getImageAndValidateProject(folderId, imageId, projectId);
|
||||||
return ImageResponse.from(image);
|
return ImageResponse.from(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,13 +69,15 @@ public class ImageService {
|
|||||||
final Long imageId,
|
final Long imageId,
|
||||||
final Integer memberId
|
final Integer memberId
|
||||||
) {
|
) {
|
||||||
|
// 권한이 편집자 이상인지 확인
|
||||||
checkEditorParticipant(memberId, projectId);
|
checkEditorParticipant(memberId, projectId);
|
||||||
Folder folder = null;
|
Folder folder = null;
|
||||||
if (moveFolderId != null) {
|
if (moveFolderId != null) {
|
||||||
folder = getFolder(moveFolderId);
|
folder = getFolder(moveFolderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Image image = getImage(folderId, imageId);
|
// 이미지가 해당 프로젝트에 속하는지 확인
|
||||||
|
Image image = getImageAndValidateProject(folderId, imageId, projectId);
|
||||||
image.moveFolder(folder);
|
image.moveFolder(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,31 +85,65 @@ public class ImageService {
|
|||||||
* 이미지 삭제
|
* 이미지 삭제
|
||||||
*/
|
*/
|
||||||
public void deleteImage(final Integer projectId, final Integer folderId, final Long imageId, final Integer memberId) {
|
public void deleteImage(final Integer projectId, final Integer folderId, final Long imageId, final Integer memberId) {
|
||||||
|
// 권한이 편집자 이상인지 확인
|
||||||
checkEditorParticipant(memberId, projectId);
|
checkEditorParticipant(memberId, projectId);
|
||||||
Image image = getImage(folderId, imageId);
|
|
||||||
|
// 이미지가 해당 프로젝트에 속하는지 확인
|
||||||
|
Image image = getImageAndValidateProject(folderId, imageId, projectId);
|
||||||
|
|
||||||
imageRepository.delete(image);
|
imageRepository.delete(image);
|
||||||
s3UploadService.deleteImageFromS3(image.getImageUrl());
|
s3UploadService.deleteImageFromS3(image.getImageUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 이미지 상태 변경
|
||||||
|
*/
|
||||||
|
public ImageResponse changeImageStatus(final Integer projectId, final Integer folderId, final Long imageId, final Integer memberId, final ImageStatusRequest imageStatusRequest) {
|
||||||
|
// 참가에 존재하는지 확인
|
||||||
|
checkExistParticipant(memberId, projectId);
|
||||||
|
|
||||||
|
// 이미지가 해당 프로젝트에 속하는지 확인
|
||||||
|
Image image = getImageAndValidateProject(folderId, imageId, projectId);
|
||||||
|
|
||||||
|
// 이미지 상태 변경 로직 추가 (생략)
|
||||||
|
image.updateStatus(imageStatusRequest.getLabelStatus());
|
||||||
|
|
||||||
|
return ImageResponse.from(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 참가에 존재하는지 확인
|
||||||
private void checkExistParticipant(final Integer memberId, final Integer projectId) {
|
private void checkExistParticipant(final Integer memberId, final Integer projectId) {
|
||||||
if (!participantRepository.existsByMemberIdAndProjectId(memberId, projectId)) {
|
if (!participantRepository.existsByMemberIdAndProjectId(memberId, projectId)) {
|
||||||
throw new CustomException(ErrorCode.BAD_REQUEST);
|
throw new CustomException(ErrorCode.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 편집자 이상의 권한을 확인하는 메서드
|
||||||
private void checkEditorParticipant(final Integer memberId, final Integer projectId) {
|
private void checkEditorParticipant(final Integer memberId, final Integer projectId) {
|
||||||
if (participantRepository.doesParticipantUnauthorizedExistByMemberIdAndProjectId(memberId,projectId)) {
|
Participant participant = participantRepository.findByMemberIdAndProjectId(memberId, projectId)
|
||||||
|
.orElseThrow(() -> new CustomException(ErrorCode.PARTICIPANT_UNAUTHORIZED));
|
||||||
|
|
||||||
|
if (!participant.getPrivilege().isEditeAuth()) {
|
||||||
throw new CustomException(ErrorCode.PARTICIPANT_UNAUTHORIZED);
|
throw new CustomException(ErrorCode.PARTICIPANT_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 폴더 가져오기
|
||||||
private Folder getFolder(final Integer folderId) {
|
private Folder getFolder(final Integer folderId) {
|
||||||
return folderRepository.findById(folderId)
|
return folderRepository.findById(folderId)
|
||||||
.orElseThrow(() -> new CustomException(ErrorCode.FOLDER_NOT_FOUND));
|
.orElseThrow(() -> new CustomException(ErrorCode.FOLDER_NOT_FOUND));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Image getImage(Integer folderId, Long imageId) {
|
// 이미지 가져오면서 프로젝트 소속 여부를 확인
|
||||||
return imageRepository.findByIdAndFolderId(imageId, folderId)
|
private Image getImageAndValidateProject(final Integer folderId, final Long imageId, final Integer projectId) {
|
||||||
|
Image image = imageRepository.findByIdAndFolderId(imageId, folderId)
|
||||||
.orElseThrow(() -> new CustomException(ErrorCode.IMAGE_NOT_FOUND));
|
.orElseThrow(() -> new CustomException(ErrorCode.IMAGE_NOT_FOUND));
|
||||||
|
|
||||||
|
// 이미지가 해당 프로젝트에 속하는지 확인
|
||||||
|
if (!image.getFolder().getProject().getId().equals(projectId)) {
|
||||||
|
throw new CustomException(ErrorCode.PROJECT_IMAGE_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,23 @@
|
|||||||
package com.worlabel.domain.participant.entity;
|
package com.worlabel.domain.participant.entity;
|
||||||
|
|
||||||
/**
|
import lombok.Getter;
|
||||||
* 프로젝트 참여 권한
|
|
||||||
*/
|
@Getter
|
||||||
public enum PrivilegeType {
|
public enum PrivilegeType {
|
||||||
ADMIN, MANAGER, EDITOR, VIEWER
|
ADMIN(4),
|
||||||
}
|
MANAGER(3),
|
||||||
|
EDITOR(2),
|
||||||
|
VIEWER(1);
|
||||||
|
|
||||||
|
private final int score;
|
||||||
|
|
||||||
|
// 점수를 부여하는 생성자
|
||||||
|
PrivilegeType(final int score) {
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 편집자 이상의 권한이 있는지 확인하는 메서드
|
||||||
|
public boolean isEditeAuth() {
|
||||||
|
return this.score >= EDITOR.getScore();
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.Query;
|
|||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface ParticipantRepository extends JpaRepository<Participant, Integer> {
|
public interface ParticipantRepository extends JpaRepository<Participant, Integer> {
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ public interface ParticipantRepository extends JpaRepository<Participant, Intege
|
|||||||
@Param("memberId") Integer memberId,
|
@Param("memberId") Integer memberId,
|
||||||
@Param("projectId") Integer projectId);
|
@Param("projectId") Integer projectId);
|
||||||
|
|
||||||
Participant findByMemberIdAndProjectId(Integer memberId, Integer projectId);
|
Optional<Participant> findByMemberIdAndProjectId(Integer memberId, Integer projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +98,8 @@ public class ProjectService {
|
|||||||
checkAdminParticipant(memberId, projectId);
|
checkAdminParticipant(memberId, projectId);
|
||||||
checkNotAdminParticipant(participantRequest.getMemberId(), projectId);
|
checkNotAdminParticipant(participantRequest.getMemberId(), projectId);
|
||||||
|
|
||||||
Participant participant = participantRepository.findByMemberIdAndProjectId(participantRequest.getMemberId(), projectId);
|
Participant participant = participantRepository.findByMemberIdAndProjectId(participantRequest.getMemberId(), projectId)
|
||||||
|
.orElseThrow(() -> new CustomException(ErrorCode.PARTICIPANT_UNAUTHORIZED));
|
||||||
participant.updatePrivilege(participantRequest.getPrivilegeType());
|
participant.updatePrivilege(participantRequest.getPrivilegeType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,11 +108,12 @@ public class ProjectService {
|
|||||||
checkAdminParticipant(memberId, projectId);
|
checkAdminParticipant(memberId, projectId);
|
||||||
checkNotAdminParticipant(removeMemberId, projectId);
|
checkNotAdminParticipant(removeMemberId, projectId);
|
||||||
|
|
||||||
Participant participant = participantRepository.findByMemberIdAndProjectId(removeMemberId, projectId);
|
Participant participant = participantRepository.findByMemberIdAndProjectId(removeMemberId, projectId)
|
||||||
|
.orElseThrow(() -> new CustomException(ErrorCode.PARTICIPANT_UNAUTHORIZED));
|
||||||
|
|
||||||
participantRepository.delete(participant);
|
participantRepository.delete(participant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Workspace getWorkspace(final Integer memberId, final Integer workspaceId) {
|
private Workspace getWorkspace(final Integer memberId, final Integer workspaceId) {
|
||||||
return workspaceRepository.findByMemberIdAndId(memberId, workspaceId)
|
return workspaceRepository.findByMemberIdAndId(memberId, workspaceId)
|
||||||
.orElseThrow(() -> new CustomException(ErrorCode.WORKSPACE_NOT_FOUND));
|
.orElseThrow(() -> new CustomException(ErrorCode.WORKSPACE_NOT_FOUND));
|
||||||
|
@ -36,6 +36,7 @@ public enum ErrorCode {
|
|||||||
|
|
||||||
// Project - 4000
|
// Project - 4000
|
||||||
PROJECT_NOT_FOUND(HttpStatus.NOT_FOUND, 4000, "프로젝트를 찾을 수 없습니다"),
|
PROJECT_NOT_FOUND(HttpStatus.NOT_FOUND, 4000, "프로젝트를 찾을 수 없습니다"),
|
||||||
|
PROJECT_IMAGE_MISMATCH(HttpStatus.BAD_REQUEST, 4001, "해당 프로젝트에는 존재하지 않는 이미지입니다."),
|
||||||
|
|
||||||
// Participant - 5000,
|
// Participant - 5000,
|
||||||
PARTICIPANT_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 5000, "해당 프로젝트에 접근 권한이 없습니다."),
|
PARTICIPANT_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 5000, "해당 프로젝트에 접근 권한이 없습니다."),
|
||||||
@ -51,10 +52,9 @@ public enum ErrorCode {
|
|||||||
// AI - 8000
|
// AI - 8000
|
||||||
AI_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 8000, "AI 서버 오류 입니다."),
|
AI_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 8000, "AI 서버 오류 입니다."),
|
||||||
|
|
||||||
// Comment - 9000
|
// Comment - 9000,
|
||||||
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, 9000, "해당 댓글을 찾을 수 없습니다."),
|
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, 9000, "해당 댓글을 찾을 수 없습니다.");
|
||||||
|
|
||||||
;
|
|
||||||
private final HttpStatus status;
|
private final HttpStatus status;
|
||||||
private final int code;
|
private final int code;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
Loading…
Reference in New Issue
Block a user