diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileController.java new file mode 100644 index 0000000..76c705a --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileController.java @@ -0,0 +1,71 @@ +package com.dreamteam.alter.adapter.inbound.general.file.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; +import com.dreamteam.alter.adapter.inbound.general.file.dto.AppUploadFileResponseDto; +import com.dreamteam.alter.application.aop.AppActionContext; +import com.dreamteam.alter.domain.file.port.inbound.AppDeleteFileUseCase; +import com.dreamteam.alter.domain.file.port.inbound.AppGetPresignedUrlUseCase; +import com.dreamteam.alter.domain.file.port.inbound.AppUploadFileUseCase; +import com.dreamteam.alter.domain.file.type.BucketType; +import com.dreamteam.alter.domain.file.type.FileTargetType; +import com.dreamteam.alter.domain.user.context.AppActor; + +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; + + +@RestController +@RequestMapping("/app/files") +@RequiredArgsConstructor +public class AppFileController implements AppFileControllerSpec { + + @Resource(name = "appUploadFile") + private final AppUploadFileUseCase appUploadFile; + + @Resource(name = "appGetPresignedUrl") + private final AppGetPresignedUrlUseCase appGetPresignedUrl; + + @Resource(name = "appDeleteFile") + private final AppDeleteFileUseCase appDeleteFile; + + @Override + @PostMapping + public ResponseEntity> uploadFile( + @RequestParam("file") MultipartFile file, + @RequestParam("targetType") FileTargetType targetType, + @RequestParam("bucketType") BucketType bucketType + ) { + AppActor actor = AppActionContext.getInstance().getActor(); + return ResponseEntity.ok(CommonApiResponse.of(appUploadFile.execute(actor, file, targetType, bucketType))); + } + + @Override + @GetMapping("/{fileId}/presigned-url") + public ResponseEntity> getPresignedUrl( + @PathVariable String fileId + ) { + AppActor actor = AppActionContext.getInstance().getActor(); + return ResponseEntity.ok(CommonApiResponse.of(appGetPresignedUrl.execute(actor, fileId))); + } + + @Override + @DeleteMapping("/{fileId}") + public ResponseEntity> deleteFile( + @PathVariable String fileId + ) { + AppActor actor = AppActionContext.getInstance().getActor(); + appDeleteFile.execute(actor, fileId); + return ResponseEntity.ok(CommonApiResponse.empty()); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileControllerSpec.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileControllerSpec.java new file mode 100644 index 0000000..948e6be --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileControllerSpec.java @@ -0,0 +1,89 @@ +package com.dreamteam.alter.adapter.inbound.general.file.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.common.dto.ErrorResponse; +import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; +import com.dreamteam.alter.adapter.inbound.general.file.dto.AppUploadFileResponseDto; +import com.dreamteam.alter.domain.file.type.BucketType; +import com.dreamteam.alter.domain.file.type.FileTargetType; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; + + +@Tag(name = "APP - 파일 API") +public interface AppFileControllerSpec { + + @Operation(summary = "파일 업로드", description = "파일을 S3에 업로드하고 PENDING 상태로 저장합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "파일 업로드 성공"), + @ApiResponse(responseCode = "400", description = "실패 케이스", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject(name = "유효하지 않은 파일", value = "{\"code\" : \"B022\"}"), + @ExampleObject(name = "허용되지 않는 파일 형식", value = "{\"code\" : \"B023\"}"), + @ExampleObject(name = "파일 크기 초과", value = "{\"code\" : \"B024\"}") + })) + }) + ResponseEntity> uploadFile( + @RequestParam("file") MultipartFile file, + @RequestParam("targetType") FileTargetType targetType, + @RequestParam("bucketType") BucketType bucketType + ); + + @Operation(summary = "Presigned URL 조회", description = "본인이 업로드한 Private 파일 접근을 위한 Presigned URL을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Presigned URL 조회 성공"), + @ApiResponse(responseCode = "403", description = "권한 없음", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject(name = "본인 파일 아님", value = "{\"code\" : \"A005\"}") + })), + @ApiResponse(responseCode = "404", description = "존재하지 않는 파일", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject(name = "파일 없음", value = "{\"code\" : \"B021\"}") + })) + }) + ResponseEntity> getPresignedUrl( + @PathVariable String fileId + ); + + @Operation(summary = "파일 삭제", description = "본인이 업로드한 파일을 S3에서 삭제하고 DELETED 상태로 변경합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "파일 삭제 성공"), + @ApiResponse(responseCode = "403", description = "권한 없음", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject(name = "본인 파일 아님", value = "{\"code\" : \"A005\"}") + })), + @ApiResponse(responseCode = "404", description = "존재하지 않는 파일", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject(name = "파일 없음", value = "{\"code\" : \"B021\"}") + })) + }) + ResponseEntity> deleteFile( + @PathVariable String fileId + ); +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/dto/AppUploadFileResponseDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/dto/AppUploadFileResponseDto.java new file mode 100644 index 0000000..a15ff4e --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/file/dto/AppUploadFileResponseDto.java @@ -0,0 +1,21 @@ +package com.dreamteam.alter.adapter.inbound.general.file.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Schema(description = "파일 업로드 응답 DTO") +public class AppUploadFileResponseDto { + + @Schema(description = "파일 ID", example = "01959b4e-4e5f-7c3a-8d9e-0f1a2b3c4d5e") + private String fileId; + + public static AppUploadFileResponseDto of(String fileId) { + return new AppUploadFileResponseDto(fileId); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceController.java new file mode 100644 index 0000000..33f22d9 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceController.java @@ -0,0 +1,38 @@ +package com.dreamteam.alter.adapter.inbound.general.workspace.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; +import com.dreamteam.alter.application.aop.AppActionContext; +import com.dreamteam.alter.domain.user.context.AppActor; +import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceUseCase; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/app/workspaces") +@RequiredArgsConstructor +@Validated +public class UserWorkspaceController implements UserWorkspaceControllerSpec { + + @Resource(name = "createWorkspace") + private final CreateWorkspaceUseCase createWorkspace; + + @Override + @PostMapping + public ResponseEntity> createWorkspace( + @RequestBody @Valid CreateWorkspaceRequestDto request + ) { + AppActor actor = AppActionContext.getInstance().getActor(); + createWorkspace.execute(actor, request); + return ResponseEntity.ok(CommonApiResponse.empty()); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceControllerSpec.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceControllerSpec.java new file mode 100644 index 0000000..a5e63fd --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceControllerSpec.java @@ -0,0 +1,27 @@ +package com.dreamteam.alter.adapter.inbound.general.workspace.controller; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; + +@Tag(name = "APP - 업장 API") +public interface UserWorkspaceControllerSpec { + + @Operation(summary = "업장 등록 신청") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "업장 등록 신청 성공"), + @ApiResponse(responseCode = "400", description = "유효하지 않은 파일입니다. (INVALID_FILE)"), + @ApiResponse(responseCode = "403", description = "접근 권한이 없습니다. (FORBIDDEN)"), + @ApiResponse(responseCode = "404", description = "존재하지 않는 파일입니다. (FILE_NOT_FOUND)"), + @ApiResponse(responseCode = "409", description = "이미 연결된 파일입니다. (FILE_ALREADY_ATTACHED)") + }) + ResponseEntity> createWorkspace( + @RequestBody @Valid CreateWorkspaceRequestDto request + ); +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/CreateWorkspaceRequestDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/CreateWorkspaceRequestDto.java new file mode 100644 index 0000000..ba6b9a6 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/CreateWorkspaceRequestDto.java @@ -0,0 +1,68 @@ +package com.dreamteam.alter.adapter.inbound.general.workspace.dto; + +import java.math.BigDecimal; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "업장 등록 신청 DTO") +public class CreateWorkspaceRequestDto { + + @NotBlank + @Schema(description = "업장 이름", example = "세븐일레븐") + private String bizName; + + @NotBlank + @Schema(description = "사업자 등록번호", example = "123-45-12345") + private String brn; + + @NotBlank + @Schema(description = "풀주소", example = "서울특별시 구로구 고척동 123") + private String address; + + @NotBlank + @Schema(description = "시/도", example = "서울특별시") + private String province; + + @NotBlank + @Schema(description = "구", example = "구로구") + private String district; + + @NotBlank + @Schema(description = "동", example = "고척동") + private String town; + + @NotNull + @Schema(description = "위도", example = "35.150485") + private BigDecimal latitude; + + @NotNull + @Schema(description = "경도", example = "129.115717") + private BigDecimal longitude; + + @NotBlank + @Schema(description = "업장 형태", example = "음식점") + private String type; + + @NotBlank + @Schema(description = "업장 연락처", example = "02-1234-5678") + private String contact; + + @NotBlank + @Schema(description = "사업자등록증명원 파일 ID", example = "01959b4e-4e5f-7c3a-8d9e-0f1a2b3c4d5e") + private String workspaceCertFileId; + + @NotBlank + @Schema(description = "대표자 신분증 사본 파일 ID", example = "01959b4e-4e5f-7c3a-8d9e-0f1a2b3c4d5e") + private String workspaceOwnIdentityFileId; + + @Schema(description = "위임 확인서 파일 ID", example = "01959b4e-4e5f-7c3a-8d9e-0f1a2b3c4d5e") + private String workspaceWarrantFileId; +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java new file mode 100644 index 0000000..17114c4 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java @@ -0,0 +1,62 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.controller; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.CreateWorkspaceReasonCommentRequestDto; +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.WorkspaceReasonCommentResponseDto; +import com.dreamteam.alter.application.aop.ManagerActionContext; +import com.dreamteam.alter.domain.user.context.ManagerActor; +import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceReasonCommentUseCase; +import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceReasonCommentsUseCase; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/manager/workspaces/{workspaceId}/reasons/{reasonId}/comments") +@PreAuthorize("hasAnyRole('MANAGER')") +@RequiredArgsConstructor +@Validated +public class ManagerWorkspaceReasonCommentController implements ManagerWorkspaceReasonCommentControllerSpec { + + @Resource(name = "createWorkspaceReasonComment") + private final CreateWorkspaceReasonCommentUseCase createWorkspaceReasonComment; + + @Resource(name = "getWorkspaceReasonComments") + private final GetWorkspaceReasonCommentsUseCase getWorkspaceReasonComments; + + @Override + @PostMapping + public ResponseEntity> createWorkspaceReasonComment( + @PathVariable Long workspaceId, + @PathVariable Long reasonId, + @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request + ) { + ManagerActor actor = ManagerActionContext.getInstance().getActor(); + createWorkspaceReasonComment.execute(actor, workspaceId, reasonId, request); + return ResponseEntity.ok(CommonApiResponse.empty()); + } + + @Override + @GetMapping + public ResponseEntity>> getWorkspaceReasonComments( + @PathVariable Long workspaceId, + @PathVariable Long reasonId + ) { + ManagerActor actor = ManagerActionContext.getInstance().getActor(); + return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceReasonComments.execute(actor, workspaceId, reasonId))); + } + +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java new file mode 100644 index 0000000..487c838 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java @@ -0,0 +1,42 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.controller; + +import java.util.List; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.CreateWorkspaceReasonCommentRequestDto; +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.WorkspaceReasonCommentResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + +@Tag(name = "MANAGER - 업장 승인/반려 사유 코멘트 API") +public interface ManagerWorkspaceReasonCommentControllerSpec { + + @Operation(summary = "매니저 - 승인/반려 사유 코멘트 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "코멘트 등록 성공"), + @ApiResponse(responseCode = "403", description = "해당 업장의 관리자가 아님 (A002)"), + @ApiResponse(responseCode = "404", description = "존재하지 않는 사유 (B019)") + }) + ResponseEntity> createWorkspaceReasonComment( + @PathVariable Long workspaceId, + @PathVariable Long reasonId, + @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request + ); + + @Operation(summary = "매니저 - 승인/반려 사유 코멘트 목록 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "코멘트 목록 조회 성공"), + @ApiResponse(responseCode = "403", description = "해당 업장의 관리자가 아님 (A002)"), + @ApiResponse(responseCode = "404", description = "존재하지 않는 사유 (B019)") + }) + ResponseEntity>> getWorkspaceReasonComments( + @PathVariable Long workspaceId, + @PathVariable Long reasonId + ); +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/CreateWorkspaceReasonCommentRequestDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/CreateWorkspaceReasonCommentRequestDto.java new file mode 100644 index 0000000..d8b64ab --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/CreateWorkspaceReasonCommentRequestDto.java @@ -0,0 +1,20 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "매니저 - 승인/반려 사유 댓글 등록 DTO") +public class CreateWorkspaceReasonCommentRequestDto { + + @Schema(description = "댓글 내용", example = "왜 반려죠") + @Max(255) + @NotBlank + private String comment; +} diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java new file mode 100644 index 0000000..db09bae --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java @@ -0,0 +1,31 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.dto; + +import java.time.LocalDateTime; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Builder(access = AccessLevel.PRIVATE) +@Schema(description = "업장 등록 요청 승인/반려 댓글 조회 DTO") +public class WorkspaceReasonCommentResponseDto { + private Long id; + private String comment; + private LocalDateTime createdAt; + + public static WorkspaceReasonCommentResponseDto from(WorkspaceReasonComment entity) { + return new WorkspaceReasonCommentResponseDto( + entity.getId(), + entity.getComment(), + entity.getCreatedAt() + ); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserJpaRepository.java b/src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserJpaRepository.java new file mode 100644 index 0000000..d63ee82 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserJpaRepository.java @@ -0,0 +1,7 @@ +package com.dreamteam.alter.adapter.outbound.user.persistence; + +import com.dreamteam.alter.domain.user.entity.ManagerUser; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ManagerUserJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserRepositoryImpl.java new file mode 100644 index 0000000..292a39e --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserRepositoryImpl.java @@ -0,0 +1,18 @@ +package com.dreamteam.alter.adapter.outbound.user.persistence; + +import com.dreamteam.alter.domain.user.entity.ManagerUser; +import com.dreamteam.alter.domain.user.port.outbound.ManagerUserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class ManagerUserRepositoryImpl implements ManagerUserRepository { + + private final ManagerUserJpaRepository managerUserJpaRepository; + + @Override + public ManagerUser save(ManagerUser managerUser) { + return managerUserJpaRepository.save(managerUser); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentJpaRepository.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentJpaRepository.java new file mode 100644 index 0000000..05125b9 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentJpaRepository.java @@ -0,0 +1,10 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; + +@Repository +public interface WorkspaceReasonCommentJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentQueryRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentQueryRepositoryImpl.java new file mode 100644 index 0000000..c416371 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentQueryRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import java.util.List; + +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.QWorkspaceReasonComment; +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonCommentQueryRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class WorkspaceReasonCommentQueryRepositoryImpl implements WorkspaceReasonCommentQueryRepository { + + private final JPAQueryFactory queryFactory; + + @Override + public List getCommentsByReasonId(Long reasonId) { + QWorkspaceReasonComment q = QWorkspaceReasonComment.workspaceReasonComment; + + return queryFactory + .selectFrom(q) + .where(q.workspaceReason.id.eq(reasonId)) + .orderBy(q.createdAt.asc(), q.id.asc()) + .fetch(); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentRepositoryImpl.java new file mode 100644 index 0000000..2e8b63e --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentRepositoryImpl.java @@ -0,0 +1,20 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonCommentRepository; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class WorkspaceReasonCommentRepositoryImpl implements WorkspaceReasonCommentRepository { + + private final WorkspaceReasonCommentJpaRepository workspaceReasonCommentJpaRepository; + + @Override + public void save(WorkspaceReasonComment comment) { + workspaceReasonCommentJpaRepository.save(comment); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonJpaRepository.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonJpaRepository.java new file mode 100644 index 0000000..52bf491 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonJpaRepository.java @@ -0,0 +1,10 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReason; + +@Repository +public interface WorkspaceReasonJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonQueryRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonQueryRepositoryImpl.java new file mode 100644 index 0000000..2c6d9b5 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonQueryRepositoryImpl.java @@ -0,0 +1,33 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import java.util.Optional; + +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.QWorkspaceReason; +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReason; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonQueryRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class WorkspaceReasonQueryRepositoryImpl implements WorkspaceReasonQueryRepository { + + private final JPAQueryFactory queryFactory; + + @Override + public Optional findByIdAndWorkspaceId(Long reasonId, Long workspaceId) { + QWorkspaceReason qWorkspaceReason = QWorkspaceReason.workspaceReason; + return Optional.ofNullable( + queryFactory + .selectFrom(qWorkspaceReason) + .where( + qWorkspaceReason.id.eq(reasonId), + qWorkspaceReason.workspace.id.eq(workspaceId) + ) + .fetchOne() + ); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRepositoryImpl.java new file mode 100644 index 0000000..259c50d --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRepositoryImpl.java @@ -0,0 +1,20 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.Workspace; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRepository; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class WorkspaceRepositoryImpl implements WorkspaceRepository { + + private final WorkspaceJpaRepository workspaceJpaRepository; + + @Override + public Long save(Workspace workspace) { + return workspaceJpaRepository.save(workspace).getId(); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/file/usecase/AppDeleteFile.java b/src/main/java/com/dreamteam/alter/application/file/usecase/AppDeleteFile.java new file mode 100644 index 0000000..8661b7d --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/file/usecase/AppDeleteFile.java @@ -0,0 +1,35 @@ +package com.dreamteam.alter.application.file.usecase; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dreamteam.alter.application.file.FileDeleteService; +import com.dreamteam.alter.common.exception.CustomException; +import com.dreamteam.alter.common.exception.ErrorCode; +import com.dreamteam.alter.domain.file.entity.File; +import com.dreamteam.alter.domain.file.port.inbound.AppDeleteFileUseCase; +import com.dreamteam.alter.domain.file.port.outbound.FileQueryRepository; +import com.dreamteam.alter.domain.user.context.AppActor; + +import lombok.RequiredArgsConstructor; + +@Service("appDeleteFile") +@RequiredArgsConstructor +public class AppDeleteFile implements AppDeleteFileUseCase { + + private final FileQueryRepository fileQueryRepository; + private final FileDeleteService fileDeleteService; + + @Override + @Transactional + public void execute(AppActor actor, String fileId) { + File file = fileQueryRepository.findById(fileId) + .orElseThrow(() -> new CustomException(ErrorCode.FILE_NOT_FOUND)); + + if (!file.getUploadedBy().equals(actor.getUserId())) { + throw new CustomException(ErrorCode.FORBIDDEN); + } + + fileDeleteService.delete(file); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/file/usecase/AppGetPresignedUrl.java b/src/main/java/com/dreamteam/alter/application/file/usecase/AppGetPresignedUrl.java new file mode 100644 index 0000000..d281d3f --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/file/usecase/AppGetPresignedUrl.java @@ -0,0 +1,36 @@ +package com.dreamteam.alter.application.file.usecase; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; +import com.dreamteam.alter.application.file.FileUrlService; +import com.dreamteam.alter.common.exception.CustomException; +import com.dreamteam.alter.common.exception.ErrorCode; +import com.dreamteam.alter.domain.file.entity.File; +import com.dreamteam.alter.domain.file.port.inbound.AppGetPresignedUrlUseCase; +import com.dreamteam.alter.domain.file.port.outbound.FileQueryRepository; +import com.dreamteam.alter.domain.user.context.AppActor; + +import lombok.RequiredArgsConstructor; + +@Service("appGetPresignedUrl") +@RequiredArgsConstructor +public class AppGetPresignedUrl implements AppGetPresignedUrlUseCase { + + private final FileQueryRepository fileQueryRepository; + private final FileUrlService fileUrlService; + + @Override + @Transactional(readOnly = true) + public FilePresignedUrlResponseDto execute(AppActor actor, String fileId) { + File file = fileQueryRepository.findById(fileId) + .orElseThrow(() -> new CustomException(ErrorCode.FILE_NOT_FOUND)); + + if (!file.getUploadedBy().equals(actor.getUserId())) { + throw new CustomException(ErrorCode.FORBIDDEN); + } + + return FilePresignedUrlResponseDto.of(fileUrlService.getPresignedUrl(file)); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/file/usecase/AppUploadFile.java b/src/main/java/com/dreamteam/alter/application/file/usecase/AppUploadFile.java new file mode 100644 index 0000000..7543947 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/file/usecase/AppUploadFile.java @@ -0,0 +1,30 @@ +package com.dreamteam.alter.application.file.usecase; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import com.dreamteam.alter.adapter.inbound.general.file.dto.AppUploadFileResponseDto; +import com.dreamteam.alter.application.file.FileUploadService; +import com.dreamteam.alter.domain.file.port.inbound.AppUploadFileUseCase; +import com.dreamteam.alter.domain.file.type.BucketType; +import com.dreamteam.alter.domain.file.type.FileTargetType; +import com.dreamteam.alter.domain.user.context.AppActor; + +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; + +@Service("appUploadFile") +@RequiredArgsConstructor +public class AppUploadFile implements AppUploadFileUseCase { + + @Resource(name = "fileUploadService") + private final FileUploadService fileUploadService; + + @Override + @Transactional + public AppUploadFileResponseDto execute(AppActor actor, MultipartFile file, FileTargetType targetType, BucketType bucketType) { + String fileId = fileUploadService.upload(file, targetType, bucketType, actor.getUserId()); + return AppUploadFileResponseDto.of(fileId); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/file/usecase/AttachFiles.java b/src/main/java/com/dreamteam/alter/application/file/usecase/AttachFiles.java index 999cdbe..bb50bcb 100644 --- a/src/main/java/com/dreamteam/alter/application/file/usecase/AttachFiles.java +++ b/src/main/java/com/dreamteam/alter/application/file/usecase/AttachFiles.java @@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Map; @Service("attachFiles") @RequiredArgsConstructor @@ -29,16 +30,35 @@ public void execute(List fileIds, FileTargetType targetType, String targ } for (File file : files) { - if (!file.getUploadedBy().equals(userId)) { - throw new CustomException(ErrorCode.FORBIDDEN); - } - if (file.getStatus() != FileStatus.PENDING) { - throw new CustomException(ErrorCode.FILE_ALREADY_ATTACHED); - } - if (file.getTargetType() != targetType) { - throw new CustomException(ErrorCode.INVALID_FILE); - } + validateFile(file, userId, targetType); file.attach(targetId); } } + + @Override + @Transactional + public void executeMap(Map fileIdsMap, String targetId, Long userId) { + List files = fileQueryRepository.findAllByIdIn(fileIdsMap.keySet().stream().toList()); + + if (files.size() != fileIdsMap.size()) { + throw new CustomException(ErrorCode.FILE_NOT_FOUND); + } + + for (File file : files) { + validateFile(file, userId, fileIdsMap.get(file.getId())); + file.attach(targetId); + } + } + + private void validateFile(File file, Long userId, FileTargetType targetType) { + if (!file.getUploadedBy().equals(userId)) { + throw new CustomException(ErrorCode.FORBIDDEN); + } + if (file.getStatus() != FileStatus.PENDING) { + throw new CustomException(ErrorCode.FILE_ALREADY_ATTACHED); + } + if (file.getTargetType() != targetType) { + throw new CustomException(ErrorCode.INVALID_FILE); + } + } } diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java new file mode 100644 index 0000000..a5b778b --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java @@ -0,0 +1,65 @@ +package com.dreamteam.alter.application.workspace.usecase; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; +import com.dreamteam.alter.domain.file.port.inbound.AttachFilesUseCase; +import com.dreamteam.alter.domain.file.type.FileTargetType; +import com.dreamteam.alter.domain.user.context.AppActor; +import com.dreamteam.alter.domain.user.entity.ManagerUser; +import com.dreamteam.alter.domain.user.port.outbound.ManagerUserRepository; +import com.dreamteam.alter.domain.user.type.ManagerUserStatus; +import com.dreamteam.alter.domain.workspace.entity.Workspace; +import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceUseCase; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRepository; +import com.dreamteam.alter.domain.workspace.type.WorkspaceStatus; + +import lombok.RequiredArgsConstructor; + +@Service("createWorkspace") +@RequiredArgsConstructor +@Transactional +public class CreateWorkspace implements CreateWorkspaceUseCase { + + private final ManagerUserRepository managerUserRepository; + private final WorkspaceRepository workspaceRepository; + private final AttachFilesUseCase attachFiles; + + @Override + public void execute(AppActor actor, CreateWorkspaceRequestDto request) { + ManagerUser managerUser = managerUserRepository.save( + ManagerUser.create(actor.getUser(), ManagerUserStatus.PENDING) + ); + + Workspace workspace = Workspace.create( + managerUser, + request.getBrn(), + request.getBizName(), + request.getType(), + request.getContact(), + null, + WorkspaceStatus.PENDING, + request.getAddress(), + request.getProvince(), + request.getDistrict(), + request.getTown(), + request.getLatitude(), + request.getLongitude() + ); + + String savedWorkspaceId = workspaceRepository.save(workspace).toString(); + Long userId = actor.getUserId(); + + Map fileMap = new HashMap<>(); + fileMap.put(request.getWorkspaceCertFileId(), FileTargetType.WORKSPACE_CERTIFICATE); + fileMap.put(request.getWorkspaceOwnIdentityFileId(), FileTargetType.WORKSPACE_OWN_IDENTITY); + if (request.getWorkspaceWarrantFileId() != null) { + fileMap.put(request.getWorkspaceWarrantFileId(), FileTargetType.WORKSPACE_WARRANT); + } + attachFiles.executeMap(fileMap, savedWorkspaceId, userId); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java new file mode 100644 index 0000000..1aa8f6f --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java @@ -0,0 +1,40 @@ +package com.dreamteam.alter.application.workspace.usecase; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.CreateWorkspaceReasonCommentRequestDto; +import com.dreamteam.alter.common.exception.CustomException; +import com.dreamteam.alter.common.exception.ErrorCode; +import com.dreamteam.alter.domain.user.context.ManagerActor; +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReason; +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; +import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceReasonCommentUseCase; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceQueryRepository; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonCommentRepository; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonQueryRepository; + +import lombok.RequiredArgsConstructor; + +@Service("createWorkspaceReasonComment") +@RequiredArgsConstructor +@Transactional +public class CreateWorkspaceReasonComment implements CreateWorkspaceReasonCommentUseCase { + + private final WorkspaceQueryRepository workspaceQueryRepository; + private final WorkspaceReasonQueryRepository workspaceReasonQueryRepository; + private final WorkspaceReasonCommentRepository workspaceReasonCommentRepository; + + @Override + public void execute(ManagerActor actor, Long workspaceId, Long reasonId, CreateWorkspaceReasonCommentRequestDto request) { + if (!workspaceQueryRepository.existsByIdAndManagerUser(workspaceId, actor.getManagerUser())) { + throw new CustomException(ErrorCode.FORBIDDEN); + } + + WorkspaceReason reason = workspaceReasonQueryRepository.findByIdAndWorkspaceId(reasonId, workspaceId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + + WorkspaceReasonComment comment = WorkspaceReasonComment.create(reason, request.getComment()); + workspaceReasonCommentRepository.save(comment); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java new file mode 100644 index 0000000..2c6ca8a --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java @@ -0,0 +1,42 @@ +package com.dreamteam.alter.application.workspace.usecase; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.WorkspaceReasonCommentResponseDto; +import com.dreamteam.alter.common.exception.CustomException; +import com.dreamteam.alter.common.exception.ErrorCode; +import com.dreamteam.alter.domain.user.context.ManagerActor; +import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceReasonCommentsUseCase; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceQueryRepository; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonCommentQueryRepository; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceReasonQueryRepository; + +import lombok.RequiredArgsConstructor; + +@Service("getWorkspaceReasonComments") +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GetWorkspaceReasonComments implements GetWorkspaceReasonCommentsUseCase { + + private final WorkspaceQueryRepository workspaceQueryRepository; + private final WorkspaceReasonQueryRepository workspaceReasonQueryRepository; + private final WorkspaceReasonCommentQueryRepository workspaceReasonCommentQueryRepository; + + @Override + public List execute(ManagerActor actor, Long workspaceId, Long reasonId) { + if (!workspaceQueryRepository.existsByIdAndManagerUser(workspaceId, actor.getManagerUser())) { + throw new CustomException(ErrorCode.FORBIDDEN); + } + + workspaceReasonQueryRepository.findByIdAndWorkspaceId(reasonId, workspaceId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + + return workspaceReasonCommentQueryRepository.getCommentsByReasonId(reasonId) + .stream() + .map(WorkspaceReasonCommentResponseDto::from) + .toList(); + } +} diff --git a/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppDeleteFileUseCase.java b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppDeleteFileUseCase.java new file mode 100644 index 0000000..dca046b --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppDeleteFileUseCase.java @@ -0,0 +1,7 @@ +package com.dreamteam.alter.domain.file.port.inbound; + +import com.dreamteam.alter.domain.user.context.AppActor; + +public interface AppDeleteFileUseCase { + void execute(AppActor actor, String fileId); +} diff --git a/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppGetPresignedUrlUseCase.java b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppGetPresignedUrlUseCase.java new file mode 100644 index 0000000..f049ce0 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppGetPresignedUrlUseCase.java @@ -0,0 +1,8 @@ +package com.dreamteam.alter.domain.file.port.inbound; + +import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; +import com.dreamteam.alter.domain.user.context.AppActor; + +public interface AppGetPresignedUrlUseCase { + FilePresignedUrlResponseDto execute(AppActor actor, String fileId); +} diff --git a/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppUploadFileUseCase.java b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppUploadFileUseCase.java new file mode 100644 index 0000000..520a16e --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppUploadFileUseCase.java @@ -0,0 +1,12 @@ +package com.dreamteam.alter.domain.file.port.inbound; + +import org.springframework.web.multipart.MultipartFile; + +import com.dreamteam.alter.adapter.inbound.general.file.dto.AppUploadFileResponseDto; +import com.dreamteam.alter.domain.file.type.BucketType; +import com.dreamteam.alter.domain.file.type.FileTargetType; +import com.dreamteam.alter.domain.user.context.AppActor; + +public interface AppUploadFileUseCase { + AppUploadFileResponseDto execute(AppActor actor, MultipartFile file, FileTargetType targetType, BucketType bucketType); +} diff --git a/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AttachFilesUseCase.java b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AttachFilesUseCase.java index b71fe9e..d947e06 100644 --- a/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AttachFilesUseCase.java +++ b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AttachFilesUseCase.java @@ -3,7 +3,9 @@ import com.dreamteam.alter.domain.file.type.FileTargetType; import java.util.List; +import java.util.Map; public interface AttachFilesUseCase { void execute(List fileIds, FileTargetType targetType, String targetId, Long userId); + void executeMap(Map fileIdsMap, String targetId, Long userId); } diff --git a/src/main/java/com/dreamteam/alter/domain/file/type/FileTargetType.java b/src/main/java/com/dreamteam/alter/domain/file/type/FileTargetType.java index e147847..5d18764 100644 --- a/src/main/java/com/dreamteam/alter/domain/file/type/FileTargetType.java +++ b/src/main/java/com/dreamteam/alter/domain/file/type/FileTargetType.java @@ -5,5 +5,8 @@ public enum FileTargetType { USER_CERTIFICATE, POSTING, WORKSPACE, + WORKSPACE_CERTIFICATE, // 사업자등록증명원 + WORKSPACE_OWN_IDENTITY, // 대표자 신분증 사본 + WORKSPACE_WARRANT, // 위임 확인서 CHAT_MESSAGE } diff --git a/src/main/java/com/dreamteam/alter/domain/user/port/outbound/ManagerUserRepository.java b/src/main/java/com/dreamteam/alter/domain/user/port/outbound/ManagerUserRepository.java new file mode 100644 index 0000000..c7c22a7 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/user/port/outbound/ManagerUserRepository.java @@ -0,0 +1,7 @@ +package com.dreamteam.alter.domain.user.port.outbound; + +import com.dreamteam.alter.domain.user.entity.ManagerUser; + +public interface ManagerUserRepository { + ManagerUser save(ManagerUser managerUser); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java new file mode 100644 index 0000000..d7cda85 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java @@ -0,0 +1,68 @@ +package com.dreamteam.alter.domain.workspace.entity; + +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import com.dreamteam.alter.domain.workspace.type.WorkspaceReasonStatus; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "workspace_reasons") +@Builder(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EntityListeners(AuditingEntityListener.class) +public class WorkspaceReason { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(name = "workspace_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY, optional = false) + private Workspace workspace; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + private WorkspaceReasonStatus status; + + @Column(name = "reason", nullable = false) + private String reason; + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at", nullable = false) + private LocalDateTime updatedAt; + + public static WorkspaceReason create(Workspace workspace, WorkspaceReasonStatus status, String reason) { + return WorkspaceReason.builder() + .workspace(workspace) + .status(status) + .reason(reason) + .build(); + } +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReasonComment.java b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReasonComment.java new file mode 100644 index 0000000..d1a3ea3 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReasonComment.java @@ -0,0 +1,59 @@ +package com.dreamteam.alter.domain.workspace.entity; + +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "workspace_reason_comments") +@Builder(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EntityListeners(AuditingEntityListener.class) +public class WorkspaceReasonComment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(name = "workspace_reason_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY, optional = false) + private WorkspaceReason workspaceReason; + + @Column(name = "comment", nullable = false) + private String comment; + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at", nullable = false) + private LocalDateTime updatedAt; + + public static WorkspaceReasonComment create(WorkspaceReason workspaceReason, String comment) { + return WorkspaceReasonComment.builder() + .workspaceReason(workspaceReason) + .comment(comment) + .build(); + } +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceReasonCommentUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceReasonCommentUseCase.java new file mode 100644 index 0000000..f3422ec --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceReasonCommentUseCase.java @@ -0,0 +1,8 @@ +package com.dreamteam.alter.domain.workspace.port.inbound; + +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.CreateWorkspaceReasonCommentRequestDto; +import com.dreamteam.alter.domain.user.context.ManagerActor; + +public interface CreateWorkspaceReasonCommentUseCase { + void execute(ManagerActor actor, Long workspaceId, Long reasonId, CreateWorkspaceReasonCommentRequestDto request); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java new file mode 100644 index 0000000..9b44bad --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java @@ -0,0 +1,8 @@ +package com.dreamteam.alter.domain.workspace.port.inbound; + +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; +import com.dreamteam.alter.domain.user.context.AppActor; + +public interface CreateWorkspaceUseCase { + void execute(AppActor actor, CreateWorkspaceRequestDto request); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceReasonCommentsUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceReasonCommentsUseCase.java new file mode 100644 index 0000000..7b26aed --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceReasonCommentsUseCase.java @@ -0,0 +1,10 @@ +package com.dreamteam.alter.domain.workspace.port.inbound; + +import java.util.List; + +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.WorkspaceReasonCommentResponseDto; +import com.dreamteam.alter.domain.user.context.ManagerActor; + +public interface GetWorkspaceReasonCommentsUseCase { + List execute(ManagerActor actor, Long workspaceId, Long reasonId); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentQueryRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentQueryRepository.java new file mode 100644 index 0000000..013f7fd --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentQueryRepository.java @@ -0,0 +1,9 @@ +package com.dreamteam.alter.domain.workspace.port.outbound; + +import java.util.List; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; + +public interface WorkspaceReasonCommentQueryRepository { + List getCommentsByReasonId(Long reasonId); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentRepository.java new file mode 100644 index 0000000..5cc2133 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentRepository.java @@ -0,0 +1,7 @@ +package com.dreamteam.alter.domain.workspace.port.outbound; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReasonComment; + +public interface WorkspaceReasonCommentRepository { + void save(WorkspaceReasonComment comment); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonQueryRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonQueryRepository.java new file mode 100644 index 0000000..b8ceb77 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonQueryRepository.java @@ -0,0 +1,9 @@ +package com.dreamteam.alter.domain.workspace.port.outbound; + +import java.util.Optional; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceReason; + +public interface WorkspaceReasonQueryRepository { + Optional findByIdAndWorkspaceId(Long reasonId, Long workspaceId); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRepository.java new file mode 100644 index 0000000..df67d27 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRepository.java @@ -0,0 +1,7 @@ +package com.dreamteam.alter.domain.workspace.port.outbound; + +import com.dreamteam.alter.domain.workspace.entity.Workspace; + +public interface WorkspaceRepository { + Long save(Workspace workspace); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceReasonStatus.java b/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceReasonStatus.java new file mode 100644 index 0000000..7b879aa --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceReasonStatus.java @@ -0,0 +1,6 @@ +package com.dreamteam.alter.domain.workspace.type; + +public enum WorkspaceReasonStatus { + APPROVE, + REJECT +}