From ea3a12db4e95abd0b944dc88c18efb84571652ab Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Thu, 19 Mar 2026 21:34:31 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20=EC=97=85=EC=9E=A5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=8B=A0=EC=B2=AD=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserWorkspaceController.java | 38 ++++++++++++ .../UserWorkspaceControllerSpec.java | 23 +++++++ .../dto/CreateWorkspaceRequestDto.java | 61 +++++++++++++++++++ .../persistence/ManagerUserJpaRepository.java | 7 +++ .../ManagerUserRepositoryImpl.java | 18 ++++++ .../persistence/WorkspaceRepositoryImpl.java | 20 ++++++ .../workspace/usecase/CreateWorkspace.java | 50 +++++++++++++++ .../port/outbound/ManagerUserRepository.java | 7 +++ .../port/inbound/CreateWorkspaceUseCase.java | 8 +++ .../port/outbound/WorkspaceRepository.java | 7 +++ 10 files changed, 239 insertions(+) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceController.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceControllerSpec.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/CreateWorkspaceRequestDto.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserJpaRepository.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/user/persistence/ManagerUserRepositoryImpl.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRepositoryImpl.java create mode 100644 src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java create mode 100644 src/main/java/com/dreamteam/alter/domain/user/port/outbound/ManagerUserRepository.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRepository.java 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 00000000..08c6f855 --- /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 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; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +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; + +@RestController +@RequestMapping("/workspaces") +@RequiredArgsConstructor +@Validated +public class UserWorkspaceController implements UserWorkspaceControllerSpec { + + @Resource(name = "createWorkspace") + private final CreateWorkspaceUseCase createWorkspace; + + @Override + @PostMapping + @PreAuthorize("hasAnyRole('USER')") + 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 00000000..0723a682 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceControllerSpec.java @@ -0,0 +1,23 @@ +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 = "GENERAL - 업장 API") +public interface UserWorkspaceControllerSpec { + + @Operation(summary = "업장 등록 신청") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "업장 등록 신청 성공") + }) + 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 00000000..bb1ef0ce --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/CreateWorkspaceRequestDto.java @@ -0,0 +1,61 @@ +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 = "홍길동") + private String ownName; + + @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; +} 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 00000000..d63ee82b --- /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 00000000..292a39e1 --- /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/WorkspaceRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRepositoryImpl.java new file mode 100644 index 00000000..029e1cb2 --- /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 void save(Workspace workspace) { + workspaceJpaRepository.save(workspace); + } +} 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 00000000..6e390ba5 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java @@ -0,0 +1,50 @@ +package com.dreamteam.alter.application.workspace.usecase; + +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.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; + + @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() + ); + + workspaceRepository.save(workspace); + } +} 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 00000000..c7c22a75 --- /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/port/inbound/CreateWorkspaceUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java new file mode 100644 index 00000000..9b44bada --- /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/outbound/WorkspaceRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRepository.java new file mode 100644 index 00000000..7efdf0d3 --- /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 { + void save(Workspace workspace); +} From 31e7359447f361c06e4dab204d29a575d85edbf0 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 11:34:57 +0900 Subject: [PATCH 02/20] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EA=B4=80=EB=A0=A8=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../file/controller/AppFileController.java | 71 +++++++++++++++ .../controller/AppFileControllerSpec.java | 89 +++++++++++++++++++ .../file/dto/AppUploadFileResponseDto.java | 21 +++++ .../file/usecase/AppDeleteFile.java | 35 ++++++++ .../file/usecase/AppGetPresignedUrl.java | 36 ++++++++ .../file/usecase/AppUploadFile.java | 30 +++++++ .../port/inbound/AppDeleteFileUseCase.java | 7 ++ .../inbound/AppGetPresignedUrlUseCase.java | 8 ++ .../port/inbound/AppUploadFileUseCase.java | 12 +++ 9 files changed, 309 insertions(+) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileController.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/file/controller/AppFileControllerSpec.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/file/dto/AppUploadFileResponseDto.java create mode 100644 src/main/java/com/dreamteam/alter/application/file/usecase/AppDeleteFile.java create mode 100644 src/main/java/com/dreamteam/alter/application/file/usecase/AppGetPresignedUrl.java create mode 100644 src/main/java/com/dreamteam/alter/application/file/usecase/AppUploadFile.java create mode 100644 src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppDeleteFileUseCase.java create mode 100644 src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppGetPresignedUrlUseCase.java create mode 100644 src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppUploadFileUseCase.java 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 00000000..76c705ab --- /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 00000000..948e6be3 --- /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 00000000..a15ff4e6 --- /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/application/file/usecase/AppDeleteFile.java b/src/main/java/com/dreamteam/alter/application/file/usecase/AppDeleteFile.java new file mode 100644 index 00000000..8661b7d2 --- /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 00000000..d281d3fa --- /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 00000000..75439471 --- /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/domain/file/port/inbound/AppDeleteFileUseCase.java b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AppDeleteFileUseCase.java new file mode 100644 index 00000000..dca046bb --- /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 00000000..f049ce0c --- /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 00000000..520a16ed --- /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); +} From 2a82ddf3220a45a8a60c39607c352a67cdbc7293 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 12:49:59 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat:=20=EC=97=85=EC=9E=A5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserWorkspaceController.java | 18 ++++----- .../UserWorkspaceControllerSpec.java | 8 +++- .../dto/CreateWorkspaceRequestDto.java | 11 ++++++ .../persistence/WorkspaceRepositoryImpl.java | 4 +- .../application/file/usecase/AttachFiles.java | 38 ++++++++++++++----- .../workspace/usecase/CreateWorkspace.java | 17 ++++++++- .../file/port/inbound/AttachFilesUseCase.java | 2 + .../domain/file/type/FileTargetType.java | 3 ++ .../port/outbound/WorkspaceRepository.java | 2 +- 9 files changed, 79 insertions(+), 24 deletions(-) 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 index 08c6f855..33f22d91 100644 --- 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 @@ -1,23 +1,24 @@ 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; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -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; @RestController -@RequestMapping("/workspaces") +@RequestMapping("/app/workspaces") @RequiredArgsConstructor @Validated public class UserWorkspaceController implements UserWorkspaceControllerSpec { @@ -27,7 +28,6 @@ public class UserWorkspaceController implements UserWorkspaceControllerSpec { @Override @PostMapping - @PreAuthorize("hasAnyRole('USER')") public ResponseEntity> createWorkspace( @RequestBody @Valid CreateWorkspaceRequestDto request ) { 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 index 0723a682..a5e63fd9 100644 --- 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 @@ -10,12 +10,16 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; -@Tag(name = "GENERAL - 업장 API") +@Tag(name = "APP - 업장 API") public interface UserWorkspaceControllerSpec { @Operation(summary = "업장 등록 신청") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "업장 등록 신청 성공") + @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 index bb1ef0ce..a0316a34 100644 --- 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 @@ -58,4 +58,15 @@ public class CreateWorkspaceRequestDto { @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/outbound/workspace/persistence/WorkspaceRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRepositoryImpl.java index 029e1cb2..259c50d8 100644 --- 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 @@ -14,7 +14,7 @@ public class WorkspaceRepositoryImpl implements WorkspaceRepository { private final WorkspaceJpaRepository workspaceJpaRepository; @Override - public void save(Workspace workspace) { - workspaceJpaRepository.save(workspace); + public Long save(Workspace workspace) { + return workspaceJpaRepository.save(workspace).getId(); } } 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 999cdbe9..bb50bcbb 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 index 6e390ba5..a5b778b4 100644 --- a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java @@ -1,9 +1,14 @@ 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; @@ -22,6 +27,7 @@ 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) { @@ -45,6 +51,15 @@ public void execute(AppActor actor, CreateWorkspaceRequestDto request) { request.getLongitude() ); - workspaceRepository.save(workspace); + 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/domain/file/port/inbound/AttachFilesUseCase.java b/src/main/java/com/dreamteam/alter/domain/file/port/inbound/AttachFilesUseCase.java index b71fe9e9..d947e06e 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 e1478477..5d187647 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/workspace/port/outbound/WorkspaceRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRepository.java index 7efdf0d3..df67d277 100644 --- 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 @@ -3,5 +3,5 @@ import com.dreamteam.alter.domain.workspace.entity.Workspace; public interface WorkspaceRepository { - void save(Workspace workspace); + Long save(Workspace workspace); } From 6f123a406ab756d70517d73606a109afc8f098b9 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 13:08:55 +0900 Subject: [PATCH 04/20] =?UTF-8?q?refactor:=20=EC=97=85=EC=9E=A5=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=8B=A0=EC=B2=AD=20=EB=8C=80=ED=91=9C?= =?UTF-8?q?=EC=9E=90=20=EC=84=B1=EB=AA=85=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../general/workspace/dto/CreateWorkspaceRequestDto.java | 4 ---- 1 file changed, 4 deletions(-) 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 index a0316a34..ba6b9a6e 100644 --- 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 @@ -19,10 +19,6 @@ public class CreateWorkspaceRequestDto { @Schema(description = "업장 이름", example = "세븐일레븐") private String bizName; - @NotBlank - @Schema(description = "대표자 이름", example = "홍길동") - private String ownName; - @NotBlank @Schema(description = "사업자 등록번호", example = "123-45-12345") private String brn; From ac0aa73d27abcbdc5a9a540a53aedfd7548d00a3 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 14:30:27 +0900 Subject: [PATCH 05/20] =?UTF-8?q?feat:=20=EC=8A=B9=EC=9D=B8/=EB=B0=98?= =?UTF-8?q?=EB=A0=A4=20=EC=82=AC=EC=9C=A0=20=EB=8C=93=EA=B8=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nagerWorkspaceReasonCommentController.java | 43 ++++++++++++ ...rWorkspaceReasonCommentControllerSpec.java | 28 ++++++++ ...reateWorkspaceReasonCommentRequestDto.java | 18 +++++ .../WorkspaceReasonCommentJpaRepository.java | 10 +++ .../WorkspaceReasonCommentRepositoryImpl.java | 20 ++++++ .../WorkspaceReasonJpaRepository.java | 10 +++ .../WorkspaceReasonQueryRepositoryImpl.java | 33 ++++++++++ .../usecase/CreateWorkspaceReasonComment.java | 40 ++++++++++++ .../workspace/entity/WorkspaceReason.java | 65 +++++++++++++++++++ .../entity/WorkspaceReasonComment.java | 59 +++++++++++++++++ .../CreateWorkspaceReasonCommentUseCase.java | 8 +++ .../WorkspaceReasonCommentRepository.java | 7 ++ .../WorkspaceReasonQueryRepository.java | 9 +++ .../workspace/type/WorkspaceReasonStatus.java | 6 ++ 14 files changed, 356 insertions(+) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/CreateWorkspaceReasonCommentRequestDto.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentJpaRepository.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentRepositoryImpl.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonJpaRepository.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonQueryRepositoryImpl.java create mode 100644 src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReasonComment.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceReasonCommentUseCase.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentRepository.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonQueryRepository.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceReasonStatus.java 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 00000000..4b4316b5 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java @@ -0,0 +1,43 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +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.application.aop.ManagerActionContext; +import com.dreamteam.alter.domain.user.context.ManagerActor; +import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceReasonCommentUseCase; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/manager/workspaces/{workspaceId}/reasons/{reasonId}/comment") +@PreAuthorize("hasAnyRole('MANAGER')") +@RequiredArgsConstructor +@Validated +public class ManagerWorkspaceReasonCommentController implements ManagerWorkspaceReasonCommentControllerSpec { + + @Resource(name = "createWorkspaceReasonComment") + private final CreateWorkspaceReasonCommentUseCase createWorkspaceReasonComment; + + @Override + @PostMapping + public ResponseEntity> createWorkspaceReason( + @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()); + } +} 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 00000000..7aff82b5 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java @@ -0,0 +1,28 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.controller; + +import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; +import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.CreateWorkspaceReasonCommentRequestDto; +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> createWorkspaceReason( + @PathVariable Long workspaceId, + @PathVariable Long reasonId, + @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request + ); +} 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 00000000..81002b60 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/CreateWorkspaceReasonCommentRequestDto.java @@ -0,0 +1,18 @@ +package com.dreamteam.alter.adapter.inbound.manager.workspace.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +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 = "왜 반려죠") + @NotBlank + private String comment; +} 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 00000000..05125b90 --- /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/WorkspaceReasonCommentRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentRepositoryImpl.java new file mode 100644 index 00000000..2e8b63e3 --- /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 00000000..52bf4916 --- /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 00000000..2c6d9b59 --- /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/application/workspace/usecase/CreateWorkspaceReasonComment.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java new file mode 100644 index 00000000..1aa8f6fa --- /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/domain/workspace/entity/WorkspaceReason.java b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java new file mode 100644 index 00000000..b8d39608 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java @@ -0,0 +1,65 @@ +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.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; + + @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 00000000..d1a3ea3f --- /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 00000000..f3422eca --- /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/outbound/WorkspaceReasonCommentRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentRepository.java new file mode 100644 index 00000000..5cc21336 --- /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 00000000..b8ceb774 --- /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/type/WorkspaceReasonStatus.java b/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceReasonStatus.java new file mode 100644 index 00000000..7b879aa2 --- /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 +} From 3e3ca2c4c25f7501d7240818879913054f3f4841 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 15:13:08 +0900 Subject: [PATCH 06/20] =?UTF-8?q?fix:=20WorkspaceReason=20statue=20Enumera?= =?UTF-8?q?ted=20String=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alter/domain/workspace/entity/WorkspaceReason.java | 3 +++ 1 file changed, 3 insertions(+) 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 index b8d39608..d7cda85e 100644 --- a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java @@ -11,6 +11,8 @@ 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; @@ -41,6 +43,7 @@ public class WorkspaceReason { @ManyToOne(fetch = FetchType.LAZY, optional = false) private Workspace workspace; + @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) private WorkspaceReasonStatus status; From 7dafd0e2a63e4f875a67486d82cf76f61c2341d2 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 15:14:28 +0900 Subject: [PATCH 07/20] =?UTF-8?q?refactor:=20=EC=BD=94=EB=A9=98=ED=8A=B8?= =?UTF-8?q?=20API=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ManagerWorkspaceReasonCommentController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index 4b4316b5..550a6bac 100644 --- 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 @@ -3,6 +3,7 @@ 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; @@ -20,7 +21,7 @@ import lombok.RequiredArgsConstructor; @RestController -@RequestMapping("/manager/workspaces/{workspaceId}/reasons/{reasonId}/comment") +@RequestMapping("/manager/workspaces/{workspaceId}/reasons/{reasonId}/comments") @PreAuthorize("hasAnyRole('MANAGER')") @RequiredArgsConstructor @Validated @@ -40,4 +41,5 @@ public ResponseEntity> createWorkspaceReason( createWorkspaceReasonComment.execute(actor, workspaceId, reasonId, request); return ResponseEntity.ok(CommonApiResponse.empty()); } + } From 8f7270f00bf6b9e60733166e34f2d35b383d5e8d Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 15:50:12 +0900 Subject: [PATCH 08/20] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nagerWorkspaceReasonCommentController.java | 17 ++++++++ ...rWorkspaceReasonCommentControllerSpec.java | 14 +++++++ .../WorkspaceReasonCommentResponseDto.java | 31 ++++++++++++++ ...spaceReasonCommentQueryRepositoryImpl.java | 30 +++++++++++++ .../usecase/GetWorkspaceReasonComments.java | 42 +++++++++++++++++++ .../GetWorkspaceReasonCommentsUseCase.java | 10 +++++ ...WorkspaceReasonCommentQueryRepository.java | 9 ++++ 7 files changed, 153 insertions(+) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentQueryRepositoryImpl.java create mode 100644 src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceReasonCommentsUseCase.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceReasonCommentQueryRepository.java 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 index 550a6bac..c5e3d302 100644 --- 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 @@ -1,5 +1,7 @@ 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; @@ -12,9 +14,11 @@ 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; @@ -30,6 +34,9 @@ public class ManagerWorkspaceReasonCommentController implements ManagerWorkspace @Resource(name = "createWorkspaceReasonComment") private final CreateWorkspaceReasonCommentUseCase createWorkspaceReasonComment; + @Resource(name = "getWorkspaceReasonComments") + private final GetWorkspaceReasonCommentsUseCase getWorkspaceReasonComments; + @Override @PostMapping public ResponseEntity> createWorkspaceReason( @@ -42,4 +49,14 @@ public ResponseEntity> createWorkspaceReason( 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 index 7aff82b5..37504121 100644 --- 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 @@ -1,7 +1,10 @@ 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; @@ -25,4 +28,15 @@ ResponseEntity> createWorkspaceReason( @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/WorkspaceReasonCommentResponseDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java new file mode 100644 index 00000000..db09bae3 --- /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/workspace/persistence/WorkspaceReasonCommentQueryRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceReasonCommentQueryRepositoryImpl.java new file mode 100644 index 00000000..c416371c --- /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/application/workspace/usecase/GetWorkspaceReasonComments.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java new file mode 100644 index 00000000..2c6ca8a8 --- /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/workspace/port/inbound/GetWorkspaceReasonCommentsUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceReasonCommentsUseCase.java new file mode 100644 index 00000000..7b26aed3 --- /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 00000000..013f7fd4 --- /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); +} From 0467a467d4a91ac5537c27fbbaaa16e98f35f9ec Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 16:04:39 +0900 Subject: [PATCH 09/20] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ManagerWorkspaceReasonCommentController.java | 2 +- .../controller/ManagerWorkspaceReasonCommentControllerSpec.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 index c5e3d302..17114c4f 100644 --- 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 @@ -39,7 +39,7 @@ public class ManagerWorkspaceReasonCommentController implements ManagerWorkspace @Override @PostMapping - public ResponseEntity> createWorkspaceReason( + public ResponseEntity> createWorkspaceReasonComment( @PathVariable Long workspaceId, @PathVariable Long reasonId, @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request 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 index 37504121..487c8387 100644 --- 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 @@ -23,7 +23,7 @@ public interface ManagerWorkspaceReasonCommentControllerSpec { @ApiResponse(responseCode = "403", description = "해당 업장의 관리자가 아님 (A002)"), @ApiResponse(responseCode = "404", description = "존재하지 않는 사유 (B019)") }) - ResponseEntity> createWorkspaceReason( + ResponseEntity> createWorkspaceReasonComment( @PathVariable Long workspaceId, @PathVariable Long reasonId, @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request From c79501d010725fa8ef35df491f7633f7e16053b0 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Sat, 21 Mar 2026 16:08:04 +0900 Subject: [PATCH 10/20] =?UTF-8?q?refactor:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EA=B8=B8=EC=9D=B4=20=EC=A0=9C=ED=95=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/dto/CreateWorkspaceReasonCommentRequestDto.java | 2 ++ 1 file changed, 2 insertions(+) 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 index 81002b60..d8b64ab3 100644 --- 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 @@ -1,6 +1,7 @@ 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; @@ -13,6 +14,7 @@ public class CreateWorkspaceReasonCommentRequestDto { @Schema(description = "댓글 내용", example = "왜 반려죠") + @Max(255) @NotBlank private String comment; } From 7338e12d075b19ac5d8d219a8a3411c23ab8076d Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 21:00:44 +0900 Subject: [PATCH 11/20] =?UTF-8?q?fix:=20validation=20max=20->=20size=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/dto/CreateWorkspaceReasonCommentRequestDto.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index d8b64ab3..71417893 100644 --- 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 @@ -1,8 +1,8 @@ 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 jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -14,7 +14,7 @@ public class CreateWorkspaceReasonCommentRequestDto { @Schema(description = "댓글 내용", example = "왜 반려죠") - @Max(255) + @Size(max = 255) @NotBlank private String comment; } From fe16fabc26a4327cd0316d97f2dc2c1bc934c8a0 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 21:03:23 +0900 Subject: [PATCH 12/20] =?UTF-8?q?refactor:=20api=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/dto/WorkspaceReasonCommentResponseDto.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 index db09bae3..979b157a 100644 --- 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 @@ -17,8 +17,14 @@ @Builder(access = AccessLevel.PRIVATE) @Schema(description = "업장 등록 요청 승인/반려 댓글 조회 DTO") public class WorkspaceReasonCommentResponseDto { + + @Schema(description = "댓글 ID", example = "1") private Long id; + + @Schema(description = "댓글 내용", example = "자료 누락") private String comment; + + @Schema(description = "댓글 생성시각", example = "2026-03-01T10:00:00") private LocalDateTime createdAt; public static WorkspaceReasonCommentResponseDto from(WorkspaceReasonComment entity) { From cb625c80c179d53b0a65c3768db4da54130da4a4 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 21:30:09 +0900 Subject: [PATCH 13/20] =?UTF-8?q?feat:=20=EC=97=85=EC=9E=A5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=9A=94=EC=B2=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WorkspaceRequest 추가 - workspace 생성 -> workspaceRequest 생성 변경 --- .../controller/AdminWorkspaceController.java | 12 ++ .../controller/UserWorkspaceController.java | 4 +- .../WorkspaceRequestJpaRepository.java | 8 ++ .../WorkspaceRequestRepositoryImpl.java | 20 +++ ...space.java => CreateWorkspaceRequest.java} | 30 ++--- .../workspace/entity/WorkspaceRequest.java | 118 ++++++++++++++++++ ...ava => CreateWorkspaceRequestUseCase.java} | 2 +- .../outbound/WorkspaceRequestRepository.java | 7 ++ .../type/WorkspaceRequestStatus.java | 19 +++ 9 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/admin/workspace/controller/AdminWorkspaceController.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestJpaRepository.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestRepositoryImpl.java rename src/main/java/com/dreamteam/alter/application/workspace/usecase/{CreateWorkspace.java => CreateWorkspaceRequest.java} (61%) create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java rename src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/{CreateWorkspaceUseCase.java => CreateWorkspaceRequestUseCase.java} (85%) create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestRepository.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceRequestStatus.java diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/admin/workspace/controller/AdminWorkspaceController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/admin/workspace/controller/AdminWorkspaceController.java new file mode 100644 index 00000000..4a71fb80 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/admin/workspace/controller/AdminWorkspaceController.java @@ -0,0 +1,12 @@ +package com.dreamteam.alter.adapter.inbound.admin.workspace.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/admin/workspaces") +@RequiredArgsConstructor +public class AdminWorkspaceController { +} 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 index 33f22d91..6e2154e9 100644 --- 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 @@ -11,7 +11,7 @@ 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 com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceRequestUseCase; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -24,7 +24,7 @@ public class UserWorkspaceController implements UserWorkspaceControllerSpec { @Resource(name = "createWorkspace") - private final CreateWorkspaceUseCase createWorkspace; + private final CreateWorkspaceRequestUseCase createWorkspace; @Override @PostMapping diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestJpaRepository.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestJpaRepository.java new file mode 100644 index 00000000..392916ec --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestJpaRepository.java @@ -0,0 +1,8 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceRequest; + +public interface WorkspaceRequestJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestRepositoryImpl.java new file mode 100644 index 00000000..c1ede248 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestRepositoryImpl.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.WorkspaceRequest; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestRepository; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class WorkspaceRequestRepositoryImpl implements WorkspaceRequestRepository { + + private final WorkspaceRequestJpaRepository workspaceRequestJpaRepository; + + @Override + public Long save(WorkspaceRequest workspaceRequest) { + return workspaceRequestJpaRepository.save(workspaceRequest).getId(); + } +} diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java similarity index 61% rename from src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java rename to src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java index a5b778b4..e96116e1 100644 --- a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspace.java +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java @@ -10,39 +10,28 @@ 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 com.dreamteam.alter.domain.workspace.entity.WorkspaceRequest; +import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceRequestUseCase; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestRepository; import lombok.RequiredArgsConstructor; @Service("createWorkspace") @RequiredArgsConstructor @Transactional -public class CreateWorkspace implements CreateWorkspaceUseCase { +public class CreateWorkspaceRequest implements CreateWorkspaceRequestUseCase { - private final ManagerUserRepository managerUserRepository; - private final WorkspaceRepository workspaceRepository; + private final WorkspaceRequestRepository workspaceRequestRepository; 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, + WorkspaceRequest workspaceRequest = WorkspaceRequest.create( + actor.getUser(), request.getBrn(), request.getBizName(), request.getType(), request.getContact(), - null, - WorkspaceStatus.PENDING, request.getAddress(), request.getProvince(), request.getDistrict(), @@ -51,8 +40,7 @@ public void execute(AppActor actor, CreateWorkspaceRequestDto request) { request.getLongitude() ); - String savedWorkspaceId = workspaceRepository.save(workspace).toString(); - Long userId = actor.getUserId(); + Long savedWorkspaceRequestId = workspaceRequestRepository.save(workspaceRequest); Map fileMap = new HashMap<>(); fileMap.put(request.getWorkspaceCertFileId(), FileTargetType.WORKSPACE_CERTIFICATE); @@ -60,6 +48,6 @@ public void execute(AppActor actor, CreateWorkspaceRequestDto request) { if (request.getWorkspaceWarrantFileId() != null) { fileMap.put(request.getWorkspaceWarrantFileId(), FileTargetType.WORKSPACE_WARRANT); } - attachFiles.executeMap(fileMap, savedWorkspaceId, userId); + attachFiles.executeMap(fileMap, savedWorkspaceRequestId.toString(), actor.getUserId()); } } diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java new file mode 100644 index 00000000..5bad9065 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java @@ -0,0 +1,118 @@ +package com.dreamteam.alter.domain.workspace.entity; + +import java.math.BigDecimal; +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.user.entity.User; +import com.dreamteam.alter.domain.workspace.type.WorkspaceRequestStatus; + +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 = "workspaces") +@Builder(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EntityListeners(AuditingEntityListener.class) +public class WorkspaceRequest { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(name = "user_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + private User user; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 20, nullable = false) + private WorkspaceRequestStatus status; + + @Column(name = "business_registration_no", length = 50, nullable = false) + private String businessRegistrationNo; + + @Column(name = "business_name", length = 128, nullable = false) + private String businessName; + + @Column(name = "business_type", length = 128, nullable = false) + private String businessType; + + @Column(name = "contact", length = 13, nullable = false) + private String contact; + + @Column(name = "full_address", length = Integer.MAX_VALUE, nullable = false) + private String fullAddress; + + @Column(name = "province", length = 64, nullable = false) + private String province; + + @Column(name = "district", length = 64, nullable = false) + private String district; + + @Column(name = "town", length = 64, nullable = false) + private String town; + + @Column(name = "latitude", precision = 9, scale = 6, nullable = false) + private BigDecimal latitude; + + @Column(name = "longitude", precision = 9, scale = 6, nullable = false) + private BigDecimal longitude; + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at", nullable = false) + private LocalDateTime updatedAt; + + public static WorkspaceRequest create( + User user, + String brn, + String bizName, + String type, + String contact, + String address, + String province, + String district, + String town, + BigDecimal latitude, + BigDecimal longitude + ) { + return WorkspaceRequest.builder() + .user(user) + .status(WorkspaceRequestStatus.PENDING) + .businessRegistrationNo(brn) + .businessName(bizName) + .businessType(type) + .contact(contact) + .fullAddress(address) + .province(province) + .district(district) + .town(town) + .latitude(latitude) + .longitude(longitude) + .build(); + } +} 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/CreateWorkspaceRequestUseCase.java similarity index 85% rename from src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java rename to src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceRequestUseCase.java index 9b44bada..d710f970 100644 --- a/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceUseCase.java +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/CreateWorkspaceRequestUseCase.java @@ -3,6 +3,6 @@ import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; import com.dreamteam.alter.domain.user.context.AppActor; -public interface CreateWorkspaceUseCase { +public interface CreateWorkspaceRequestUseCase { void execute(AppActor actor, CreateWorkspaceRequestDto request); } diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestRepository.java new file mode 100644 index 00000000..70ca7c89 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestRepository.java @@ -0,0 +1,7 @@ +package com.dreamteam.alter.domain.workspace.port.outbound; + +import com.dreamteam.alter.domain.workspace.entity.WorkspaceRequest; + +public interface WorkspaceRequestRepository { + Long save(WorkspaceRequest workspaceRequest); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceRequestStatus.java b/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceRequestStatus.java new file mode 100644 index 00000000..731b20ba --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/type/WorkspaceRequestStatus.java @@ -0,0 +1,19 @@ +package com.dreamteam.alter.domain.workspace.type; + +import java.util.Map; + +public enum WorkspaceRequestStatus { + PENDING, + ACTIVATED, + REVOKED, + ; + + + public static Map describe() { + return Map.of( + WorkspaceRequestStatus.PENDING, "승인 대기", + WorkspaceRequestStatus.REVOKED, "반려", + WorkspaceRequestStatus.ACTIVATED, "활성화" + ); + } +} From f7cc3251f9ed5f41d5ff43a50a320ff948d1bb51 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 21:44:09 +0900 Subject: [PATCH 14/20] =?UTF-8?q?feat:=20=EC=97=85=EC=9E=A5=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=82=AC=EC=9C=A0=20=EB=8C=93=EA=B8=80=20API=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WorkspaceReason fk workspace -> workspaceRequest 변경 --- ...UserWorkspaceReasonCommentController.java} | 24 ++++++------- ...WorkspaceReasonCommentControllerSpec.java} | 10 +++--- .../WorkspaceReasonQueryRepositoryImpl.java | 4 +-- .../WorkspaceRequestQueryRepositoryImpl.java | 34 +++++++++++++++++++ .../usecase/CreateWorkspaceReasonComment.java | 15 ++++---- .../usecase/GetWorkspaceReasonComments.java | 12 +++---- .../workspace/entity/WorkspaceReason.java | 8 ++--- .../CreateWorkspaceReasonCommentUseCase.java | 4 +-- .../GetWorkspaceReasonCommentsUseCase.java | 3 +- .../WorkspaceReasonQueryRepository.java | 2 +- .../WorkspaceRequestQueryRepository.java | 5 +++ 11 files changed, 79 insertions(+), 42 deletions(-) rename src/main/java/com/dreamteam/alter/adapter/inbound/{manager/workspace/controller/ManagerWorkspaceReasonCommentController.java => general/workspace/controller/UserWorkspaceReasonCommentController.java} (69%) rename src/main/java/com/dreamteam/alter/adapter/inbound/{manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java => general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java} (83%) create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java 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/general/workspace/controller/UserWorkspaceReasonCommentController.java similarity index 69% rename from src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java rename to src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentController.java index 17114c4f..f1ed5735 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentController.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentController.java @@ -1,9 +1,8 @@ -package com.dreamteam.alter.adapter.inbound.manager.workspace.controller; +package com.dreamteam.alter.adapter.inbound.general.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; @@ -15,8 +14,8 @@ 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.application.aop.AppActionContext; +import com.dreamteam.alter.domain.user.context.AppActor; import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceReasonCommentUseCase; import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceReasonCommentsUseCase; @@ -25,11 +24,10 @@ import lombok.RequiredArgsConstructor; @RestController -@RequestMapping("/manager/workspaces/{workspaceId}/reasons/{reasonId}/comments") -@PreAuthorize("hasAnyRole('MANAGER')") +@RequestMapping("/app/workspace-requests/{workspaceRequestId}/reasons/{reasonId}/comments") @RequiredArgsConstructor @Validated -public class ManagerWorkspaceReasonCommentController implements ManagerWorkspaceReasonCommentControllerSpec { +public class UserWorkspaceReasonCommentController implements UserWorkspaceReasonCommentControllerSpec { @Resource(name = "createWorkspaceReasonComment") private final CreateWorkspaceReasonCommentUseCase createWorkspaceReasonComment; @@ -40,23 +38,23 @@ public class ManagerWorkspaceReasonCommentController implements ManagerWorkspace @Override @PostMapping public ResponseEntity> createWorkspaceReasonComment( - @PathVariable Long workspaceId, + @PathVariable Long workspaceRequestId, @PathVariable Long reasonId, @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request ) { - ManagerActor actor = ManagerActionContext.getInstance().getActor(); - createWorkspaceReasonComment.execute(actor, workspaceId, reasonId, request); + AppActor actor = AppActionContext.getInstance().getActor(); + createWorkspaceReasonComment.execute(actor, workspaceRequestId, reasonId, request); return ResponseEntity.ok(CommonApiResponse.empty()); } @Override @GetMapping public ResponseEntity>> getWorkspaceReasonComments( - @PathVariable Long workspaceId, + @PathVariable Long workspaceRequestId, @PathVariable Long reasonId ) { - ManagerActor actor = ManagerActionContext.getInstance().getActor(); - return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceReasonComments.execute(actor, workspaceId, reasonId))); + AppActor actor = AppActionContext.getInstance().getActor(); + return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceReasonComments.execute(actor, workspaceRequestId, 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/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java similarity index 83% rename from src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java rename to src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java index 487c8387..5ef0508d 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/controller/ManagerWorkspaceReasonCommentControllerSpec.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java @@ -1,4 +1,4 @@ -package com.dreamteam.alter.adapter.inbound.manager.workspace.controller; +package com.dreamteam.alter.adapter.inbound.general.workspace.controller; import java.util.List; @@ -14,10 +14,10 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; -@Tag(name = "MANAGER - 업장 승인/반려 사유 코멘트 API") -public interface ManagerWorkspaceReasonCommentControllerSpec { +@Tag(name = "APP - 업장 승인/반려 사유 코멘트 API") +public interface UserWorkspaceReasonCommentControllerSpec { - @Operation(summary = "매니저 - 승인/반려 사유 코멘트 등록") + @Operation(summary = "유저 - 승인/반려 사유 코멘트 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "코멘트 등록 성공"), @ApiResponse(responseCode = "403", description = "해당 업장의 관리자가 아님 (A002)"), @@ -29,7 +29,7 @@ ResponseEntity> createWorkspaceReasonComment( @Valid @RequestBody CreateWorkspaceReasonCommentRequestDto request ); - @Operation(summary = "매니저 - 승인/반려 사유 코멘트 목록 조회") + @Operation(summary = "유저 - 승인/반려 사유 코멘트 목록 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "코멘트 목록 조회 성공"), @ApiResponse(responseCode = "403", description = "해당 업장의 관리자가 아님 (A002)"), 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 index 2c6d9b59..504fbeca 100644 --- 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 @@ -18,14 +18,14 @@ public class WorkspaceReasonQueryRepositoryImpl implements WorkspaceReasonQueryR private final JPAQueryFactory queryFactory; @Override - public Optional findByIdAndWorkspaceId(Long reasonId, Long workspaceId) { + public Optional findByIdAndWorkspaceRequestId(Long reasonId, Long workspaceRequestId) { QWorkspaceReason qWorkspaceReason = QWorkspaceReason.workspaceReason; return Optional.ofNullable( queryFactory .selectFrom(qWorkspaceReason) .where( qWorkspaceReason.id.eq(reasonId), - qWorkspaceReason.workspace.id.eq(workspaceId) + qWorkspaceReason.workspaceRequest.id.eq(workspaceRequestId) ) .fetchOne() ); diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java new file mode 100644 index 00000000..3f9b4dd7 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java @@ -0,0 +1,34 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence; + +import org.springframework.stereotype.Repository; + +import com.dreamteam.alter.domain.workspace.entity.QWorkspace; +import com.dreamteam.alter.domain.workspace.entity.QWorkspaceRequest; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; +import com.dreamteam.alter.domain.workspace.type.WorkspaceStatus; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class WorkspaceRequestQueryRepositoryImpl implements WorkspaceRequestQueryRepository { + + private final JPAQueryFactory queryFactory; + + @Override + public boolean existsByIdAndUserId(Long workspaceRequestId, Long userId) { + QWorkspaceRequest qWorkspaceRequest = QWorkspaceRequest.workspaceRequest; + + Integer result = queryFactory + .selectOne() + .from(qWorkspaceRequest) + .where( + qWorkspaceRequest.id.eq(workspaceRequestId), + qWorkspaceRequest.user.id.eq(userId) + ) + .fetchFirst(); + + return result != null; + } +} 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 index 1aa8f6fa..8739e495 100644 --- a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceReasonComment.java @@ -6,13 +6,13 @@ 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.user.context.AppActor; 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 com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; import lombok.RequiredArgsConstructor; @@ -21,20 +21,19 @@ @Transactional public class CreateWorkspaceReasonComment implements CreateWorkspaceReasonCommentUseCase { - private final WorkspaceQueryRepository workspaceQueryRepository; + private final WorkspaceRequestQueryRepository workspaceRequestQueryRepository; 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())) { + public void execute(AppActor actor, Long workspaceRequestId, Long reasonId, CreateWorkspaceReasonCommentRequestDto request) { + if (!workspaceRequestQueryRepository.existsByIdAndUserId(workspaceRequestId, actor.getUserId())) { throw new CustomException(ErrorCode.FORBIDDEN); } - WorkspaceReason reason = workspaceReasonQueryRepository.findByIdAndWorkspaceId(reasonId, workspaceId) + WorkspaceReason reason = workspaceReasonQueryRepository.findByIdAndWorkspaceRequestId(reasonId, workspaceRequestId) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); - WorkspaceReasonComment comment = WorkspaceReasonComment.create(reason, request.getComment()); - workspaceReasonCommentRepository.save(comment); + workspaceReasonCommentRepository.save(WorkspaceReasonComment.create(reason, request.getComment())); } } 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 index 2c6ca8a8..b4c86a46 100644 --- a/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java @@ -8,11 +8,11 @@ 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.user.context.AppActor; 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 com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; import lombok.RequiredArgsConstructor; @@ -21,17 +21,17 @@ @Transactional(readOnly = true) public class GetWorkspaceReasonComments implements GetWorkspaceReasonCommentsUseCase { - private final WorkspaceQueryRepository workspaceQueryRepository; + private final WorkspaceRequestQueryRepository workspaceRequestQueryRepository; private final WorkspaceReasonQueryRepository workspaceReasonQueryRepository; private final WorkspaceReasonCommentQueryRepository workspaceReasonCommentQueryRepository; @Override - public List execute(ManagerActor actor, Long workspaceId, Long reasonId) { - if (!workspaceQueryRepository.existsByIdAndManagerUser(workspaceId, actor.getManagerUser())) { + public List execute(AppActor actor, Long workspaceRequestId, Long reasonId) { + if (!workspaceRequestQueryRepository.existsByIdAndUserId(workspaceRequestId, actor.getUserId())) { throw new CustomException(ErrorCode.FORBIDDEN); } - workspaceReasonQueryRepository.findByIdAndWorkspaceId(reasonId, workspaceId) + workspaceReasonQueryRepository.findByIdAndWorkspaceRequestId(reasonId, workspaceRequestId) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); return workspaceReasonCommentQueryRepository.getCommentsByReasonId(reasonId) 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 index d7cda85e..ac044d40 100644 --- a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceReason.java @@ -39,9 +39,9 @@ public class WorkspaceReason { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @JoinColumn(name = "workspace_id", nullable = false) + @JoinColumn(name = "workspace_request_id", nullable = false) @ManyToOne(fetch = FetchType.LAZY, optional = false) - private Workspace workspace; + private WorkspaceRequest workspaceRequest; @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) @@ -58,9 +58,9 @@ public class WorkspaceReason { @Column(name = "updated_at", nullable = false) private LocalDateTime updatedAt; - public static WorkspaceReason create(Workspace workspace, WorkspaceReasonStatus status, String reason) { + public static WorkspaceReason create(WorkspaceRequest workspaceRequest, WorkspaceReasonStatus status, String reason) { return WorkspaceReason.builder() - .workspace(workspace) + .workspaceRequest(workspaceRequest) .status(status) .reason(reason) .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 index f3422eca..7541570d 100644 --- 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 @@ -1,8 +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; +import com.dreamteam.alter.domain.user.context.AppActor; public interface CreateWorkspaceReasonCommentUseCase { - void execute(ManagerActor actor, Long workspaceId, Long reasonId, CreateWorkspaceReasonCommentRequestDto request); + void execute(AppActor actor, Long workspaceRequestId, Long reasonId, CreateWorkspaceReasonCommentRequestDto 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 index 7b26aed3..5fc5704b 100644 --- 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 @@ -3,8 +3,9 @@ import java.util.List; import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.WorkspaceReasonCommentResponseDto; +import com.dreamteam.alter.domain.user.context.AppActor; import com.dreamteam.alter.domain.user.context.ManagerActor; public interface GetWorkspaceReasonCommentsUseCase { - List execute(ManagerActor actor, Long workspaceId, Long reasonId); + List execute(AppActor actor, Long workspaceRequestId, Long reasonId); } 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 index b8ceb774..8c932138 100644 --- 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 @@ -5,5 +5,5 @@ import com.dreamteam.alter.domain.workspace.entity.WorkspaceReason; public interface WorkspaceReasonQueryRepository { - Optional findByIdAndWorkspaceId(Long reasonId, Long workspaceId); + Optional findByIdAndWorkspaceRequestId(Long reasonId, Long workspaceRequestId); } diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java new file mode 100644 index 00000000..2528a023 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java @@ -0,0 +1,5 @@ +package com.dreamteam.alter.domain.workspace.port.outbound; + +public interface WorkspaceRequestQueryRepository { + boolean existsByIdAndUserId(Long workspaceRequestId, Long userId); +} From fed2dce1c990ed1ad4a8720e6a448d0c39c5d1ee Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 22:01:40 +0900 Subject: [PATCH 15/20] =?UTF-8?q?feat:=20=EC=97=85=EC=9E=A5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=8B=A0=EC=B2=AD=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserWorkspaceReasonCommentController.java | 2 +- ...rWorkspaceReasonCommentControllerSpec.java | 2 +- ...> UserWorkspaceRequestControllerSpec.java} | 4 +- ...serWorkspaceRequestRequestController.java} | 22 ++++++-- .../WorkspaceReasonCommentResponseDto.java | 2 +- .../dto/WorkspaceRequestListResponseDto.java | 54 +++++++++++++++++++ .../WorkspaceRequestQueryRepositoryImpl.java | 25 ++++++++- .../WorkspaceRequestListResponse.java | 21 ++++++++ .../usecase/CreateWorkspaceRequest.java | 2 +- .../usecase/GetWorkspaceReasonComments.java | 2 +- .../usecase/GetWorkspaceRequestList.java | 28 ++++++++++ .../GetWorkspaceReasonCommentsUseCase.java | 3 +- .../GetWorkspaceRequestListUseCase.java | 10 ++++ .../WorkspaceRequestQueryRepository.java | 6 +++ 14 files changed, 168 insertions(+), 15 deletions(-) rename src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/{UserWorkspaceControllerSpec.java => UserWorkspaceRequestControllerSpec.java} (93%) rename src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/{UserWorkspaceController.java => UserWorkspaceRequestRequestController.java} (57%) rename src/main/java/com/dreamteam/alter/adapter/inbound/{manager => general}/workspace/dto/WorkspaceReasonCommentResponseDto.java (94%) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestListResponse.java create mode 100644 src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequestList.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestListUseCase.java diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentController.java index f1ed5735..4a4766be 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentController.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentController.java @@ -13,7 +13,7 @@ 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.adapter.inbound.general.workspace.dto.WorkspaceReasonCommentResponseDto; import com.dreamteam.alter.application.aop.AppActionContext; import com.dreamteam.alter.domain.user.context.AppActor; import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceReasonCommentUseCase; diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java index 5ef0508d..1351d835 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceReasonCommentControllerSpec.java @@ -4,7 +4,7 @@ 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.adapter.inbound.general.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; 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/UserWorkspaceRequestControllerSpec.java similarity index 93% rename from src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceControllerSpec.java rename to src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java index a5e63fd9..dbe12128 100644 --- 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/UserWorkspaceRequestControllerSpec.java @@ -10,8 +10,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; -@Tag(name = "APP - 업장 API") -public interface UserWorkspaceControllerSpec { +@Tag(name = "APP - 업장 등록 API") +public interface UserWorkspaceRequestControllerSpec { @Operation(summary = "업장 등록 신청") @ApiResponses(value = { 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/UserWorkspaceRequestRequestController.java similarity index 57% rename from src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceController.java rename to src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestRequestController.java index 6e2154e9..645a6d10 100644 --- 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/UserWorkspaceRequestRequestController.java @@ -1,7 +1,10 @@ package com.dreamteam.alter.adapter.inbound.general.workspace.controller; +import java.util.List; + import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -9,9 +12,11 @@ import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestListResponseDto; import com.dreamteam.alter.application.aop.AppActionContext; import com.dreamteam.alter.domain.user.context.AppActor; import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceRequestUseCase; +import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceRequestListUseCase; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -21,10 +26,13 @@ @RequestMapping("/app/workspaces") @RequiredArgsConstructor @Validated -public class UserWorkspaceController implements UserWorkspaceControllerSpec { +public class UserWorkspaceRequestRequestController implements UserWorkspaceRequestControllerSpec { + + @Resource(name = "createWorkspaceRequest") + private final CreateWorkspaceRequestUseCase createWorkspaceRequest; - @Resource(name = "createWorkspace") - private final CreateWorkspaceRequestUseCase createWorkspace; + @Resource(name = "getWorkspaceRequestList") + private final GetWorkspaceRequestListUseCase getWorkspaceRequestList; @Override @PostMapping @@ -32,7 +40,13 @@ public ResponseEntity> createWorkspace( @RequestBody @Valid CreateWorkspaceRequestDto request ) { AppActor actor = AppActionContext.getInstance().getActor(); - createWorkspace.execute(actor, request); + createWorkspaceRequest.execute(actor, request); return ResponseEntity.ok(CommonApiResponse.empty()); } + + @GetMapping + public ResponseEntity>> getWorkspaces() { + AppActor actor = AppActionContext.getInstance().getActor(); + return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceRequestList.execute(actor))); + } } 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/general/workspace/dto/WorkspaceReasonCommentResponseDto.java similarity index 94% rename from src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java rename to src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceReasonCommentResponseDto.java index 979b157a..702e0a26 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/manager/workspace/dto/WorkspaceReasonCommentResponseDto.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceReasonCommentResponseDto.java @@ -1,4 +1,4 @@ -package com.dreamteam.alter.adapter.inbound.manager.workspace.dto; +package com.dreamteam.alter.adapter.inbound.general.workspace.dto; import java.time.LocalDateTime; diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java new file mode 100644 index 00000000..596cf012 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java @@ -0,0 +1,54 @@ +package com.dreamteam.alter.adapter.inbound.general.workspace.dto; + +import java.time.LocalDateTime; + +import com.dreamteam.alter.adapter.inbound.common.dto.DescribedEnumDto; +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestListResponse; +import com.dreamteam.alter.domain.workspace.type.WorkspaceRequestStatus; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +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 WorkspaceRequestListResponseDto { + + @NotNull + @Schema(description = "업장 등록 신청 ID", example = "1") + private Long id; + + @NotBlank + @Schema(description = "업장 이름", example = "세븐일레븐") + private String businessName; + + @NotBlank + @Schema(description = "업장 전체 주소", example = "서울특별시 강남구 테헤란로 123") + private String fullAddress; + + @NotNull + @Schema(description = "등록일", example = "2026-03-01T10:00:00") + private LocalDateTime createdAt; + + @NotNull + @Schema(description = "업장 등록 신청 상태") + private DescribedEnumDto status; + + public static WorkspaceRequestListResponseDto of(WorkspaceRequestListResponse entity) { + return WorkspaceRequestListResponseDto.builder() + .id(entity.getId()) + .businessName(entity.getBusinessName()) + .fullAddress(entity.getFullAddress()) + .createdAt(entity.getCreatedAt()) + .status(DescribedEnumDto.of(entity.getStatus(), WorkspaceRequestStatus.describe())) + .build(); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java index 3f9b4dd7..b3df2a02 100644 --- a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java @@ -1,11 +1,13 @@ package com.dreamteam.alter.adapter.outbound.workspace.persistence; +import java.util.List; + import org.springframework.stereotype.Repository; -import com.dreamteam.alter.domain.workspace.entity.QWorkspace; +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestListResponse; import com.dreamteam.alter.domain.workspace.entity.QWorkspaceRequest; import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; -import com.dreamteam.alter.domain.workspace.type.WorkspaceStatus; +import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; @@ -31,4 +33,23 @@ public boolean existsByIdAndUserId(Long workspaceRequestId, Long userId) { return result != null; } + + @Override + public List getWorkspaceRequestList(Long userId) { + QWorkspaceRequest qWorkspaceRequest = QWorkspaceRequest.workspaceRequest; + + return queryFactory + .select(Projections.constructor( + WorkspaceRequestListResponse.class, + qWorkspaceRequest.id, + qWorkspaceRequest.businessName, + qWorkspaceRequest.fullAddress, + qWorkspaceRequest.createdAt, + qWorkspaceRequest.status + )) + .from(qWorkspaceRequest) + .where(qWorkspaceRequest.user.id.eq(userId)) + .orderBy(qWorkspaceRequest.createdAt.desc()) + .fetch(); + } } diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestListResponse.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestListResponse.java new file mode 100644 index 00000000..e131d11f --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestListResponse.java @@ -0,0 +1,21 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly; + +import java.time.LocalDateTime; + +import com.dreamteam.alter.domain.workspace.type.WorkspaceRequestStatus; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class WorkspaceRequestListResponse { + + private Long id; + private String businessName; + private String fullAddress; + private LocalDateTime createdAt; + private WorkspaceRequestStatus status; +} diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java index e96116e1..f05d769e 100644 --- a/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/CreateWorkspaceRequest.java @@ -16,7 +16,7 @@ import lombok.RequiredArgsConstructor; -@Service("createWorkspace") +@Service("createWorkspaceRequest") @RequiredArgsConstructor @Transactional public class CreateWorkspaceRequest implements CreateWorkspaceRequestUseCase { 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 index b4c86a46..4aa91720 100644 --- a/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceReasonComments.java @@ -5,7 +5,7 @@ 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.adapter.inbound.general.workspace.dto.WorkspaceReasonCommentResponseDto; import com.dreamteam.alter.common.exception.CustomException; import com.dreamteam.alter.common.exception.ErrorCode; import com.dreamteam.alter.domain.user.context.AppActor; diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequestList.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequestList.java new file mode 100644 index 00000000..ff5b720b --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequestList.java @@ -0,0 +1,28 @@ +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.general.workspace.dto.WorkspaceRequestListResponseDto; +import com.dreamteam.alter.domain.user.context.AppActor; +import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceRequestListUseCase; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; + +import lombok.RequiredArgsConstructor; + +@Service("getWorkspaceRequestList") +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GetWorkspaceRequestList implements GetWorkspaceRequestListUseCase { + + private final WorkspaceRequestQueryRepository workspaceRequestQueryRepository; + + @Override + public List execute(AppActor actor) { + return workspaceRequestQueryRepository.getWorkspaceRequestList(actor.getUserId()).stream() + .map(WorkspaceRequestListResponseDto::of) + .toList(); + } +} 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 index 5fc5704b..9580ba40 100644 --- 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 @@ -2,9 +2,8 @@ import java.util.List; -import com.dreamteam.alter.adapter.inbound.manager.workspace.dto.WorkspaceReasonCommentResponseDto; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceReasonCommentResponseDto; import com.dreamteam.alter.domain.user.context.AppActor; -import com.dreamteam.alter.domain.user.context.ManagerActor; public interface GetWorkspaceReasonCommentsUseCase { List execute(AppActor actor, Long workspaceRequestId, Long reasonId); diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestListUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestListUseCase.java new file mode 100644 index 00000000..56361822 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestListUseCase.java @@ -0,0 +1,10 @@ +package com.dreamteam.alter.domain.workspace.port.inbound; + +import java.util.List; + +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestListResponseDto; +import com.dreamteam.alter.domain.user.context.AppActor; + +public interface GetWorkspaceRequestListUseCase { + List execute(AppActor actor); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java index 2528a023..47e1f901 100644 --- a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java @@ -1,5 +1,11 @@ package com.dreamteam.alter.domain.workspace.port.outbound; +import java.util.List; + +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestListResponse; + public interface WorkspaceRequestQueryRepository { boolean existsByIdAndUserId(Long workspaceRequestId, Long userId); + + List getWorkspaceRequestList(Long userId); } From 3ac5315e724ece65258b40fe953facc25cfa2981 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 22:05:57 +0900 Subject: [PATCH 16/20] =?UTF-8?q?refactor:=20API=20=EC=8A=A4=ED=8E=99=20?= =?UTF-8?q?=EB=B0=8F=20=ED=81=B4=EB=9E=98=EC=8A=A4,=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ller.java => UserWorkspaceRequestController.java} | 7 ++++--- .../UserWorkspaceRequestControllerSpec.java | 12 +++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) rename src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/{UserWorkspaceRequestRequestController.java => UserWorkspaceRequestController.java} (92%) diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestRequestController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java similarity index 92% rename from src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestRequestController.java rename to src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java index 645a6d10..84ad49ad 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestRequestController.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java @@ -26,7 +26,7 @@ @RequestMapping("/app/workspaces") @RequiredArgsConstructor @Validated -public class UserWorkspaceRequestRequestController implements UserWorkspaceRequestControllerSpec { +public class UserWorkspaceRequestController implements UserWorkspaceRequestControllerSpec { @Resource(name = "createWorkspaceRequest") private final CreateWorkspaceRequestUseCase createWorkspaceRequest; @@ -36,7 +36,7 @@ public class UserWorkspaceRequestRequestController implements UserWorkspaceReque @Override @PostMapping - public ResponseEntity> createWorkspace( + public ResponseEntity> createWorkspaceRequest( @RequestBody @Valid CreateWorkspaceRequestDto request ) { AppActor actor = AppActionContext.getInstance().getActor(); @@ -44,8 +44,9 @@ public ResponseEntity> createWorkspace( return ResponseEntity.ok(CommonApiResponse.empty()); } + @Override @GetMapping - public ResponseEntity>> getWorkspaces() { + public ResponseEntity>> getWorkspaceRequestList() { AppActor actor = AppActionContext.getInstance().getActor(); return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceRequestList.execute(actor))); } diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java index dbe12128..ebf1f83f 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java @@ -1,7 +1,11 @@ package com.dreamteam.alter.adapter.inbound.general.workspace.controller; +import java.util.List; + import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestListResponseDto; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -21,7 +25,13 @@ public interface UserWorkspaceRequestControllerSpec { @ApiResponse(responseCode = "404", description = "존재하지 않는 파일입니다. (FILE_NOT_FOUND)"), @ApiResponse(responseCode = "409", description = "이미 연결된 파일입니다. (FILE_ALREADY_ATTACHED)") }) - ResponseEntity> createWorkspace( + ResponseEntity> createWorkspaceRequest( @RequestBody @Valid CreateWorkspaceRequestDto request ); + + @Operation(summary = "업장 등록 신청 목록 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "업장 등록 신청 목록 조회 성공"), + }) + ResponseEntity>> getWorkspaceRequestList(); } From beeb374022f32f04146d50a40506f68eade6967c Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 22:06:35 +0900 Subject: [PATCH 17/20] =?UTF-8?q?refactor:=20API=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/controller/UserWorkspaceRequestController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java index 84ad49ad..23501605 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java @@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor; @RestController -@RequestMapping("/app/workspaces") +@RequestMapping("/app/workspace-requests") @RequiredArgsConstructor @Validated public class UserWorkspaceRequestController implements UserWorkspaceRequestControllerSpec { From ce19e8b1aae8f58ab2d0726f47488e59e712d62d Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 22:23:56 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20=EC=97=85=EC=9E=A5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=8B=A0=EC=B2=AD=20=EC=83=81=EC=84=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserWorkspaceRequestController.java | 15 ++++ .../UserWorkspaceRequestControllerSpec.java | 15 ++++ .../dto/WorkspaceRequestResponseDto.java | 72 +++++++++++++++++++ .../WorkspaceRequestQueryRepositoryImpl.java | 25 +++++++ .../readonly/WorkspaceRequestResponse.java | 28 ++++++++ .../usecase/GetWorkspaceRequest.java | 34 +++++++++ .../inbound/GetWorkspaceRequestUseCase.java | 8 +++ .../WorkspaceRequestQueryRepository.java | 3 + 8 files changed, 200 insertions(+) create mode 100644 src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestResponseDto.java create mode 100644 src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestResponse.java create mode 100644 src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequest.java create mode 100644 src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestUseCase.java diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java index 23501605..3bb0ab67 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestController.java @@ -5,6 +5,7 @@ import org.springframework.http.ResponseEntity; 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; @@ -13,10 +14,12 @@ import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestListResponseDto; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestResponseDto; import com.dreamteam.alter.application.aop.AppActionContext; import com.dreamteam.alter.domain.user.context.AppActor; import com.dreamteam.alter.domain.workspace.port.inbound.CreateWorkspaceRequestUseCase; import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceRequestListUseCase; +import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceRequestUseCase; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -34,6 +37,9 @@ public class UserWorkspaceRequestController implements UserWorkspaceRequestContr @Resource(name = "getWorkspaceRequestList") private final GetWorkspaceRequestListUseCase getWorkspaceRequestList; + @Resource(name = "getWorkspaceRequest") + private final GetWorkspaceRequestUseCase getWorkspaceRequest; + @Override @PostMapping public ResponseEntity> createWorkspaceRequest( @@ -50,4 +56,13 @@ public ResponseEntity>> AppActor actor = AppActionContext.getInstance().getActor(); return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceRequestList.execute(actor))); } + + @Override + @GetMapping("/{workspaceRequestId}") + public ResponseEntity> getWorkspaceRequestDetail( + @PathVariable Long workspaceRequestId + ) { + AppActor actor = AppActionContext.getInstance().getActor(); + return ResponseEntity.ok(CommonApiResponse.of(getWorkspaceRequest.execute(actor, workspaceRequestId))); + } } diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java index ebf1f83f..d013ddb3 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/controller/UserWorkspaceRequestControllerSpec.java @@ -5,6 +5,9 @@ import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; import com.dreamteam.alter.adapter.inbound.general.workspace.dto.CreateWorkspaceRequestDto; import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestListResponseDto; +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestResponseDto; +import com.dreamteam.alter.application.aop.AppActionContext; +import com.dreamteam.alter.domain.user.context.AppActor; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -12,6 +15,8 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @Tag(name = "APP - 업장 등록 API") @@ -34,4 +39,14 @@ ResponseEntity> createWorkspaceRequest( @ApiResponse(responseCode = "200", description = "업장 등록 신청 목록 조회 성공"), }) ResponseEntity>> getWorkspaceRequestList(); + + + @Operation(summary = "업장 등록 신청 상세 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "업장 등록 신청 상세 조회 성공"), + @ApiResponse(responseCode = "404", description = "업장 등록 신청 찾을 수 없습니다."), + }) + ResponseEntity> getWorkspaceRequestDetail( + @PathVariable Long workspaceRequestId + ); } diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestResponseDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestResponseDto.java new file mode 100644 index 00000000..619665ba --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestResponseDto.java @@ -0,0 +1,72 @@ +package com.dreamteam.alter.adapter.inbound.general.workspace.dto; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.dreamteam.alter.adapter.inbound.common.dto.DescribedEnumDto; +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestResponse; +import com.dreamteam.alter.domain.workspace.type.WorkspaceRequestStatus; + +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 WorkspaceRequestResponseDto { + + @Schema(description = "업장 등록 신청 ID", example = "1") + private Long id; + + @Schema(description = "사업자 등록번호", example = "123-45-67890") + private String businessRegistrationNo; + + @Schema(description = "업장 이름", example = "드림팀 카페") + private String businessName; + + @Schema(description = "업종", example = "카페") + private String businessType; + + @Schema(description = "연락처", example = "01012345678") + private String contact; + + @Schema(description = "업장 등록 신청 상태") + private DescribedEnumDto status; + + @Schema(description = "업장 전체 주소", example = "서울특별시 강남구 테헤란로 123") + private String fullAddress; + + @Schema(description = "업장 위도", example = "37.5665") + private BigDecimal latitude; + + @Schema(description = "업장 경도", example = "126.9780") + private BigDecimal longitude; + + @Schema(description = "업장 등록 신청 일시", example = "2023-10-01T12:00:00") + private LocalDateTime createdAt; + + @Schema(description = "업장 등록 신청 수정 일시", example = "2023-10-01T12:00:00") + private LocalDateTime updatedAt; + + public static WorkspaceRequestResponseDto of(WorkspaceRequestResponse workspaceRequest) { + return WorkspaceRequestResponseDto.builder() + .id(workspaceRequest.getId()) + .businessRegistrationNo(workspaceRequest.getBusinessRegistrationNo()) + .businessName(workspaceRequest.getBusinessName()) + .businessType(workspaceRequest.getBusinessType()) + .contact(workspaceRequest.getContact()) + .status(DescribedEnumDto.of(workspaceRequest.getStatus(), WorkspaceRequestStatus.describe())) + .fullAddress(workspaceRequest.getFullAddress()) + .latitude(workspaceRequest.getLatitude()) + .longitude(workspaceRequest.getLongitude()) + .createdAt(workspaceRequest.getCreatedAt()) + .updatedAt(workspaceRequest.getUpdatedAt()) + .build(); + } +} diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java index b3df2a02..c4ee0c6c 100644 --- a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/WorkspaceRequestQueryRepositoryImpl.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Repository; import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestListResponse; +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestResponse; import com.dreamteam.alter.domain.workspace.entity.QWorkspaceRequest; import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; import com.querydsl.core.types.Projections; @@ -52,4 +53,28 @@ public List getWorkspaceRequestList(Long userId) { .orderBy(qWorkspaceRequest.createdAt.desc()) .fetch(); } + + @Override + public WorkspaceRequestResponse getWorkspaceRequest(Long userId, Long workspaceRequestId) { + QWorkspaceRequest qWorkspaceRequest = QWorkspaceRequest.workspaceRequest; + + return queryFactory + .select(Projections.constructor( + WorkspaceRequestResponse.class, + qWorkspaceRequest.id, + qWorkspaceRequest.businessRegistrationNo, + qWorkspaceRequest.businessName, + qWorkspaceRequest.businessType, + qWorkspaceRequest.contact, + qWorkspaceRequest.fullAddress, + qWorkspaceRequest.latitude, + qWorkspaceRequest.longitude, + qWorkspaceRequest.status, + qWorkspaceRequest.createdAt, + qWorkspaceRequest.updatedAt + )) + .from(qWorkspaceRequest) + .where(qWorkspaceRequest.user.id.eq(userId)) + .fetchOne(); + } } diff --git a/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestResponse.java b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestResponse.java new file mode 100644 index 00000000..28af8488 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/adapter/outbound/workspace/persistence/readonly/WorkspaceRequestResponse.java @@ -0,0 +1,28 @@ +package com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.dreamteam.alter.domain.workspace.type.WorkspaceRequestStatus; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class WorkspaceRequestResponse { + + private Long id; + private String businessRegistrationNo; + private String businessName; + private String businessType; + private String contact; + private String fullAddress; + private BigDecimal latitude; + private BigDecimal longitude; + private WorkspaceRequestStatus status; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequest.java b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequest.java new file mode 100644 index 00000000..ae15de52 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/application/workspace/usecase/GetWorkspaceRequest.java @@ -0,0 +1,34 @@ +package com.dreamteam.alter.application.workspace.usecase; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; + +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestResponseDto; +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestResponse; +import com.dreamteam.alter.common.exception.CustomException; +import com.dreamteam.alter.common.exception.ErrorCode; +import com.dreamteam.alter.domain.user.context.AppActor; +import com.dreamteam.alter.domain.workspace.port.inbound.GetWorkspaceRequestUseCase; +import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceRequestQueryRepository; + +import lombok.RequiredArgsConstructor; + +@Service("getWorkspaceRequest") +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GetWorkspaceRequest implements GetWorkspaceRequestUseCase { + + private final WorkspaceRequestQueryRepository workspaceRequestQueryRepository; + + @Override + public WorkspaceRequestResponseDto execute(AppActor actor, Long workspaceRequestId) { + WorkspaceRequestResponse workspaceRequest = workspaceRequestQueryRepository.getWorkspaceRequest(actor.getUserId(), workspaceRequestId); + + if (ObjectUtils.isEmpty(workspaceRequest)) { + throw new CustomException(ErrorCode.NOT_FOUND, "등록 신청한 업장을 찾을 수 없습니다."); + } + + return WorkspaceRequestResponseDto.of(workspaceRequest); + } +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestUseCase.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestUseCase.java new file mode 100644 index 00000000..c650c5f4 --- /dev/null +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/inbound/GetWorkspaceRequestUseCase.java @@ -0,0 +1,8 @@ +package com.dreamteam.alter.domain.workspace.port.inbound; + +import com.dreamteam.alter.adapter.inbound.general.workspace.dto.WorkspaceRequestResponseDto; +import com.dreamteam.alter.domain.user.context.AppActor; + +public interface GetWorkspaceRequestUseCase { + WorkspaceRequestResponseDto execute(AppActor actor, Long workspaceRequestId); +} diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java index 47e1f901..062c552b 100644 --- a/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java +++ b/src/main/java/com/dreamteam/alter/domain/workspace/port/outbound/WorkspaceRequestQueryRepository.java @@ -3,9 +3,12 @@ import java.util.List; import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestListResponse; +import com.dreamteam.alter.adapter.outbound.workspace.persistence.readonly.WorkspaceRequestResponse; public interface WorkspaceRequestQueryRepository { boolean existsByIdAndUserId(Long workspaceRequestId, Long userId); List getWorkspaceRequestList(Long userId); + + WorkspaceRequestResponse getWorkspaceRequest(Long userId, Long workspaceRequestId); } From a07dc3c54001cedb33dcfc9155f1b6a3d61fd1bf Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 22:27:36 +0900 Subject: [PATCH 19/20] =?UTF-8?q?refactor:=20=EC=9D=91=EB=8B=B5=20DTO=20va?= =?UTF-8?q?lidation=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/dto/WorkspaceRequestListResponseDto.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java index 596cf012..e6206992 100644 --- a/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java +++ b/src/main/java/com/dreamteam/alter/adapter/inbound/general/workspace/dto/WorkspaceRequestListResponseDto.java @@ -22,23 +22,18 @@ @Schema(description = "업장 등록 신청 목록 조회 응답 DTO") public class WorkspaceRequestListResponseDto { - @NotNull @Schema(description = "업장 등록 신청 ID", example = "1") private Long id; - @NotBlank @Schema(description = "업장 이름", example = "세븐일레븐") private String businessName; - @NotBlank @Schema(description = "업장 전체 주소", example = "서울특별시 강남구 테헤란로 123") private String fullAddress; - @NotNull @Schema(description = "등록일", example = "2026-03-01T10:00:00") private LocalDateTime createdAt; - @NotNull @Schema(description = "업장 등록 신청 상태") private DescribedEnumDto status; From 3d4135b48196b7adb259ace97b07ea8cdfe6b3c2 Mon Sep 17 00:00:00 2001 From: Choi Junyoung Date: Mon, 30 Mar 2026 22:27:49 +0900 Subject: [PATCH 20/20] =?UTF-8?q?fix:=20WorkspaceRequest=20table=EB=AA=85?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alter/domain/workspace/entity/WorkspaceRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java index 5bad9065..d477d303 100644 --- a/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java +++ b/src/main/java/com/dreamteam/alter/domain/workspace/entity/WorkspaceRequest.java @@ -30,7 +30,7 @@ @Entity @Getter -@Table(name = "workspaces") +@Table(name = "workspace_requests") @Builder(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE)