diff --git a/src/main/kotlin/picklab/backend/archive/application/ArchiveUseCase.kt b/src/main/kotlin/picklab/backend/archive/application/ArchiveUseCase.kt index 7d077e31..30a75b97 100644 --- a/src/main/kotlin/picklab/backend/archive/application/ArchiveUseCase.kt +++ b/src/main/kotlin/picklab/backend/archive/application/ArchiveUseCase.kt @@ -2,14 +2,17 @@ package picklab.backend.archive.application import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional +import picklab.backend.activity.domain.enums.ActivityType import picklab.backend.activity.domain.service.ActivityService import picklab.backend.archive.domain.entity.ArchiveReferenceUrl import picklab.backend.archive.domain.entity.ArchiveUploadFileUrl +import picklab.backend.archive.domain.enums.ArchiveSortType import picklab.backend.archive.domain.service.ArchiveReferenceUrlService import picklab.backend.archive.domain.service.ArchiveService import picklab.backend.archive.domain.service.ArchiveUploadFileUrlService import picklab.backend.archive.entrypoint.request.ArchiveCreateRequest import picklab.backend.archive.entrypoint.request.ArchiveUpdateRequest +import picklab.backend.archive.entrypoint.response.ArchiveActivityResponse import picklab.backend.common.model.MemberPrincipal import picklab.backend.file.application.FileManagementService import picklab.backend.member.domain.MemberService @@ -65,4 +68,16 @@ class ArchiveUseCase( archiveService.save(archive) } + + @Transactional(readOnly = true) + fun getArchiveList( + activityType: ActivityType?, + sort: ArchiveSortType, + memberPrincipal: MemberPrincipal, + ): List { + val member = memberService.findActiveMember(memberPrincipal.memberId) + return archiveService + .findCompletedArchives(member, activityType, sort) + .map { ArchiveActivityResponse.from(it) } + } } diff --git a/src/main/kotlin/picklab/backend/archive/domain/enums/ArchiveSortType.kt b/src/main/kotlin/picklab/backend/archive/domain/enums/ArchiveSortType.kt new file mode 100644 index 00000000..e1ecbc52 --- /dev/null +++ b/src/main/kotlin/picklab/backend/archive/domain/enums/ArchiveSortType.kt @@ -0,0 +1,15 @@ +package picklab.backend.archive.domain.enums + +import org.springframework.data.domain.Sort + +enum class ArchiveSortType { + LATEST, + OLDEST, + ; + + fun toSort(): Sort = + when (this) { + LATEST -> Sort.by(Sort.Direction.DESC, "createdAt") + OLDEST -> Sort.by(Sort.Direction.ASC, "createdAt") + } +} diff --git a/src/main/kotlin/picklab/backend/archive/domain/repository/ArchiveRepository.kt b/src/main/kotlin/picklab/backend/archive/domain/repository/ArchiveRepository.kt index 4e88ad22..0f854a3c 100644 --- a/src/main/kotlin/picklab/backend/archive/domain/repository/ArchiveRepository.kt +++ b/src/main/kotlin/picklab/backend/archive/domain/repository/ArchiveRepository.kt @@ -1,8 +1,13 @@ package picklab.backend.archive.domain.repository +import org.springframework.data.domain.Sort import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param import org.springframework.stereotype.Repository +import picklab.backend.activity.domain.enums.ActivityType import picklab.backend.archive.domain.entity.Archive +import picklab.backend.archive.domain.enums.ProgressStatus import picklab.backend.member.domain.entity.Member @Repository @@ -11,4 +16,21 @@ interface ArchiveRepository : JpaRepository { id: Long, member: Member, ): Archive? + + @Query("SELECT a FROM Archive a JOIN FETCH a.activity WHERE a.member = :member AND a.activityProgressStatus = :status") + fun findByMemberAndProgressStatus( + @Param("member") member: Member, + @Param("status") status: ProgressStatus, + sort: Sort, + ): List + + @Query( + "SELECT a FROM Archive a JOIN FETCH a.activity WHERE a.member = :member AND a.activityProgressStatus = :status AND a.activityType = :activityType", + ) + fun findByMemberAndProgressStatusAndActivityType( + @Param("member") member: Member, + @Param("status") status: ProgressStatus, + @Param("activityType") activityType: ActivityType, + sort: Sort, + ): List } diff --git a/src/main/kotlin/picklab/backend/archive/domain/service/ArchiveService.kt b/src/main/kotlin/picklab/backend/archive/domain/service/ArchiveService.kt index dda73ade..94548b07 100644 --- a/src/main/kotlin/picklab/backend/archive/domain/service/ArchiveService.kt +++ b/src/main/kotlin/picklab/backend/archive/domain/service/ArchiveService.kt @@ -2,7 +2,10 @@ package picklab.backend.archive.domain.service import jakarta.transaction.Transactional import org.springframework.stereotype.Service +import picklab.backend.activity.domain.enums.ActivityType import picklab.backend.archive.domain.entity.Archive +import picklab.backend.archive.domain.enums.ArchiveSortType +import picklab.backend.archive.domain.enums.ProgressStatus import picklab.backend.archive.domain.repository.ArchiveRepository import picklab.backend.common.model.BusinessException import picklab.backend.common.model.ErrorCode @@ -21,4 +24,22 @@ class ArchiveService( ): Archive = archiveRepository .findByIdAndMember(archiveId, member) ?: throw BusinessException(ErrorCode.NOT_FOUND_ARCHIVE) + + fun findCompletedArchives( + member: Member, + activityType: ActivityType?, + sort: ArchiveSortType, + ): List { + val domainSort = sort.toSort() + return if (activityType != null) { + archiveRepository.findByMemberAndProgressStatusAndActivityType( + member, + ProgressStatus.COMPLETED, + activityType, + domainSort, + ) + } else { + archiveRepository.findByMemberAndProgressStatus(member, ProgressStatus.COMPLETED, domainSort) + } + } } diff --git a/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveApi.kt b/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveApi.kt index 537e2b30..57673681 100644 --- a/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveApi.kt +++ b/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveApi.kt @@ -1,6 +1,7 @@ package picklab.backend.archive.entrypoint import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses import io.swagger.v3.oas.annotations.tags.Tag @@ -8,8 +9,12 @@ import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestParam +import picklab.backend.activity.domain.enums.ActivityType +import picklab.backend.archive.domain.enums.ArchiveSortType import picklab.backend.archive.entrypoint.request.ArchiveCreateRequest import picklab.backend.archive.entrypoint.request.ArchiveUpdateRequest +import picklab.backend.archive.entrypoint.response.ArchiveActivityResponse import picklab.backend.common.model.MemberPrincipal import picklab.backend.common.model.ResponseWrapper @@ -44,4 +49,19 @@ interface ArchiveApi { @PathVariable archiveId: Long, @RequestBody request: ArchiveUpdateRequest, ): ResponseEntity> + + @Operation( + summary = "아카이브 활동 목록 조회", + description = "수료 완료한 활동 목록을 조회합니다.", + ) + @ApiResponses( + value = [ + ApiResponse(responseCode = "200", description = "아카이브 목록 조회에 성공했습니다."), + ], + ) + fun getList( + @AuthenticationPrincipal member: MemberPrincipal, + @Parameter(description = "활동 유형 필터 (미입력 시 전체)") @RequestParam(required = false) activityType: ActivityType?, + @Parameter(description = "정렬 순서 (LATEST: 최신순, OLDEST: 오래된순)") @RequestParam(defaultValue = "LATEST") sort: ArchiveSortType, + ): ResponseEntity>> } diff --git a/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveController.kt b/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveController.kt index 4d71413e..c9c3a45e 100644 --- a/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveController.kt +++ b/src/main/kotlin/picklab/backend/archive/entrypoint/ArchiveController.kt @@ -2,14 +2,19 @@ package picklab.backend.archive.entrypoint import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping 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.RequestParam import org.springframework.web.bind.annotation.RestController +import picklab.backend.activity.domain.enums.ActivityType import picklab.backend.archive.application.ArchiveUseCase +import picklab.backend.archive.domain.enums.ArchiveSortType import picklab.backend.archive.entrypoint.request.ArchiveCreateRequest import picklab.backend.archive.entrypoint.request.ArchiveUpdateRequest +import picklab.backend.archive.entrypoint.response.ArchiveActivityResponse import picklab.backend.common.model.MemberPrincipal import picklab.backend.common.model.ResponseWrapper import picklab.backend.common.model.SuccessCode @@ -36,4 +41,14 @@ class ArchiveController( archiveUseCase.updateArchive(archiveId, request, member) return ResponseEntity.ok(ResponseWrapper.success(SuccessCode.UPDATE_ARCHIVE_SUCCESS)) } + + @GetMapping("/v1/archive") + override fun getList( + @AuthenticationPrincipal member: MemberPrincipal, + @RequestParam(required = false) activityType: ActivityType?, + @RequestParam(defaultValue = "LATEST") sort: ArchiveSortType, + ): ResponseEntity>> { + val result = archiveUseCase.getArchiveList(activityType, sort, member) + return ResponseEntity.ok(ResponseWrapper.success(SuccessCode.GET_ARCHIVE_LIST, result)) + } } diff --git a/src/main/kotlin/picklab/backend/archive/entrypoint/request/ArchiveCreateRequest.kt b/src/main/kotlin/picklab/backend/archive/entrypoint/request/ArchiveCreateRequest.kt index 147bfe3f..379b9272 100644 --- a/src/main/kotlin/picklab/backend/archive/entrypoint/request/ArchiveCreateRequest.kt +++ b/src/main/kotlin/picklab/backend/archive/entrypoint/request/ArchiveCreateRequest.kt @@ -47,7 +47,7 @@ class ArchiveCreateRequest( userEndDate = endDate, role = role, activityProgressStatus = ProgressStatus.IN_PROGRESSING, - writeStatus = WriteStatus.IN_PROGRESS, + writeStatus = WriteStatus.COMPLETED, customRole = customRole, ) } diff --git a/src/main/kotlin/picklab/backend/archive/entrypoint/response/ArchiveActivityResponse.kt b/src/main/kotlin/picklab/backend/archive/entrypoint/response/ArchiveActivityResponse.kt new file mode 100644 index 00000000..e1f3dae8 --- /dev/null +++ b/src/main/kotlin/picklab/backend/archive/entrypoint/response/ArchiveActivityResponse.kt @@ -0,0 +1,39 @@ +package picklab.backend.archive.entrypoint.response + +import io.swagger.v3.oas.annotations.media.Schema +import picklab.backend.archive.domain.entity.Archive +import picklab.backend.archive.domain.enums.WriteStatus +import java.time.LocalDate + +data class ArchiveActivityResponse( + @field:Schema(description = "아카이브 ID") + val id: Long, + @field:Schema(description = "활동 썸네일 이미지 URL") + val activityThumbnailUrl: String?, + @field:Schema(description = "활동 유형") + val activityType: String, + @field:Schema(description = "활동명") + val title: String, + @field:Schema(description = "주최기관/단체명") + val organizer: String?, + @field:Schema(description = "활동 시작일") + val userStartDate: LocalDate, + @field:Schema(description = "활동 종료일") + val userEndDate: LocalDate, + @field:Schema(description = "작성 여부") + val writeStatus: WriteStatus, +) { + companion object { + fun from(archive: Archive): ArchiveActivityResponse = + ArchiveActivityResponse( + id = archive.id, + activityThumbnailUrl = archive.activity.activityThumbnailUrl, + activityType = archive.activityType.name, + title = archive.activity.title, + organizer = archive.activity.organizer, + userStartDate = archive.userStartDate, + userEndDate = archive.userEndDate, + writeStatus = archive.writeStatus, + ) + } +} diff --git a/src/main/kotlin/picklab/backend/common/model/SuccessCode.kt b/src/main/kotlin/picklab/backend/common/model/SuccessCode.kt index 8157e348..62922eb2 100644 --- a/src/main/kotlin/picklab/backend/common/model/SuccessCode.kt +++ b/src/main/kotlin/picklab/backend/common/model/SuccessCode.kt @@ -39,6 +39,7 @@ enum class SuccessCode( // Archive 관련 CREATE_ARCHIVE_SUCCESS(HttpStatus.OK, "아카이브 생성에 성공했습니다."), UPDATE_ARCHIVE_SUCCESS(HttpStatus.OK, "아카이브 수정에 성공했습니다."), + GET_ARCHIVE_LIST(HttpStatus.OK, "아카이브 목록 조회에 성공했습니다."), // Notification 관련 SEND_NOTIFICATION_SUCCESS(HttpStatus.OK, "알림 전송에 성공했습니다."),