From dd0d60e393fb16f29621332383151acdee33c5e5 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 15:16:47 +0900 Subject: [PATCH 01/22] =?UTF-8?q?feat:=20=EC=9D=91=EB=AA=A8=EC=9E=90=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=20=EC=9D=91=EB=AA=A8=EC=9E=90=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/pairgame/api/UserPairGameApi.java | 33 ++++++++++++ .../controller/UserPairGameController.java | 28 ++++++++++ .../request/CreatePairGameApplierRequest.java | 33 ++++++++++++ .../PairGameApplierAmountResponse.java | 15 ++++++ .../pairgame/entity/PairGameApplier.java | 52 +++++++++++++++++++ .../repository/PairGameRepository.java | 8 +++ .../service/FacadeUserPairGameService.java | 31 +++++++++++ .../pairgame/service/PairGameService.java | 26 ++++++++++ .../command/CreatePairGameApplierCommand.java | 24 +++++++++ .../dto/query/PairGameApplierAmountQuery.java | 13 +++++ .../file/service/S3FileService.java | 46 ++++++++++++++++ 11 files changed, 309 insertions(+) create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/command/CreatePairGameApplierCommand.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameApplierAmountQuery.java diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java new file mode 100644 index 00000000..f42bb068 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java @@ -0,0 +1,33 @@ +package ddingdong.ddingdongBE.domain.pairgame.api; + +import ddingdong.ddingdongBE.domain.pairgame.controller.dto.request.CreatePairGameApplierRequest; +import ddingdong.ddingdongBE.domain.pairgame.controller.dto.response.PairGameApplierAmountResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@Tag(name = "PairGame - User", description = "User PairGame API") +@RequestMapping("/server") +public interface UserPairGameApi { + + @Operation(summary = "응모자 생성 API") + @ApiResponse(responseCode = "201", description = "응모자 생성 성공") + @ResponseStatus(HttpStatus.CREATED) + @PostMapping("/pair-game/appliers") + void createPairGameApplier( + @Valid @RequestPart("request") CreatePairGameApplierRequest request, + @RequestPart("file") MultipartFile file + ); + + @Operation(summary = "응모자 현황 조회 API") + @ApiResponse(responseCode = "200", description = "응모자 현황 조회 성공") + @ResponseStatus(HttpStatus.OK) + @GetMapping("/pair-game/appliers/amount") + PairGameApplierAmountResponse getPairGameApplierAmount(); + + +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java new file mode 100644 index 00000000..81e53256 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java @@ -0,0 +1,28 @@ +package ddingdong.ddingdongBE.domain.pairgame.controller; + +import ddingdong.ddingdongBE.domain.pairgame.api.UserPairGameApi; +import ddingdong.ddingdongBE.domain.pairgame.controller.dto.request.CreatePairGameApplierRequest; +import ddingdong.ddingdongBE.domain.pairgame.controller.dto.response.PairGameApplierAmountResponse; +import ddingdong.ddingdongBE.domain.pairgame.service.FacadeUserPairGameService; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequiredArgsConstructor +public class UserPairGameController implements UserPairGameApi { + + private final FacadeUserPairGameService facadeUserPairGameService; + + @Override + public void createPairGameApplier(CreatePairGameApplierRequest createPairGameApplierRequest, MultipartFile studentFeeImageFile) { + facadeUserPairGameService.createApplier(createPairGameApplierRequest.toCommand(), studentFeeImageFile); + } + + @Override + public PairGameApplierAmountResponse getPairGameApplierAmount() { + PairGameApplierAmountQuery query = facadeUserPairGameService.getPairGameApplierAmount(); + return PairGameApplierAmountResponse.from(query); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java new file mode 100644 index 00000000..6375c807 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java @@ -0,0 +1,33 @@ +package ddingdong.ddingdongBE.domain.pairgame.controller.dto.request; + +import ddingdong.ddingdongBE.domain.pairgame.service.dto.command.CreatePairGameApplierCommand; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +public record CreatePairGameApplierRequest ( + + @NotNull(message = "응모자 이름은 필수 입력 사항입니다.") + @Schema(description = "응모자 이름", example = "김띵동") + String name, + + @NotNull(message = "응모자 학과는 필수 입력 사항입니다.") + @Schema(description = "응모자 학과", example = "융합소프트웨어학부") + String department, + + @NotNull(message = "응모자 학번은 필수 입력 사항입니다.") + @Schema(description = "응모자 학번", example = "60000000") + String studentNumber, + + @NotNull(message = "응모자 전화번호는 필수 입력 사항입니다.") + @Schema(description = "응모자 전화번호", example = "010-0000-0000") + String phoneNumber + ) { + public CreatePairGameApplierCommand toCommand() { + return CreatePairGameApplierCommand.builder() + .name(name) + .department(department) + .studentNumber(studentNumber) + .phoneNumber(phoneNumber) + .build(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java new file mode 100644 index 00000000..375996f9 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java @@ -0,0 +1,15 @@ +package ddingdong.ddingdongBE.domain.pairgame.controller.dto.response; + +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import lombok.Builder; + +@Builder +public record PairGameApplierAmountResponse( + int amount +) { + public static PairGameApplierAmountResponse from(PairGameApplierAmountQuery query) { + return PairGameApplierAmountResponse.builder() + .amount(query.amount()) + .build(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java new file mode 100644 index 00000000..4db8c026 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java @@ -0,0 +1,52 @@ +package ddingdong.ddingdongBE.domain.pairgame.entity; + +import ddingdong.ddingdongBE.common.BaseEntity; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +import java.time.LocalDateTime; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@SQLDelete(sql = "update applier set deleted_at = CURRENT_TIMESTAMP where id=?") +@SQLRestriction("deleted_at IS NULL") +public class PairGameApplier extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 50) + private String name; + + @Column(nullable = false, length = 50) + private String department; + + @Column(nullable = false, length = 50) + private String studentNumber; + + @Column(nullable = false, length = 50) + private String phoneNumber; + + @Column(nullable = false) + private String studentFeeImageUrl; + + @Column(columnDefinition = "TIMESTAMP") + private LocalDateTime deletedAt; + + @Builder + private PairGameApplier(String name, String department, String studentNumber, String phoneNumber, String studentFeeImageUrl) { + this.name = name; + this.department = department; + this.studentNumber = studentNumber; + this.phoneNumber = phoneNumber; + this.studentFeeImageUrl = studentFeeImageUrl; + } + +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java new file mode 100644 index 00000000..1fe6b3ae --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java @@ -0,0 +1,8 @@ +package ddingdong.ddingdongBE.domain.pairgame.repository; + +import ddingdong.ddingdongBE.domain.pairgame.entity.PairGameApplier; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface PairGameRepository extends JpaRepository { +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java new file mode 100644 index 00000000..5e5628c7 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -0,0 +1,31 @@ +package ddingdong.ddingdongBE.domain.pairgame.service; + +import ddingdong.ddingdongBE.domain.pairgame.service.dto.command.CreatePairGameApplierCommand; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import ddingdong.ddingdongBE.file.service.S3FileService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FacadeUserPairGameService { + + private final PairGameService pairGameService; + private final S3FileService s3FileService; + + @Transactional + public void createApplier(CreatePairGameApplierCommand createPairGameApplierCommand, MultipartFile studentFeeImageFile) { + String key = s3FileService.uploadMultipartFile(studentFeeImageFile, LocalDateTime.now(), "pair-game"); + String studentFeeImageUrl = s3FileService.getUploadedMultipartFileUrl(key); + pairGameService.create(createPairGameApplierCommand.toEntity(studentFeeImageUrl)); + } + + public PairGameApplierAmountQuery getPairGameApplierAmount() { + return pairGameService.getPairGameApplierAmount(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java new file mode 100644 index 00000000..093e14bc --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java @@ -0,0 +1,26 @@ +package ddingdong.ddingdongBE.domain.pairgame.service; + +import ddingdong.ddingdongBE.domain.pairgame.entity.PairGameApplier; +import ddingdong.ddingdongBE.domain.pairgame.repository.PairGameRepository; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class PairGameService { + + private final PairGameRepository pairGameRepository; + + @Transactional + public PairGameApplier create(PairGameApplier pairGameApplier) { + return pairGameRepository.save(pairGameApplier); + } + + public PairGameApplierAmountQuery getPairGameApplierAmount() { + int amount = pairGameRepository.findAll().size(); + return PairGameApplierAmountQuery.of(amount); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/command/CreatePairGameApplierCommand.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/command/CreatePairGameApplierCommand.java new file mode 100644 index 00000000..8f1b88f1 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/command/CreatePairGameApplierCommand.java @@ -0,0 +1,24 @@ +package ddingdong.ddingdongBE.domain.pairgame.service.dto.command; + +import ddingdong.ddingdongBE.domain.pairgame.entity.PairGameApplier; +import lombok.Builder; +import org.springframework.web.multipart.MultipartFile; + +@Builder +public record CreatePairGameApplierCommand( + String name, + String department, + String studentNumber, + String phoneNumber, + MultipartFile studentFeeImageFile +){ + public PairGameApplier toEntity(String studentFeeImageUrl) { + return PairGameApplier.builder() + .name(name) + .department(department) + .studentNumber(studentNumber) + .phoneNumber(phoneNumber) + .studentFeeImageUrl(studentFeeImageUrl) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameApplierAmountQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameApplierAmountQuery.java new file mode 100644 index 00000000..889a96a0 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameApplierAmountQuery.java @@ -0,0 +1,13 @@ +package ddingdong.ddingdongBE.domain.pairgame.service.dto.query; + +import lombok.Builder; + +@Builder +public record PairGameApplierAmountQuery( + int amount +) { + public static PairGameApplierAmountQuery of(int amount) { + return PairGameApplierAmountQuery.builder() + .amount(amount).build(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java b/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java index 2fc42657..def99bf6 100644 --- a/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java +++ b/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java @@ -1,6 +1,9 @@ package ddingdong.ddingdongBE.file.service; import com.github.f4b6a3.uuid.UuidCreator; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; @@ -17,6 +20,8 @@ import ddingdong.ddingdongBE.file.service.dto.query.UploadedFileUrlAndNameQuery; import ddingdong.ddingdongBE.file.service.dto.query.UploadedFileUrlQuery; import ddingdong.ddingdongBE.file.service.dto.query.UploadedVideoUrlQuery; + +import java.io.IOException; import java.net.URL; import java.time.LocalDateTime; import java.util.UUID; @@ -85,6 +90,15 @@ public UploadedFileUrlQuery getUploadedFileUrl(String key) { return new UploadedFileUrlQuery(splitKey[splitKey.length - 1], originUrl, cdnUrl); } + public String getUploadedMultipartFileUrl(String key) { + if (key == null) { + return null; + } + return s3Client.utilities() + .getUrl(builder -> builder.bucket(inputBucket).key(key)) + .toExternalForm(); + } + public UploadedFileUrlAndNameQuery getUploadedFileUrlAndName(String key, String fileName) { UploadedFileUrlQuery fileUrlQuery = getUploadedFileUrl(key); return new UploadedFileUrlAndNameQuery( @@ -108,6 +122,29 @@ public UploadedVideoUrlQuery getUploadedVideoUrl(String key) { return new UploadedVideoUrlQuery(thumbnailOriginUrl, thumbnailCdnUrl, videoOriginUrl, videoCdnUrl); } + public String uploadMultipartFile(MultipartFile file, LocalDateTime dateTime, String directory) { + UUID fileName = UuidCreator.getTimeOrderedEpoch(); + String extension = extractFileExtension(file.getOriginalFilename()); + ContentType contentType = ContentType.fromExtension(extension); + + String key = generateKey(contentType, dateTime, directory, fileName); + try { + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(inputBucket) + .key(key) + .contentType(file.getContentType()) + .build(); + s3Client.putObject(putObjectRequest, + RequestBody.fromInputStream(file.getInputStream(), file.getSize())); + return key; + } catch (IOException e) { + throw new RuntimeException("파일 읽기 실패", e); + } catch (SdkException e) { + log.error("AWS Service Error : {}", e.getMessage()); + throw new AwsService(); + } + } + private GeneratePreSignedUrlRequestQuery buildPresignedUrlRequest(GeneratePreSignedUrlRequestCommand command, ContentType contentType) { UUID id = UuidCreator.getTimeOrderedEpoch(); String key = generateKey(contentType, command, id); @@ -137,6 +174,15 @@ private String generateKey(ContentType contentType, GeneratePreSignedUrlRequestC uploadFileName.toString()); } + private String generateKey(ContentType contentType, LocalDateTime dateTime, String directory, UUID uploadFileName) { + return String.format("%s/%s/%s/%s/%s", + serverProfile, + contentType.getKeyMediaType(), + formatDate(dateTime), + directory, + uploadFileName.toString()); + } + private String formatDate(LocalDateTime dateTime) { return String.format("%d-%d-%d", dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth()); } From 85792f4d95ff4ca416fe03f5d450c1d6aa75d3d4 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 15:17:28 +0900 Subject: [PATCH 02/22] =?UTF-8?q?chore:=20=EC=9C=A0=EC=A0=80=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=9D=84=20=EC=9C=84=ED=95=9C=20SecurityConfig=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 --- .../ddingdong/ddingdongBE/common/config/SecurityConfig.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/common/config/SecurityConfig.java b/src/main/java/ddingdong/ddingdongBE/common/config/SecurityConfig.java index 1d439f86..635081a5 100644 --- a/src/main/java/ddingdong/ddingdongBE/common/config/SecurityConfig.java +++ b/src/main/java/ddingdong/ddingdongBE/common/config/SecurityConfig.java @@ -51,11 +51,13 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthService authSer API_PREFIX + "/questions/**", API_PREFIX + "/feeds/**", API_PREFIX + "/forms/**", - API_PREFIX + "/file/upload-url/form-application" + API_PREFIX + "/file/upload-url/form-application", + API_PREFIX + "/pair-game/**" ) .permitAll() .requestMatchers(POST, - API_PREFIX + "/forms/{formId}/applications" + API_PREFIX + "/forms/{formId}/applications", + API_PREFIX + "/pair-game/appliers" ) .permitAll() .requestMatchers(API_PREFIX + "/internal/**") From b20c6d814507b24811a6914e521b5e5a55a477c3 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 15:18:03 +0900 Subject: [PATCH 03/22] =?UTF-8?q?chore:=20pairGameApplier=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=83=9D=EC=84=B1=20flyway=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../V51__create_pair_game_applier_table.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/resources/db/migration/V51__create_pair_game_applier_table.sql diff --git a/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql b/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql new file mode 100644 index 00000000..4c1d54ec --- /dev/null +++ b/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE pair_game_applier +( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(50) NOT NULL, + department VARCHAR(50) NOT NULL, + student_number VARCHAR(50) NOT NULL, + phone_number VARCHAR(50) NOT NULL, + student_fee_image_url VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NULL + deleted_at TIMESTAMP NULL DEFAULT NULL +); From 2811f64c7bb8de94562558604a7cb47ad721d259 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 17:17:33 +0900 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20=EC=A7=9D=20=EB=A7=9E=EC=B6=94?= =?UTF-8?q?=EA=B8=B0=20=EA=B2=8C=EC=9E=84=20=EB=A9=94=ED=83=80=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FileMetaDataRepository.java | 10 ++++++++++ .../service/FileMetaDataService.java | 2 ++ .../service/FileMetaDataServiceImpl.java | 5 +++++ .../domain/pairgame/api/UserPairGameApi.java | 7 ++++++- .../controller/UserPairGameController.java | 8 ++++++++ .../dto/response/PairGameMetaDataResponse.java | 17 +++++++++++++++++ .../service/FacadeUserPairGameService.java | 18 ++++++++++++++++++ .../dto/query/PairGameMetaDataQuery.java | 16 ++++++++++++++++ 8 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java create mode 100644 src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java diff --git a/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/repository/FileMetaDataRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/repository/FileMetaDataRepository.java index a10a419f..7c8d4175 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/repository/FileMetaDataRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/repository/FileMetaDataRepository.java @@ -23,6 +23,16 @@ List findAllByDomainTypeAndEntityIdWithFileStatus( @Param("fileStatus") FileStatus fileStatus ); + @Query(""" + select fmd from FileMetaData fmd + where fmd.domainType = :domainType + and fmd.fileStatus = :fileStatus + """) + List findAllByDomainTypeWithFileStatus( + @Param("domainType") DomainType domainType, + @Param("fileStatus") FileStatus fileStatus + ); + @Query("select fmd from FileMetaData fmd where fmd.entityId = :entityId and fmd.fileStatus = :fileStatus") List findAllByEntityIdWithFileStatus( @Param("entityId") Long entityId, diff --git a/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataService.java b/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataService.java index 187030be..a064a2d4 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataService.java @@ -16,6 +16,8 @@ public interface FileMetaDataService { List getCoupledAllByDomainTypeAndEntityId(DomainType domainType, Long entityId); + List getCoupledAllByDomainType(DomainType domainType); + List getCoupledAllByEntityId(Long entityId); List getCoupledAllByDomainTypeAndEntityIdOrderedAsc(DomainType domainType, diff --git a/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataServiceImpl.java index 7b3f8684..6754cc60 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/filemetadata/service/FileMetaDataServiceImpl.java @@ -50,6 +50,11 @@ public List getCoupledAllByDomainTypeAndEntityId(DomainType domain return fileMetaDataRepository.findAllByDomainTypeAndEntityIdWithFileStatus(domainType, entityId, COUPLED); } + @Override + public List getCoupledAllByDomainType(DomainType domainType) { + return fileMetaDataRepository.findAllByDomainTypeWithFileStatus(domainType, COUPLED); + } + @Override public List getCoupledAllByEntityId(Long entityId) { return fileMetaDataRepository.findAllByEntityIdWithFileStatus(entityId, COUPLED); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java index f42bb068..12a56903 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java @@ -2,6 +2,7 @@ import ddingdong.ddingdongBE.domain.pairgame.controller.dto.request.CreatePairGameApplierRequest; import ddingdong.ddingdongBE.domain.pairgame.controller.dto.response.PairGameApplierAmountResponse; +import ddingdong.ddingdongBE.domain.pairgame.controller.dto.response.PairGameMetaDataResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; @@ -29,5 +30,9 @@ void createPairGameApplier( @GetMapping("/pair-game/appliers/amount") PairGameApplierAmountResponse getPairGameApplierAmount(); - + @Operation(summary = "게임 메타데이터 조회 API") + @ApiResponse(responseCode = "200", description = "게임 메타데이터 조회 성공") + @ResponseStatus(HttpStatus.OK) + @GetMapping("/pair-game/metadata") + PairGameMetaDataResponse getPairGameMetaData(); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java index 81e53256..198438ad 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java @@ -3,8 +3,10 @@ import ddingdong.ddingdongBE.domain.pairgame.api.UserPairGameApi; import ddingdong.ddingdongBE.domain.pairgame.controller.dto.request.CreatePairGameApplierRequest; import ddingdong.ddingdongBE.domain.pairgame.controller.dto.response.PairGameApplierAmountResponse; +import ddingdong.ddingdongBE.domain.pairgame.controller.dto.response.PairGameMetaDataResponse; import ddingdong.ddingdongBE.domain.pairgame.service.FacadeUserPairGameService; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -25,4 +27,10 @@ public PairGameApplierAmountResponse getPairGameApplierAmount() { PairGameApplierAmountQuery query = facadeUserPairGameService.getPairGameApplierAmount(); return PairGameApplierAmountResponse.from(query); } + + @Override + public PairGameMetaDataResponse getPairGameMetaData() { + PairGameMetaDataQuery query = facadeUserPairGameService.getPairGameMetaData(); + return PairGameMetaDataResponse.from(query); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java new file mode 100644 index 00000000..18fdd24f --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java @@ -0,0 +1,17 @@ +package ddingdong.ddingdongBE.domain.pairgame.controller.dto.response; + +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery; +import lombok.Builder; + +import java.util.List; + +@Builder +public record PairGameMetaDataResponse( + List images +) { + public static PairGameMetaDataResponse from(PairGameMetaDataQuery query) { + return PairGameMetaDataResponse.builder() + .images(query.images()) + .build(); + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 5e5628c7..dfd7a628 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -1,7 +1,11 @@ package ddingdong.ddingdongBE.domain.pairgame.service; +import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType; +import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData; +import ddingdong.ddingdongBE.domain.filemetadata.service.FileMetaDataService; import ddingdong.ddingdongBE.domain.pairgame.service.dto.command.CreatePairGameApplierCommand; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery; import ddingdong.ddingdongBE.file.service.S3FileService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -9,6 +13,9 @@ import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -17,6 +24,7 @@ public class FacadeUserPairGameService { private final PairGameService pairGameService; private final S3FileService s3FileService; + private final FileMetaDataService fileMetaDataService; @Transactional public void createApplier(CreatePairGameApplierCommand createPairGameApplierCommand, MultipartFile studentFeeImageFile) { @@ -28,4 +36,14 @@ public void createApplier(CreatePairGameApplierCommand createPairGameApplierComm public PairGameApplierAmountQuery getPairGameApplierAmount() { return pairGameService.getPairGameApplierAmount(); } + + public PairGameMetaDataQuery getPairGameMetaData() { + List allClubProfileMetaData = fileMetaDataService.getCoupledAllByDomainType(DomainType.CLUB_PROFILE); + Collections.shuffle(allClubProfileMetaData); + List pairGameMetaData = allClubProfileMetaData.stream() + .limit(18) + .map(file -> s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl()) + .collect(Collectors.toList()); + return PairGameMetaDataQuery.of(pairGameMetaData); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java new file mode 100644 index 00000000..6f145c16 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java @@ -0,0 +1,16 @@ +package ddingdong.ddingdongBE.domain.pairgame.service.dto.query; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record PairGameMetaDataQuery( + List images +) { + public static PairGameMetaDataQuery of(List images) { + return PairGameMetaDataQuery.builder() + .images(images) + .build(); + } +} From 19fe204d91e1b5ceb352cfe01a1e662e887d0787 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 17:24:49 +0900 Subject: [PATCH 05/22] =?UTF-8?q?refactor:=20=ED=95=99=EC=83=9D=ED=9A=8C?= =?UTF-8?q?=EB=B9=84=20=EB=82=A9=EB=B6=80=EB=82=B4=EC=97=AD=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20cdn=20url=20=EC=A0=80=EC=9E=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pairgame/service/FacadeUserPairGameService.java | 2 +- .../ddingdongBE/file/service/S3FileService.java | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index dfd7a628..022de554 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -29,7 +29,7 @@ public class FacadeUserPairGameService { @Transactional public void createApplier(CreatePairGameApplierCommand createPairGameApplierCommand, MultipartFile studentFeeImageFile) { String key = s3FileService.uploadMultipartFile(studentFeeImageFile, LocalDateTime.now(), "pair-game"); - String studentFeeImageUrl = s3FileService.getUploadedMultipartFileUrl(key); + String studentFeeImageUrl = s3FileService.getUploadedFileUrl(key).cdnUrl(); pairGameService.create(createPairGameApplierCommand.toEntity(studentFeeImageUrl)); } diff --git a/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java b/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java index def99bf6..5bb28c66 100644 --- a/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java +++ b/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java @@ -90,15 +90,6 @@ public UploadedFileUrlQuery getUploadedFileUrl(String key) { return new UploadedFileUrlQuery(splitKey[splitKey.length - 1], originUrl, cdnUrl); } - public String getUploadedMultipartFileUrl(String key) { - if (key == null) { - return null; - } - return s3Client.utilities() - .getUrl(builder -> builder.bucket(inputBucket).key(key)) - .toExternalForm(); - } - public UploadedFileUrlAndNameQuery getUploadedFileUrlAndName(String key, String fileName) { UploadedFileUrlQuery fileUrlQuery = getUploadedFileUrl(key); return new UploadedFileUrlAndNameQuery( From 4cbf1f8d2ba5c5a1ad7ee62c029f09bab0df551b Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 17:34:49 +0900 Subject: [PATCH 06/22] =?UTF-8?q?fix:=20=ED=94=8C=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=9B=A8=EC=9D=B4=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V51__create_pair_game_applier_table.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql b/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql index 4c1d54ec..11ab0855 100644 --- a/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql +++ b/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql @@ -7,6 +7,6 @@ CREATE TABLE pair_game_applier phone_number VARCHAR(50) NOT NULL, student_fee_image_url VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NULL, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NULL + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NULL, deleted_at TIMESTAMP NULL DEFAULT NULL ); From 5006dc1ae754194420e28c003bc7ea08786cd28f Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 21:21:32 +0900 Subject: [PATCH 07/22] =?UTF-8?q?refactor:=20studentNumber=20unique=20?= =?UTF-8?q?=EC=A0=9C=EC=95=BD=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddingdongBE/domain/pairgame/entity/PairGameApplier.java | 2 +- .../db/migration/V51__create_pair_game_applier_table.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java index 4db8c026..934434d6 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java @@ -28,7 +28,7 @@ public class PairGameApplier extends BaseEntity { @Column(nullable = false, length = 50) private String department; - @Column(nullable = false, length = 50) + @Column(unique = true, nullable = false, length = 50) private String studentNumber; @Column(nullable = false, length = 50) diff --git a/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql b/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql index 11ab0855..1ad46421 100644 --- a/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql +++ b/src/main/resources/db/migration/V51__create_pair_game_applier_table.sql @@ -3,7 +3,7 @@ CREATE TABLE pair_game_applier id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, department VARCHAR(50) NOT NULL, - student_number VARCHAR(50) NOT NULL, + student_number VARCHAR(50) NOT NULL UNIQUE, phone_number VARCHAR(50) NOT NULL, student_fee_image_url VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NULL, From 817e76e07a2a656be51a9b8e7ac835099e13bf3f Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 21:59:17 +0900 Subject: [PATCH 08/22] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=EB=9E=98?= =?UTF-8?q?=EB=B9=97=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/FileException.java | 25 +++++++++++++++++++ .../pairgame/entity/PairGameApplier.java | 2 +- .../service/FacadeUserPairGameService.java | 4 +++ .../pairgame/service/PairGameService.java | 2 +- .../file/service/S3FileService.java | 3 ++- 5 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ddingdong/ddingdongBE/common/exception/FileException.java diff --git a/src/main/java/ddingdong/ddingdongBE/common/exception/FileException.java b/src/main/java/ddingdong/ddingdongBE/common/exception/FileException.java new file mode 100644 index 00000000..3e9e3106 --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/common/exception/FileException.java @@ -0,0 +1,25 @@ +package ddingdong.ddingdongBE.common.exception; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +public class FileException extends CustomException { + + private static final String UPLOADED_FILE_NOT_FOUND_MESSAGE = "업로드 할 파일이 없습니다."; + private static final String FILE_READING_ERROR_MESSAGE = "파일을 읽을 수 없습니다."; + + public FileException(String message, int errorCode) { super(message, errorCode); } + + public static final class UploadedFileNotFoundException extends FileException { + + public UploadedFileNotFoundException() { + super(UPLOADED_FILE_NOT_FOUND_MESSAGE, BAD_REQUEST.value()); + } + } + + public static final class FileReadingException extends FileException { + + public FileReadingException() { + super(FILE_READING_ERROR_MESSAGE, BAD_REQUEST.value()); + } + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java index 934434d6..ccf4f34d 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/entity/PairGameApplier.java @@ -14,7 +14,7 @@ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@SQLDelete(sql = "update applier set deleted_at = CURRENT_TIMESTAMP where id=?") +@SQLDelete(sql = "update pair_game_applier set deleted_at = CURRENT_TIMESTAMP where id=?") @SQLRestriction("deleted_at IS NULL") public class PairGameApplier extends BaseEntity { diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 022de554..20b24089 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -1,5 +1,6 @@ package ddingdong.ddingdongBE.domain.pairgame.service; +import ddingdong.ddingdongBE.common.exception.FileException.UploadedFileNotFoundException; import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType; import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData; import ddingdong.ddingdongBE.domain.filemetadata.service.FileMetaDataService; @@ -28,6 +29,9 @@ public class FacadeUserPairGameService { @Transactional public void createApplier(CreatePairGameApplierCommand createPairGameApplierCommand, MultipartFile studentFeeImageFile) { + if (studentFeeImageFile == null || studentFeeImageFile.isEmpty()) { + throw new UploadedFileNotFoundException(); + } String key = s3FileService.uploadMultipartFile(studentFeeImageFile, LocalDateTime.now(), "pair-game"); String studentFeeImageUrl = s3FileService.getUploadedFileUrl(key).cdnUrl(); pairGameService.create(createPairGameApplierCommand.toEntity(studentFeeImageUrl)); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java index 093e14bc..6ae628cc 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java @@ -20,7 +20,7 @@ public PairGameApplier create(PairGameApplier pairGameApplier) { } public PairGameApplierAmountQuery getPairGameApplierAmount() { - int amount = pairGameRepository.findAll().size(); + int amount = (int) pairGameRepository.count(); return PairGameApplierAmountQuery.of(amount); } } diff --git a/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java b/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java index 5bb28c66..588981b1 100644 --- a/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java +++ b/src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java @@ -1,6 +1,7 @@ package ddingdong.ddingdongBE.file.service; import com.github.f4b6a3.uuid.UuidCreator; +import ddingdong.ddingdongBE.common.exception.FileException.FileReadingException; import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.core.sync.RequestBody; @@ -129,7 +130,7 @@ public String uploadMultipartFile(MultipartFile file, LocalDateTime dateTime, St RequestBody.fromInputStream(file.getInputStream(), file.getSize())); return key; } catch (IOException e) { - throw new RuntimeException("파일 읽기 실패", e); + throw new FileReadingException(); } catch (SdkException e) { log.error("AWS Service Error : {}", e.getMessage()); throw new AwsService(); From f2f3c46c6682412f061159458108be779e697e69 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Fri, 30 Jan 2026 22:14:02 +0900 Subject: [PATCH 09/22] =?UTF-8?q?refactor:=20=ED=95=99=EB=B2=88=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EC=B2=B4=ED=81=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/PairGameApplierException.java | 17 +++++++++++++++++ .../pairgame/repository/PairGameRepository.java | 1 + .../service/FacadeUserPairGameService.java | 1 + .../pairgame/service/PairGameService.java | 7 +++++++ 4 files changed, 26 insertions(+) create mode 100644 src/main/java/ddingdong/ddingdongBE/common/exception/PairGameApplierException.java diff --git a/src/main/java/ddingdong/ddingdongBE/common/exception/PairGameApplierException.java b/src/main/java/ddingdong/ddingdongBE/common/exception/PairGameApplierException.java new file mode 100644 index 00000000..f026994f --- /dev/null +++ b/src/main/java/ddingdong/ddingdongBE/common/exception/PairGameApplierException.java @@ -0,0 +1,17 @@ +package ddingdong.ddingdongBE.common.exception; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +public class PairGameApplierException extends CustomException { + + private static final String DUPLICATED_PAIR_GAME_APPLIER_MESSAGE = "이미 존재하는 응모자입니다."; + public PairGameApplierException(String message, int errorCode) { + super(message, errorCode); + } + + public static final class DuplicatedPairGameApplierException extends PairGameApplierException { + public DuplicatedPairGameApplierException() { + super(DUPLICATED_PAIR_GAME_APPLIER_MESSAGE, BAD_REQUEST.value()); + } + } +} diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java index 1fe6b3ae..47d71275 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/repository/PairGameRepository.java @@ -5,4 +5,5 @@ public interface PairGameRepository extends JpaRepository { + boolean existsByStudentNumber(String studentNumber); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 20b24089..77acda4b 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -32,6 +32,7 @@ public void createApplier(CreatePairGameApplierCommand createPairGameApplierComm if (studentFeeImageFile == null || studentFeeImageFile.isEmpty()) { throw new UploadedFileNotFoundException(); } + pairGameService.validateStudentNumberUnique(createPairGameApplierCommand.studentNumber()); String key = s3FileService.uploadMultipartFile(studentFeeImageFile, LocalDateTime.now(), "pair-game"); String studentFeeImageUrl = s3FileService.getUploadedFileUrl(key).cdnUrl(); pairGameService.create(createPairGameApplierCommand.toEntity(studentFeeImageUrl)); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java index 6ae628cc..11e07728 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/PairGameService.java @@ -1,5 +1,6 @@ package ddingdong.ddingdongBE.domain.pairgame.service; +import ddingdong.ddingdongBE.common.exception.PairGameApplierException.DuplicatedPairGameApplierException; import ddingdong.ddingdongBE.domain.pairgame.entity.PairGameApplier; import ddingdong.ddingdongBE.domain.pairgame.repository.PairGameRepository; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; @@ -23,4 +24,10 @@ public PairGameApplierAmountQuery getPairGameApplierAmount() { int amount = (int) pairGameRepository.count(); return PairGameApplierAmountQuery.of(amount); } + + public void validateStudentNumberUnique(String studentNumber) { + if (pairGameRepository.existsByStudentNumber(studentNumber)) { + throw new DuplicatedPairGameApplierException(); + } + } } From 797aaed62496a8e19c04e88eb6e44fc8da8e0d39 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Sun, 1 Feb 2026 02:00:38 +0900 Subject: [PATCH 10/22] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=BD=94=EB=A9=98=ED=8A=B8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/pairgame/service/FacadeUserPairGameService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 77acda4b..14a82541 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -34,8 +34,7 @@ public void createApplier(CreatePairGameApplierCommand createPairGameApplierComm } pairGameService.validateStudentNumberUnique(createPairGameApplierCommand.studentNumber()); String key = s3FileService.uploadMultipartFile(studentFeeImageFile, LocalDateTime.now(), "pair-game"); - String studentFeeImageUrl = s3FileService.getUploadedFileUrl(key).cdnUrl(); - pairGameService.create(createPairGameApplierCommand.toEntity(studentFeeImageUrl)); + pairGameService.create(createPairGameApplierCommand.toEntity(key)); } public PairGameApplierAmountQuery getPairGameApplierAmount() { From 62fb8b366bb208cadf16d5b340a16f7b4b53b063 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Sun, 1 Feb 2026 02:27:40 +0900 Subject: [PATCH 11/22] =?UTF-8?q?refactor:=20toCommand=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=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 --- .../domain/pairgame/controller/UserPairGameController.java | 2 +- .../controller/dto/request/CreatePairGameApplierRequest.java | 4 +++- .../domain/pairgame/service/FacadeUserPairGameService.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java index 198438ad..9c0e4d55 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/UserPairGameController.java @@ -19,7 +19,7 @@ public class UserPairGameController implements UserPairGameApi { @Override public void createPairGameApplier(CreatePairGameApplierRequest createPairGameApplierRequest, MultipartFile studentFeeImageFile) { - facadeUserPairGameService.createApplier(createPairGameApplierRequest.toCommand(), studentFeeImageFile); + facadeUserPairGameService.createPairGameApplier(createPairGameApplierRequest.toCommand(studentFeeImageFile)); } @Override diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java index 6375c807..3a1a8c01 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/request/CreatePairGameApplierRequest.java @@ -3,6 +3,7 @@ import ddingdong.ddingdongBE.domain.pairgame.service.dto.command.CreatePairGameApplierCommand; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import org.springframework.web.multipart.MultipartFile; public record CreatePairGameApplierRequest ( @@ -22,12 +23,13 @@ public record CreatePairGameApplierRequest ( @Schema(description = "응모자 전화번호", example = "010-0000-0000") String phoneNumber ) { - public CreatePairGameApplierCommand toCommand() { + public CreatePairGameApplierCommand toCommand(MultipartFile studentFeeImageFile) { return CreatePairGameApplierCommand.builder() .name(name) .department(department) .studentNumber(studentNumber) .phoneNumber(phoneNumber) + .studentFeeImageFile(studentFeeImageFile) .build(); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 14a82541..48fc3420 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -28,7 +28,8 @@ public class FacadeUserPairGameService { private final FileMetaDataService fileMetaDataService; @Transactional - public void createApplier(CreatePairGameApplierCommand createPairGameApplierCommand, MultipartFile studentFeeImageFile) { + public void createPairGameApplier(CreatePairGameApplierCommand createPairGameApplierCommand) { + MultipartFile studentFeeImageFile = createPairGameApplierCommand.studentFeeImageFile(); if (studentFeeImageFile == null || studentFeeImageFile.isEmpty()) { throw new UploadedFileNotFoundException(); } From 05f4df81ebe7d60e8338e04fdb2cf1e0ff344542 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Sun, 1 Feb 2026 20:03:48 +0900 Subject: [PATCH 12/22] =?UTF-8?q?test:=20FacadeUserPairGameServiceTest=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FacadeUserPairGameServiceTest.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java diff --git a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java new file mode 100644 index 00000000..a0056758 --- /dev/null +++ b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java @@ -0,0 +1,107 @@ +package ddingdong.ddingdongBE.domain.pairgame.service; + +import ddingdong.ddingdongBE.common.exception.FileException.UploadedFileNotFoundException; +import ddingdong.ddingdongBE.common.support.TestContainerSupport; +import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType; +import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData; +import ddingdong.ddingdongBE.domain.filemetadata.entity.FileStatus; +import ddingdong.ddingdongBE.domain.filemetadata.repository.FileMetaDataRepository; +import ddingdong.ddingdongBE.domain.pairgame.entity.PairGameApplier; +import ddingdong.ddingdongBE.domain.pairgame.repository.PairGameRepository; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.command.CreatePairGameApplierCommand; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery; +import ddingdong.ddingdongBE.file.service.S3FileService; +import ddingdong.ddingdongBE.file.service.dto.query.UploadedFileUrlQuery; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.mock.web.MockMultipartFile; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; + +@SpringBootTest +class FacadeUserPairGameServiceTest extends TestContainerSupport { + + @Autowired + private FacadeUserPairGameService facadeUserPairGameService; + + @Autowired + private PairGameRepository pairGameRepository; + + @Autowired + private FileMetaDataRepository fileMetaDataRepository; + + @MockitoBean + private S3FileService s3FileService; + + @Test + @DisplayName("유저: 학번 중복 검사 후 응모자를 생성할 수 있다.") + void createPairGameApplier_Success() { + // given + MockMultipartFile file = new MockMultipartFile("file", "test.jpg", "image/jpeg", "content".getBytes()); + CreatePairGameApplierCommand command = new CreatePairGameApplierCommand( + "김띵동", "융합소프트웨어학부", "60112233", "010-1234-5678", file + ); + given(s3FileService.uploadMultipartFile(eq(file), any(LocalDateTime.class), eq("pair-game"))) + .willReturn("s3-uploaded-file-key"); + + // when + facadeUserPairGameService.createPairGameApplier(command); + + // then + List appliers = pairGameRepository.findAll(); + assertThat(appliers).hasSize(1); + + PairGameApplier savedApplier = appliers.get(0); + assertThat(savedApplier.getName()).isEqualTo("김띵동"); + assertThat(savedApplier.getStudentNumber()).isEqualTo("60112233"); + } + + @Test + @DisplayName("유저: 응모자 생성 요청에 파일이 없으면 예외가 발생한다.") + void createPairGameApplier_Fail_NoFile() { + // given + CreatePairGameApplierCommand command = new CreatePairGameApplierCommand( + "김띵동", "융합소프트웨어학부", "60112233", "010-1234-5678", null + ); + + // when & then + assertThatThrownBy(() -> facadeUserPairGameService.createPairGameApplier(command)) + .isInstanceOf(UploadedFileNotFoundException.class); + } + + @Test + @DisplayName("유저: 동아리 로고 이미지를 랜덤으로 18개 조회할 수 있다.") + void getPairGameMetaData_Integration() { + // given + List metaDataList = IntStream.range(0, 20) + .mapToObj(i -> FileMetaData.builder() + .domainType(DomainType.CLUB_PROFILE) + .fileKey("key" + i) + .fileName("test.jpg") + .fileStatus(FileStatus.COUPLED) + .build()) + .toList(); + fileMetaDataRepository.saveAll(metaDataList); + + given(s3FileService.getUploadedFileUrl(any())) + .willReturn(new UploadedFileUrlQuery("id", "originUrl", "cdnUrl")); + + // when + PairGameMetaDataQuery result = facadeUserPairGameService.getPairGameMetaData(); + + // then + assertThat(result.images()).hasSize(18); + assertThat(result.images().get(0)).isEqualTo("cdnUrl"); + } +} \ No newline at end of file From ef8b07acb62d9a9262d4332f420294569896b860 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Sun, 1 Feb 2026 22:54:18 +0900 Subject: [PATCH 13/22] =?UTF-8?q?test:=20FacadeUserPairGameServiceTest=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/pairgame/service/FacadeUserPairGameServiceTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java index a0056758..47cf059f 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java @@ -21,6 +21,7 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.UUID; import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThat; @@ -86,6 +87,7 @@ void getPairGameMetaData_Integration() { // given List metaDataList = IntStream.range(0, 20) .mapToObj(i -> FileMetaData.builder() + .id(UUID.randomUUID()) .domainType(DomainType.CLUB_PROFILE) .fileKey("key" + i) .fileName("test.jpg") From bca624fb7c25a1fb8f03e6d71c4f57d73af02988 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Sun, 1 Feb 2026 22:55:49 +0900 Subject: [PATCH 14/22] =?UTF-8?q?chore:=20=EB=8F=84=EC=BB=A4=20=EC=B5=9C?= =?UTF-8?q?=EC=8B=A0=20=EB=B2=84=EC=A0=84=20=EC=9D=B4=EC=8A=88=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=EC=9D=84=20=EC=9C=84=ED=95=9C=20properties=20?= =?UTF-8?q?=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 Testcontainer API 버전 명시 --- src/test/resources/docker-java.properties | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/test/resources/docker-java.properties diff --git a/src/test/resources/docker-java.properties b/src/test/resources/docker-java.properties new file mode 100644 index 00000000..e1af86b4 --- /dev/null +++ b/src/test/resources/docker-java.properties @@ -0,0 +1 @@ +api.version=1.44 \ No newline at end of file From 6f77f8285afffeedb4817151b64e1fe634aa0bb7 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Mon, 2 Feb 2026 03:11:28 +0900 Subject: [PATCH 15/22] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EB=A9=94=ED=83=80=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=9D=91=EB=8B=B5=EC=97=90=20clubName=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/club/service/ClubService.java | 2 ++ .../club/service/GeneralClubService.java | 5 ++++ .../response/PairGameMetaDataResponse.java | 26 +++++++++++++++++-- .../service/FacadeUserPairGameService.java | 23 ++++++++++++---- .../dto/query/PairGameMetaDataQuery.java | 18 ++++++++++--- .../FacadeUserPairGameServiceTest.java | 24 ++++++++++++++--- 6 files changed, 85 insertions(+), 13 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java b/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java index 0467014c..9636b654 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java @@ -14,6 +14,8 @@ public interface ClubService { List findAll(); + List getAllByIds(List clubIds); + void update(Club club, Club updatedClub); void delete(Long clubId); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java b/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java index 3a75f8f4..09b63258 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java @@ -56,6 +56,11 @@ public List findAll() { return clubRepository.findAll(); } + @Override + public List getAllByIds(List clubIds) { + return clubRepository.findAllById(clubIds); + } + @Override @Transactional public void update(Club club, Club updatedClub) { diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java index 18fdd24f..73707cce 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java @@ -1,17 +1,39 @@ package ddingdong.ddingdongBE.domain.pairgame.controller.dto.response; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery.PairGameClubAndImageQuery; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import java.util.List; @Builder public record PairGameMetaDataResponse( - List images + @ArraySchema(schema = @Schema(implementation = PairGameMetaDataResponse.PairGameClubAndImageResponse.class)) + List metaData ) { + @Builder + public record PairGameClubAndImageResponse( + @Schema(description = "동아리 이름", example = "COW") + String clubName, + @Schema(description = "동아리 로고 이미지 CDN URL", example = "https://cdn.com") + String imageUrl + ) { + public static PairGameClubAndImageResponse from(PairGameClubAndImageQuery query) { + return PairGameClubAndImageResponse.builder() + .clubName(query.clubName()) + .imageUrl(query.imageUrl()) + .build(); + } + } public static PairGameMetaDataResponse from(PairGameMetaDataQuery query) { + List responses = query.metaData().stream() + .map(PairGameClubAndImageResponse::from) + .toList(); + return PairGameMetaDataResponse.builder() - .images(query.images()) + .metaData(responses) .build(); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 48fc3420..bb5b414c 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -1,12 +1,15 @@ package ddingdong.ddingdongBE.domain.pairgame.service; import ddingdong.ddingdongBE.common.exception.FileException.UploadedFileNotFoundException; +import ddingdong.ddingdongBE.domain.club.entity.Club; +import ddingdong.ddingdongBE.domain.club.service.ClubService; import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType; import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData; import ddingdong.ddingdongBE.domain.filemetadata.service.FileMetaDataService; import ddingdong.ddingdongBE.domain.pairgame.service.dto.command.CreatePairGameApplierCommand; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery; +import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameMetaDataQuery.PairGameClubAndImageQuery; import ddingdong.ddingdongBE.file.service.S3FileService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,6 +19,7 @@ import java.time.LocalDateTime; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Service @@ -26,6 +30,7 @@ public class FacadeUserPairGameService { private final PairGameService pairGameService; private final S3FileService s3FileService; private final FileMetaDataService fileMetaDataService; + private final ClubService clubService; @Transactional public void createPairGameApplier(CreatePairGameApplierCommand createPairGameApplierCommand) { @@ -45,10 +50,18 @@ public PairGameApplierAmountQuery getPairGameApplierAmount() { public PairGameMetaDataQuery getPairGameMetaData() { List allClubProfileMetaData = fileMetaDataService.getCoupledAllByDomainType(DomainType.CLUB_PROFILE); Collections.shuffle(allClubProfileMetaData); - List pairGameMetaData = allClubProfileMetaData.stream() - .limit(18) - .map(file -> s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl()) - .collect(Collectors.toList()); - return PairGameMetaDataQuery.of(pairGameMetaData); + List selectedMetaData = allClubProfileMetaData.stream().limit(18).toList(); + + List clubIds = selectedMetaData.stream().map(FileMetaData::getEntityId).toList(); + List clubs = clubService.getAllByIds(clubIds); + Map clubNameMap = clubs.stream().collect(Collectors.toMap(Club::getId, Club::getName)); + + List pairGameMetaData = selectedMetaData.stream().map(file -> { + String name = clubNameMap.get(file.getEntityId()); + String imageUrl = s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl(); + return PairGameClubAndImageQuery.builder().clubName(name).imageUrl(imageUrl).build(); + }).toList(); + + return PairGameMetaDataQuery.builder().metaData(pairGameMetaData).build(); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java index 6f145c16..7cce4f4c 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java @@ -6,11 +6,23 @@ @Builder public record PairGameMetaDataQuery( - List images + List metaData ) { - public static PairGameMetaDataQuery of(List images) { + @Builder + public record PairGameClubAndImageQuery( + String clubName, + String imageUrl + ) { + public static PairGameClubAndImageQuery of(String clubName, String imageUrl) { + return PairGameClubAndImageQuery.builder() + .clubName(clubName) + .imageUrl(imageUrl) + .build(); + } + } + public static PairGameMetaDataQuery of(List metaData) { return PairGameMetaDataQuery.builder() - .images(images) + .metaData(metaData) .build(); } } diff --git a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java index 47cf059f..b9574379 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java @@ -2,6 +2,8 @@ import ddingdong.ddingdongBE.common.exception.FileException.UploadedFileNotFoundException; import ddingdong.ddingdongBE.common.support.TestContainerSupport; +import ddingdong.ddingdongBE.domain.club.entity.Club; +import ddingdong.ddingdongBE.domain.club.repository.ClubRepository; import ddingdong.ddingdongBE.domain.filemetadata.entity.DomainType; import ddingdong.ddingdongBE.domain.filemetadata.entity.FileMetaData; import ddingdong.ddingdongBE.domain.filemetadata.entity.FileStatus; @@ -42,6 +44,9 @@ class FacadeUserPairGameServiceTest extends TestContainerSupport { @Autowired private FileMetaDataRepository fileMetaDataRepository; + @Autowired + private ClubRepository clubRepository; + @MockitoBean private S3FileService s3FileService; @@ -82,13 +87,22 @@ void createPairGameApplier_Fail_NoFile() { } @Test - @DisplayName("유저: 동아리 로고 이미지를 랜덤으로 18개 조회할 수 있다.") + @DisplayName("유저: 동아리 로고 이미지 URL과 이름을 랜덤으로 18개 조회할 수 있다.") void getPairGameMetaData_Integration() { // given + List clubs = IntStream.range(0, 20) + .mapToObj(i -> Club.builder() + .name("동아리" + i) + .leader("회장" + i) + .build()) + .toList(); + clubRepository.saveAll(clubs); + List metaDataList = IntStream.range(0, 20) .mapToObj(i -> FileMetaData.builder() .id(UUID.randomUUID()) .domainType(DomainType.CLUB_PROFILE) + .entityId(clubs.get(i).getId()) .fileKey("key" + i) .fileName("test.jpg") .fileStatus(FileStatus.COUPLED) @@ -103,7 +117,11 @@ void getPairGameMetaData_Integration() { PairGameMetaDataQuery result = facadeUserPairGameService.getPairGameMetaData(); // then - assertThat(result.images()).hasSize(18); - assertThat(result.images().get(0)).isEqualTo("cdnUrl"); + assertThat(result.metaData()).hasSize(18); + String firstClubName = result.metaData().get(0).clubName(); + String firstImageUrl = result.metaData().get(0).imageUrl(); + + assertThat(firstClubName).startsWith("동아리"); + assertThat(firstImageUrl).isEqualTo("cdnUrl"); } } \ No newline at end of file From 51e21ba2eddfc7b7f476e0c772cd71286841f447 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Mon, 2 Feb 2026 03:15:03 +0900 Subject: [PATCH 16/22] =?UTF-8?q?refactor:=20ClubService=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 팀 컨벤션에 맞추어 find -> get 수정 --- .../ddingdongBE/domain/club/service/ClubService.java | 4 ++-- .../domain/club/service/FacadeAdminClubServiceImpl.java | 2 +- .../domain/club/service/FacadeUserClubServiceImpl.java | 2 +- .../ddingdongBE/domain/club/service/GeneralClubService.java | 4 ++-- .../dto/response/PairGameApplierAmountResponse.java | 2 ++ .../domain/club/service/FacadeAdminClubServiceImplTest.java | 2 +- .../domain/club/service/FacadeUserClubServiceImplTest.java | 6 +++--- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java b/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java index 9636b654..c08fc4be 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/service/ClubService.java @@ -12,7 +12,7 @@ public interface ClubService { Club getByUserId(Long userId); - List findAll(); + List getAll(); List getAllByIds(List clubIds); @@ -22,5 +22,5 @@ public interface ClubService { Club getByUserIdWithFetch(Long userId); - List findAllClubListInfo(); + List getAllClubListInfo(); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImpl.java index 3fab31e2..4b4bb348 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImpl.java @@ -38,7 +38,7 @@ public Long create(CreateClubCommand command) { @Override public List findAll() { - return clubService.findAll().stream() + return clubService.getAll().stream() .map(club -> { UploadedFileUrlAndNameQuery clubProfileImageQuery = fileMetaDataService.getCoupledAllByDomainTypeAndEntityId( DomainType.CLUB_PROFILE, club.getId()) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImpl.java index fef08344..16e86d14 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImpl.java @@ -34,7 +34,7 @@ public class FacadeUserClubServiceImpl implements FacadeUserClubService { @Override public List findAllWithRecruitTimeCheckPoint(LocalDate now) { - List userClubListInfos = clubService.findAllClubListInfo(); + List userClubListInfos = clubService.getAllClubListInfo(); return userClubListInfos.stream() .map(info -> UserClubListQuery.of(info, checkRecruit(now, info.getStart(), info.getEnd()).getText())) .toList(); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java b/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java index 09b63258..37b418b3 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/service/GeneralClubService.java @@ -47,12 +47,12 @@ public Club getByUserIdWithFetch(Long userId) { } @Override - public List findAllClubListInfo() { + public List getAllClubListInfo() { return clubRepository.findAllClubListInfo(LocalDate.now()); } @Override - public List findAll() { + public List getAll() { return clubRepository.findAll(); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java index 375996f9..de7c8de5 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameApplierAmountResponse.java @@ -1,10 +1,12 @@ package ddingdong.ddingdongBE.domain.pairgame.controller.dto.response; import ddingdong.ddingdongBE.domain.pairgame.service.dto.query.PairGameApplierAmountQuery; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; @Builder public record PairGameApplierAmountResponse( + @Schema(description = "총 응모자 수", example = "200") int amount ) { public static PairGameApplierAmountResponse from(PairGameApplierAmountQuery query) { diff --git a/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImplTest.java index 9f637213..6bae3576 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImplTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeAdminClubServiceImplTest.java @@ -66,7 +66,7 @@ void create() { @DisplayName("어드민: 동아리 목록 조회") @Test - void findAll() { + void getAll() { // given List clubs = new ArrayList<>(); diff --git a/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImplTest.java b/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImplTest.java index a3acf214..4e7e5607 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImplTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/club/service/FacadeUserClubServiceImplTest.java @@ -49,7 +49,7 @@ void tearDown() { @DisplayName("유저: 동아리 목록 조회 - 모집 전") @Test - void findAllWithRecruitTimeCheckPointBeforeRecruit() { + void getAllWithRecruitTimeCheckPointBeforeRecruit() { // given LocalDate startRecruitingDate = LocalDate.of(2025, 9, 1); LocalDate endRecruitingDate = LocalDate.of(2025, 12, 31); @@ -75,7 +75,7 @@ void findAllWithRecruitTimeCheckPointBeforeRecruit() { @DisplayName("유저: 동아리 목록 조회 - 모집 가능") @Test - void findAllWithRecruitTimeCheckPointRecruiting() { + void getAllWithRecruitTimeCheckPointRecruiting() { // given LocalDate startRecruitingDate = LocalDate.of(2025, 9, 1); LocalDate endRecruitingDate = LocalDate.of(2025, 12, 31); @@ -101,7 +101,7 @@ void findAllWithRecruitTimeCheckPointRecruiting() { @DisplayName("유저: 동아리 목록 조회 - 모집 마감") @Test - void findAllWithRecruitTimeCheckPointEndRecruit() { + void getAllWithRecruitTimeCheckPointEndRecruit() { // given LocalDate startRecruitingDate = LocalDate.of(2025, 9, 1); LocalDate endRecruitingDate = LocalDate.of(2025, 12, 1); From c6945aaacfc0f663097467894acf940eee6cef59 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Tue, 3 Feb 2026 19:06:43 +0900 Subject: [PATCH 17/22] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=96=B8=ED=8A=B8=20=EC=9A=94=EC=B2=AD=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 게임 메타데이터 조회 API 응답에 category 추가 --- .../dto/response/PairGameMetaDataResponse.java | 3 +++ .../service/FacadeUserPairGameService.java | 15 +++++++++------ .../service/dto/query/PairGameMetaDataQuery.java | 4 +++- .../service/FacadeUserPairGameServiceTest.java | 5 ++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java index 73707cce..0bdfda8e 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/controller/dto/response/PairGameMetaDataResponse.java @@ -17,12 +17,15 @@ public record PairGameMetaDataResponse( public record PairGameClubAndImageResponse( @Schema(description = "동아리 이름", example = "COW") String clubName, + @Schema(description = "동아리 분과", example = "사회연구") + String category, @Schema(description = "동아리 로고 이미지 CDN URL", example = "https://cdn.com") String imageUrl ) { public static PairGameClubAndImageResponse from(PairGameClubAndImageQuery query) { return PairGameClubAndImageResponse.builder() .clubName(query.clubName()) + .category(query.category()) .imageUrl(query.imageUrl()) .build(); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index bb5b414c..55b2c50a 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -54,14 +54,17 @@ public PairGameMetaDataQuery getPairGameMetaData() { List clubIds = selectedMetaData.stream().map(FileMetaData::getEntityId).toList(); List clubs = clubService.getAllByIds(clubIds); - Map clubNameMap = clubs.stream().collect(Collectors.toMap(Club::getId, Club::getName)); + + Map clubMap = clubs.stream().collect(Collectors.toMap(Club::getId, club -> club)); List pairGameMetaData = selectedMetaData.stream().map(file -> { - String name = clubNameMap.get(file.getEntityId()); - String imageUrl = s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl(); - return PairGameClubAndImageQuery.builder().clubName(name).imageUrl(imageUrl).build(); + Club club = clubMap.get(file.getEntityId()); + return PairGameClubAndImageQuery.of( + club.getName(), + club.getCategory(), + s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl() + ); }).toList(); - - return PairGameMetaDataQuery.builder().metaData(pairGameMetaData).build(); + return PairGameMetaDataQuery.of(pairGameMetaData); } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java index 7cce4f4c..e4bd8a8a 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/dto/query/PairGameMetaDataQuery.java @@ -11,11 +11,13 @@ public record PairGameMetaDataQuery( @Builder public record PairGameClubAndImageQuery( String clubName, + String category, String imageUrl ) { - public static PairGameClubAndImageQuery of(String clubName, String imageUrl) { + public static PairGameClubAndImageQuery of(String clubName, String category, String imageUrl) { return PairGameClubAndImageQuery.builder() .clubName(clubName) + .category(category) .imageUrl(imageUrl) .build(); } diff --git a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java index b9574379..2bbce4e0 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java @@ -93,6 +93,7 @@ void getPairGameMetaData_Integration() { List clubs = IntStream.range(0, 20) .mapToObj(i -> Club.builder() .name("동아리" + i) + .category("분과" + i) .leader("회장" + i) .build()) .toList(); @@ -119,9 +120,11 @@ void getPairGameMetaData_Integration() { // then assertThat(result.metaData()).hasSize(18); String firstClubName = result.metaData().get(0).clubName(); + String firstClubCategory = result.metaData().get(0).category(); String firstImageUrl = result.metaData().get(0).imageUrl(); - assertThat(firstClubName).startsWith("동아리"); + assertThat(firstClubName).isEqualTo("동아리0"); + assertThat(firstClubCategory).isEqualTo("분과0"); assertThat(firstImageUrl).isEqualTo("cdnUrl"); } } \ No newline at end of file From 3478507ee31e0a8f18a65604e937ebf3f39b3a06 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Tue, 3 Feb 2026 20:39:09 +0900 Subject: [PATCH 18/22] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EB=B0=A9=EC=A7=80=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20=EB=8A=90=EC=8A=A8=ED=95=98=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pairgame/service/FacadeUserPairGameServiceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java index 2bbce4e0..1fa822e6 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameServiceTest.java @@ -87,7 +87,7 @@ void createPairGameApplier_Fail_NoFile() { } @Test - @DisplayName("유저: 동아리 로고 이미지 URL과 이름을 랜덤으로 18개 조회할 수 있다.") + @DisplayName("유저: 동아리 로고 이미지 URL, 분과, 이름을 랜덤으로 18개 조회할 수 있다.") void getPairGameMetaData_Integration() { // given List clubs = IntStream.range(0, 20) @@ -123,8 +123,8 @@ void getPairGameMetaData_Integration() { String firstClubCategory = result.metaData().get(0).category(); String firstImageUrl = result.metaData().get(0).imageUrl(); - assertThat(firstClubName).isEqualTo("동아리0"); - assertThat(firstClubCategory).isEqualTo("분과0"); + assertThat(firstClubName).startsWith("동아리"); + assertThat(firstClubCategory).startsWith("분과"); assertThat(firstImageUrl).isEqualTo("cdnUrl"); } } \ No newline at end of file From 1f9673be7c811930d8b2bd1943dfbdc5f4d3602c Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Wed, 4 Feb 2026 23:52:17 +0900 Subject: [PATCH 19/22] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/FacadeUserPairGameService.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 55b2c50a..2eacb3e9 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -50,21 +50,27 @@ public PairGameApplierAmountQuery getPairGameApplierAmount() { public PairGameMetaDataQuery getPairGameMetaData() { List allClubProfileMetaData = fileMetaDataService.getCoupledAllByDomainType(DomainType.CLUB_PROFILE); Collections.shuffle(allClubProfileMetaData); - List selectedMetaData = allClubProfileMetaData.stream().limit(18).toList(); + List selectedFiles = allClubProfileMetaData.stream().limit(18).toList(); - List clubIds = selectedMetaData.stream().map(FileMetaData::getEntityId).toList(); + List clubIds = selectedFiles.stream().map(FileMetaData::getEntityId).toList(); List clubs = clubService.getAllByIds(clubIds); Map clubMap = clubs.stream().collect(Collectors.toMap(Club::getId, club -> club)); - List pairGameMetaData = selectedMetaData.stream().map(file -> { - Club club = clubMap.get(file.getEntityId()); - return PairGameClubAndImageQuery.of( - club.getName(), - club.getCategory(), - s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl() - ); - }).toList(); + List pairGameMetaData = assembleMetaData(selectedFiles, clubMap); + return PairGameMetaDataQuery.of(pairGameMetaData); } + + private List assembleMetaData(List files, Map clubMap) { + return files.stream() + .map(file -> { + Club club = clubMap.get(file.getEntityId()); + String name = club.getName(); + String category = club.getCategory(); + String imageUrl = s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl(); + return PairGameClubAndImageQuery.of(name, category, imageUrl); + }) + .toList(); + } } From cc3b983098dac0f262d732e5b6473b09113db418 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Wed, 4 Feb 2026 23:54:46 +0900 Subject: [PATCH 20/22] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/FacadeUserPairGameService.java | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java index 2eacb3e9..e3b9a5d2 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/service/FacadeUserPairGameService.java @@ -50,27 +50,21 @@ public PairGameApplierAmountQuery getPairGameApplierAmount() { public PairGameMetaDataQuery getPairGameMetaData() { List allClubProfileMetaData = fileMetaDataService.getCoupledAllByDomainType(DomainType.CLUB_PROFILE); Collections.shuffle(allClubProfileMetaData); - List selectedFiles = allClubProfileMetaData.stream().limit(18).toList(); + List selectedMetaData = allClubProfileMetaData.stream().limit(18).toList(); - List clubIds = selectedFiles.stream().map(FileMetaData::getEntityId).toList(); + List clubIds = selectedMetaData.stream().map(FileMetaData::getEntityId).toList(); List clubs = clubService.getAllByIds(clubIds); Map clubMap = clubs.stream().collect(Collectors.toMap(Club::getId, club -> club)); - List pairGameMetaData = assembleMetaData(selectedFiles, clubMap); - + List pairGameMetaData = selectedMetaData.stream().map(file -> { + Club club = clubMap.get(file.getEntityId()); + return PairGameClubAndImageQuery.of( + club.getName(), + club.getCategory(), + s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl() + ); + }).toList(); return PairGameMetaDataQuery.of(pairGameMetaData); } - - private List assembleMetaData(List files, Map clubMap) { - return files.stream() - .map(file -> { - Club club = clubMap.get(file.getEntityId()); - String name = club.getName(); - String category = club.getCategory(); - String imageUrl = s3FileService.getUploadedFileUrl(file.getFileKey()).cdnUrl(); - return PairGameClubAndImageQuery.of(name, category, imageUrl); - }) - .toList(); - } } From b315567520737235ed5b5a02deac119b2da2e9b8 Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Thu, 5 Feb 2026 00:30:19 +0900 Subject: [PATCH 21/22] =?UTF-8?q?fix:=20Form-data=20request=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddingdongBE/domain/pairgame/api/UserPairGameApi.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java index 12a56903..3ef74349 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/pairgame/api/UserPairGameApi.java @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -18,7 +19,9 @@ public interface UserPairGameApi { @Operation(summary = "응모자 생성 API") @ApiResponse(responseCode = "201", description = "응모자 생성 성공") @ResponseStatus(HttpStatus.CREATED) - @PostMapping("/pair-game/appliers") + @PostMapping( + value = "/pair-game/appliers", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE) void createPairGameApplier( @Valid @RequestPart("request") CreatePairGameApplierRequest request, @RequestPart("file") MultipartFile file From 2ce8bc70eb687f8ffd9f481db7c988c22d983b2b Mon Sep 17 00:00:00 2001 From: Seooooo24 Date: Thu, 5 Feb 2026 00:42:48 +0900 Subject: [PATCH 22/22] =?UTF-8?q?refactor:=20=EC=BB=AC=EB=9F=BC=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/V52__alter_table_pair_game_applier_image_key.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/resources/db/migration/V52__alter_table_pair_game_applier_image_key.sql diff --git a/src/main/resources/db/migration/V52__alter_table_pair_game_applier_image_key.sql b/src/main/resources/db/migration/V52__alter_table_pair_game_applier_image_key.sql new file mode 100644 index 00000000..7f77bda5 --- /dev/null +++ b/src/main/resources/db/migration/V52__alter_table_pair_game_applier_image_key.sql @@ -0,0 +1,2 @@ +ALTER TABLE pair_game_applier + CHANGE COLUMN student_fee_image_url student_fee_image_key VARCHAR(255) NOT NULL; \ No newline at end of file