diff --git a/src/main/java/life/mosu/mosuserver/application/faq/FaqAttachmentService.java b/src/main/java/life/mosu/mosuserver/application/faq/FaqAttachmentService.java index 8307f04b..414e9fb2 100644 --- a/src/main/java/life/mosu/mosuserver/application/faq/FaqAttachmentService.java +++ b/src/main/java/life/mosu/mosuserver/application/faq/FaqAttachmentService.java @@ -26,6 +26,14 @@ public class FaqAttachmentService implements AttachmentService requests, FaqJpaEntity faqEntity) { fileUploadHelper.saveAttachments( @@ -41,6 +49,11 @@ public void createAttachment(List requests, FaqJpaEntity faqEntity) ); } + /** + * Deletes all attachments associated with the specified FAQ entity. + * + * @param entity the FAQ entity whose attachments will be deleted + */ @Override public void deleteAttachment(FaqJpaEntity entity) { List attachments = faqAttachmentRepository.findAllByFaqId( diff --git a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAnswerAttachmentService.java b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAnswerAttachmentService.java index 3b81af73..fb4630c6 100644 --- a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAnswerAttachmentService.java +++ b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAnswerAttachmentService.java @@ -24,6 +24,14 @@ public class InquiryAnswerAttachmentService implements private final FileUploadHelper fileUploadHelper; private final S3Service s3Service; + /** + * Saves a list of file attachments associated with the specified inquiry answer entity. + * + * Each file request is converted into an attachment entity and persisted, linking it to the given inquiry answer. + * + * @param requests the list of file requests to be attached + * @param answerEntity the inquiry answer entity to associate the attachments with + */ @Override public void createAttachment(List requests, InquiryAnswerJpaEntity answerEntity) { fileUploadHelper.saveAttachments( @@ -73,6 +81,12 @@ private InquiryDetailResponse.AttachmentResponse createAttachResponse( ); } + /** + * Creates an attachment detail response containing the file name, a presigned S3 URL, and the S3 key for the given attachment entity. + * + * @param attachment the attachment entity to convert + * @return an AttachmentDetailResponse with file name, presigned URL, and S3 key + */ private InquiryDetailResponse.AttachmentDetailResponse createAttachDetailResponse( InquiryAnswerAttachmentEntity attachment) { String presignedUrl = s3Service.getPreSignedUrl( diff --git a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAttachmentService.java b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAttachmentService.java index 2d913245..874104a6 100644 --- a/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAttachmentService.java +++ b/src/main/java/life/mosu/mosuserver/application/inquiry/InquiryAttachmentService.java @@ -23,6 +23,14 @@ public class InquiryAttachmentService implements AttachmentService requests, InquiryJpaEntity inquiryEntity) { fileUploadHelper.saveAttachments( @@ -38,6 +46,11 @@ public void createAttachment(List requests, InquiryJpaEntity inquir ); } + /** + * Deletes all attachments associated with the specified inquiry entity. + * + * @param entity the inquiry entity whose attachments should be deleted + */ @Override public void deleteAttachment(InquiryJpaEntity entity) { List attachments = inquiryAttachmentRepository.findAllByInquiryId( diff --git a/src/main/java/life/mosu/mosuserver/application/notice/NoticeAttachmentService.java b/src/main/java/life/mosu/mosuserver/application/notice/NoticeAttachmentService.java index 3f88bf5e..94a85dc2 100644 --- a/src/main/java/life/mosu/mosuserver/application/notice/NoticeAttachmentService.java +++ b/src/main/java/life/mosu/mosuserver/application/notice/NoticeAttachmentService.java @@ -26,6 +26,14 @@ public class NoticeAttachmentService implements AttachmentService requests, NoticeJpaEntity noticeEntity) { fileUploadHelper.saveAttachments( @@ -41,6 +49,11 @@ public void createAttachment(List requests, NoticeJpaEntity noticeE ); } + /** + * Deletes all attachments associated with the specified notice entity. + * + * @param entity the notice entity whose attachments will be deleted + */ @Override public void deleteAttachment(NoticeJpaEntity entity) { List attachments = noticeAttachmentRepository.findAllByNoticeId( @@ -48,6 +61,13 @@ public void deleteAttachment(NoticeJpaEntity entity) { noticeAttachmentRepository.deleteAll(attachments); } + /** + * Converts all attachments of the specified notice entity into a list of response DTOs, + * each containing the file name and a pre-signed URL for secure access. + * + * @param notice the notice entity whose attachments are to be converted + * @return a list of attachment response DTOs with file names and pre-signed URLs + */ public List toAttachmentResponses(NoticeJpaEntity notice) { List attachments = noticeAttachmentRepository.findAllByNoticeId( @@ -64,6 +84,14 @@ public List toAttachmentResponses(NoticeJpaEn .toList(); } + /** + * Converts all attachments associated with the given notice entity into a list of detailed attachment response DTOs. + * + * Each response includes the file name, a pre-signed URL for accessing the file, and the S3 key. + * + * @param notice the notice entity whose attachments are to be converted + * @return a list of detailed attachment response DTOs for the notice + */ public List toDetailAttResponses( NoticeJpaEntity notice) { @@ -80,6 +108,12 @@ public List toDetailAttResponses( .toList(); } + /** + * Generates a pre-signed URL for the specified S3 key with an expiration time defined in the S3 properties. + * + * @param s3Key the S3 object key for which to generate the pre-signed URL + * @return a pre-signed URL granting temporary access to the S3 object + */ private String fileUrl(String s3Key) { return s3Service.getPreSignedUrl( s3Key, diff --git a/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java b/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java index 251ff373..f40701ed 100644 --- a/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java +++ b/src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java @@ -25,12 +25,24 @@ public class NoticeService { private final NoticeRepository noticeRepository; private final NoticeAttachmentService attachmentService; + /** + * Creates a new notice and its associated attachments. + * + * Persists a notice entity based on the provided request and delegates attachment creation to the attachment service. + */ @Transactional public void createNotice(NoticeCreateRequest request) { NoticeJpaEntity noticeEntity = noticeRepository.save(request.toEntity()); attachmentService.createAttachment(request.attachments(), noticeEntity); } + /** + * Retrieves a paginated list of notices, each including its associated attachments. + * + * @param page the zero-based page index to retrieve + * @param size the number of notices per page + * @return a list of notice responses with their attachments + */ @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) public List getNoticeWithAttachments(int page, int size) { Pageable pageable = PageRequest.of(page, size, Sort.by("id")); @@ -41,6 +53,13 @@ public List getNoticeWithAttachments(int page, int size) { .toList(); } + /** + * Retrieves detailed information for a specific notice, including its attachments. + * + * @param noticeId the unique identifier of the notice to retrieve + * @return a detailed response containing notice information and its attachments + * @throws CustomRuntimeException if the notice with the given ID is not found + */ @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) public NoticeDetailResponse getNoticeDetail(Long noticeId) { NoticeJpaEntity notice = getNoticeOrThrow(noticeId); @@ -48,6 +67,13 @@ public NoticeDetailResponse getNoticeDetail(Long noticeId) { return toNoticeDetailResponse(notice); } + /** + * Deletes a notice and its associated attachments by notice ID. + * + * Throws a {@code CustomRuntimeException} with {@code ErrorCode.FILE_NOT_FOUND} if the notice does not exist. + * + * @param noticeId the ID of the notice to delete + */ @Transactional public void deleteNotice(Long noticeId) { NoticeJpaEntity noticeEntity = noticeRepository.findById(noticeId) @@ -56,6 +82,15 @@ public void deleteNotice(Long noticeId) { attachmentService.deleteAttachment(noticeEntity); } + /** + * Updates the title, content, and attachments of an existing notice. + * + * Replaces the notice's current attachments with the new attachments provided in the request. + * + * @param noticeId the ID of the notice to update + * @param request the update request containing new title, content, and attachments + * @throws CustomRuntimeException if the notice is not found + */ @Transactional public void updateNotice(Long noticeId, NoticeUpdateRequest request) { NoticeJpaEntity noticeEntity = noticeRepository.findById(noticeId) @@ -66,11 +101,23 @@ public void updateNotice(Long noticeId, NoticeUpdateRequest request) { attachmentService.createAttachment(request.attachments(), noticeEntity); } + /** + * Converts a notice entity to a response DTO, including its attachments. + * + * @param notice the notice entity to convert + * @return the response DTO representing the notice and its attachments + */ private NoticeResponse toNoticeResponse(NoticeJpaEntity notice) { return NoticeResponse.of(notice, attachmentService.toAttachmentResponses(notice)); } + /** + * Converts a notice entity to a detailed response DTO, including detailed attachment information. + * + * @param notice the notice entity to convert + * @return a detailed response DTO representing the notice and its attachments + */ private NoticeDetailResponse toNoticeDetailResponse(NoticeJpaEntity notice) { return NoticeDetailResponse.of( notice, @@ -78,6 +125,13 @@ private NoticeDetailResponse toNoticeDetailResponse(NoticeJpaEntity notice) { ); } + /** + * Retrieves a notice entity by its ID or throws a {@code CustomRuntimeException} if not found. + * + * @param noticeId the ID of the notice to retrieve + * @return the {@code NoticeJpaEntity} corresponding to the given ID + * @throws CustomRuntimeException if the notice does not exist + */ private NoticeJpaEntity getNoticeOrThrow(Long noticeId) { return noticeRepository.findById(noticeId) .orElseThrow(() -> new CustomRuntimeException(ErrorCode.NOTICE_NOT_FOUND)); diff --git a/src/main/java/life/mosu/mosuserver/domain/notice/NoticeAttachmentRepository.java b/src/main/java/life/mosu/mosuserver/domain/notice/NoticeAttachmentRepository.java index de666beb..93d2bc51 100644 --- a/src/main/java/life/mosu/mosuserver/domain/notice/NoticeAttachmentRepository.java +++ b/src/main/java/life/mosu/mosuserver/domain/notice/NoticeAttachmentRepository.java @@ -5,5 +5,11 @@ public interface NoticeAttachmentRepository extends JpaRepository { - List findAllByNoticeId(Long id); + /** + * Retrieves all notice attachment entities associated with the specified notice ID. + * + * @param id the unique identifier of the notice + * @return a list of notice attachment entities linked to the given notice ID + */ +List findAllByNoticeId(Long id); } diff --git a/src/main/java/life/mosu/mosuserver/domain/notice/NoticeJpaEntity.java b/src/main/java/life/mosu/mosuserver/domain/notice/NoticeJpaEntity.java index 61b72723..f966cb1e 100644 --- a/src/main/java/life/mosu/mosuserver/domain/notice/NoticeJpaEntity.java +++ b/src/main/java/life/mosu/mosuserver/domain/notice/NoticeJpaEntity.java @@ -32,6 +32,13 @@ public class NoticeJpaEntity extends BaseTimeEntity { @Column(name = "user_id", nullable = false) private Long userId; + /** + * Constructs a new NoticeJpaEntity with the specified title, content, and user ID. + * + * @param title the title of the notice + * @param content the content of the notice + * @param userId the ID of the user associated with the notice + */ @Builder public NoticeJpaEntity( final String title, @@ -43,6 +50,12 @@ public NoticeJpaEntity( this.userId = userId; } + /** + * Updates the title and content of this notice entity. + * + * @param title the new title for the notice + * @param content the new content for the notice + */ public void update( final String title, final String content diff --git a/src/main/java/life/mosu/mosuserver/global/util/FileRequest.java b/src/main/java/life/mosu/mosuserver/global/util/FileRequest.java index d148fc25..4520dac2 100644 --- a/src/main/java/life/mosu/mosuserver/global/util/FileRequest.java +++ b/src/main/java/life/mosu/mosuserver/global/util/FileRequest.java @@ -12,6 +12,14 @@ public record FileRequest( String s3Key ) { + /** + * Creates a FaqAttachmentJpaEntity with the specified file name, S3 key, and FAQ ID, setting its visibility to public. + * + * @param fileName the name of the file to associate with the FAQ attachment + * @param s3Key the S3 storage key for the file + * @param faqId the identifier of the FAQ to which the attachment belongs + * @return a new FaqAttachmentJpaEntity instance with public visibility + */ public FaqAttachmentJpaEntity toFaqAttachmentEntity(String fileName, String s3Key, Long faqId) { return FaqAttachmentJpaEntity.builder() .fileName(fileName) @@ -21,6 +29,14 @@ public FaqAttachmentJpaEntity toFaqAttachmentEntity(String fileName, String s3Ke .build(); } + /** + * Creates a NoticeAttachmentJpaEntity with the specified file name, S3 key, and notice ID, setting its visibility to public. + * + * @param fileName the name of the file to associate with the notice attachment + * @param s3Key the S3 storage key for the file + * @param noticeId the identifier of the notice to which the attachment belongs + * @return a NoticeAttachmentJpaEntity configured with the provided details and public visibility + */ public NoticeAttachmentJpaEntity toNoticeAttachmentEntity(String fileName, String s3Key, Long noticeId) { return NoticeAttachmentJpaEntity.builder() @@ -31,6 +47,14 @@ public NoticeAttachmentJpaEntity toNoticeAttachmentEntity(String fileName, Strin .build(); } + /** + * Creates an InquiryAttachmentJpaEntity with the specified file name, S3 key, and inquiry ID, setting the visibility to private. + * + * @param fileName the name of the file to associate with the inquiry attachment + * @param s3Key the S3 storage key for the file + * @param inquiryId the ID of the inquiry to which the attachment belongs + * @return a new InquiryAttachmentJpaEntity configured with the provided values and private visibility + */ public InquiryAttachmentJpaEntity toInquiryAttachmentEntity(String fileName, String s3Key, Long inquiryId) { return InquiryAttachmentJpaEntity.builder() diff --git a/src/main/java/life/mosu/mosuserver/infra/storage/FileUploadHelper.java b/src/main/java/life/mosu/mosuserver/infra/storage/FileUploadHelper.java index 1b1aacbd..8c940065 100644 --- a/src/main/java/life/mosu/mosuserver/infra/storage/FileUploadHelper.java +++ b/src/main/java/life/mosu/mosuserver/infra/storage/FileUploadHelper.java @@ -14,10 +14,28 @@ public class FileUploadHelper { private final S3Service s3Service; + /** + * Marks the file identified by the given S3 key as active in S3 storage. + * + * @param s3Key the S3 key of the file to update + */ public void updateTag(String s3Key) { s3Service.updateFileTagToActive(s3Key); } + /** + * Processes a list of request objects by marking their associated S3 files as active and saving corresponding entities to the database. + * + * For each request, extracts the S3 key, updates the file's tag to active in S3, maps the request and parent ID to an entity, and persists the entity using the provided repository. + * + * No action is taken if the request list is null or empty. + * + * @param requests the list of request objects to process + * @param parentId the identifier to associate with each entity + * @param repository the JPA repository used to save entities + * @param toEntityMapper a function that maps a request and parent ID to an entity + * @param getKey a function that extracts the S3 key from a request + */ public void saveAttachments( List requests, Long parentId, diff --git a/src/main/java/life/mosu/mosuserver/presentation/notice/NoticeController.java b/src/main/java/life/mosu/mosuserver/presentation/notice/NoticeController.java index f2cc96d1..018b696c 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/notice/NoticeController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/notice/NoticeController.java @@ -28,7 +28,15 @@ public class NoticeController { private final NoticeService noticeService; - // TODO: 관리자 권한 체크 추가 + /** + * Handles HTTP POST requests to create a new notice. + * + * Accepts a validated notice creation request and delegates the creation to the service layer. + * Returns a standardized API response indicating successful creation with HTTP status 201 (Created). + * + * @param request the validated notice creation request payload + * @return a response entity containing the API response wrapper with a success message + */ @PostMapping public ResponseEntity> createNotice( @Valid @RequestBody NoticeCreateRequest request) { @@ -36,6 +44,13 @@ public ResponseEntity> createNotice( return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.CREATED, "게시글 등록 성공")); } + /** + * Retrieves a paginated list of notices with their attachments. + * + * @param page the page number to retrieve (default is 0) + * @param size the number of notices per page (default is 10) + * @return a response entity containing a standardized API response with the list of notices + */ @GetMapping("/list") public ResponseEntity>> getNotices( @RequestParam(defaultValue = "0") int page, @@ -45,6 +60,12 @@ public ResponseEntity>> getNotices( return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "게시글 조회 성공", notices)); } + /** + * Retrieves detailed information for a specific notice by its ID. + * + * @param noticeId the unique identifier of the notice to retrieve + * @return a response entity containing the detailed notice information wrapped in an API response + */ @GetMapping("/{noticeId}") public ResponseEntity> getNoticeDetail( @PathVariable Long noticeId) { @@ -52,14 +73,25 @@ public ResponseEntity> getNoticeDetail( return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "게시글 상세 조회 성공", notice)); } - // TODO: 관리자 권한 체크 추가 + /** + * Deletes a specific notice identified by its ID. + * + * @param noticeId the unique identifier of the notice to delete + * @return a success response indicating the notice was deleted + */ @DeleteMapping("/{noticeId}") public ResponseEntity> deleteNotice(@PathVariable Long noticeId) { noticeService.deleteNotice(noticeId); return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "게시글 삭제 성공")); } - // TODO: 관리자 권한 체크 추가 + /** + * Updates an existing notice with the provided information. + * + * @param noticeId the ID of the notice to update + * @param request the updated notice data + * @return a success response indicating the notice was updated + */ @PutMapping("/{noticeId}") public ResponseEntity> updateNotice( @PathVariable Long noticeId, diff --git a/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeCreateRequest.java b/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeCreateRequest.java index 6fcd1ef9..2100844c 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeCreateRequest.java +++ b/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeCreateRequest.java @@ -14,6 +14,11 @@ public record NoticeCreateRequest( ) { + /** + * Converts this notice creation request into a NoticeJpaEntity. + * + * @return a NoticeJpaEntity with the title, content, and userId from this request + */ public NoticeJpaEntity toEntity() { return NoticeJpaEntity.builder() .title(title) diff --git a/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeDetailResponse.java b/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeDetailResponse.java index 8a11c2a3..6446a528 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeDetailResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeDetailResponse.java @@ -10,6 +10,13 @@ public record NoticeDetailResponse( List attachments ) { + /** + * Creates a NoticeDetailResponse from a NoticeJpaEntity and a list of attachment details. + * + * @param notice the notice entity containing the ID, title, and content + * @param attachments the list of attachment details to associate with the notice + * @return a NoticeDetailResponse representing the notice and its attachments + */ public static NoticeDetailResponse of( NoticeJpaEntity notice, List attachments diff --git a/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeResponse.java b/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeResponse.java index 5e42f349..1a005a24 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeResponse.java +++ b/src/main/java/life/mosu/mosuserver/presentation/notice/dto/NoticeResponse.java @@ -10,6 +10,13 @@ public record NoticeResponse( List attachments ) { + /** + * Creates a NoticeResponse from a NoticeJpaEntity and a list of attachments. + * + * @param notice the notice entity containing the id, title, and content + * @param attachments the list of attachments to associate with the notice + * @return a NoticeResponse representing the notice and its attachments + */ public static NoticeResponse of(NoticeJpaEntity notice, List attachments) { return new NoticeResponse( notice.getId(),