From e38f8a4d7fd5ff14e5024382ed78d4ca52191bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:25:56 +0900 Subject: [PATCH 01/23] =?UTF-8?q?feat:=20=EC=A3=BC=EB=AC=B8=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=20=EC=83=81=EC=A0=90=20=EC=A0=84=ED=99=98(=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B0=80=EC=9E=85)?= =?UTF-8?q?=20POST=20API=20=ED=8B=80=EC=A0=9C=EC=9E=91=20(#2036)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 빈 java 파일들 생성 * feat: ShopToOrderable Post를 스켈레톤코드 작성 * refact: DTO, Model 필드 생성 (임시) * feat: Controller 수정 * feat: Service, Repository 단 * refact: 컨벤션 수정 * refact: 예외코드 추가 및 수정, 엔드포인트 경로 변경, * refact: Entity 리펙토링 * refact: 미사용 import 제거 * refact: {shopId} 추가 및 개행 제거 --- .../controller/ShopToOrderableApi.java | 37 ++++++ .../controller/ShopToOrderableController.java | 36 ++++++ .../dto/ShopToOrderableRequest.java | 67 ++++++++++ .../model/ShopToOrderable.java | 121 ++++++++++++++++++ .../model/ShopToOrderableRequestStatus.java | 5 + .../repository/ShopToOrderableRepository.java | 16 +++ .../service/ShopToOrderableService.java | 54 ++++++++ .../koin/global/code/ApiResponseCode.java | 4 +- 8 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java new file mode 100644 index 000000000..952203c53 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java @@ -0,0 +1,37 @@ +package in.koreatech.koin.domain.shoptoOrderable.controller; + +import static in.koreatech.koin.domain.user.model.UserType.OWNER; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; +import in.koreatech.koin.global.auth.Auth; +import in.koreatech.koin.global.code.ApiResponseCode; +import in.koreatech.koin.global.code.ApiResponseCodes; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@Tag(name = "(Normal) Shop To Orderable Request: 주문 서비스 가입 요청", description = "사장님이 주문 서비스 가입을 요청하기위한 API") +public interface ShopToOrderableApi { + + @ApiResponseCodes({ + ApiResponseCode.OK, + ApiResponseCode.ILLEGAL_ARGUMENT, + ApiResponseCode.FORBIDDEN_USER_TYPE, + ApiResponseCode.NOT_FOUND_USER, + ApiResponseCode.NOT_FOUND_SHOP, + }) + @Operation(summary = "사장님 주문 서비스 가입 요청") + @SecurityRequirement(name = "Jwt Authentication") + @PostMapping("/owner/shops/{shopId}/orderable-requests") + ResponseEntity createOrderableRequest( + @Auth(permit = {OWNER}) Integer ownerId, + @PathVariable Integer shopId, + @RequestBody @Valid ShopToOrderableRequest request + ); +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java new file mode 100644 index 000000000..a7fe27d08 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java @@ -0,0 +1,36 @@ +package in.koreatech.koin.domain.shoptoOrderable.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; +import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService; +import lombok.RequiredArgsConstructor; + +import static in.koreatech.koin.domain.user.model.UserType.OWNER; + +import org.springframework.web.bind.annotation.RequestBody; + +import in.koreatech.koin.global.auth.Auth; +import jakarta.validation.Valid; + +//Todo: ShopToOrderable 이라는 명칭은 임시임 추후 변경 +@RestController +@RequiredArgsConstructor +public class ShopToOrderableController implements ShopToOrderableApi { + + private final ShopToOrderableService shopToOrderableService; + + @PostMapping("/owner/shops/{shopId}/orderable-requests") + public ResponseEntity createOrderableRequest( + @Auth(permit = {OWNER}) Integer ownerId, + @PathVariable Integer shopId, + @RequestBody @Valid ShopToOrderableRequest request + ) { + shopToOrderableService.createOrderableRequest(ownerId, request, shopId); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java new file mode 100644 index 000000000..47458d1db --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java @@ -0,0 +1,67 @@ +package in.koreatech.koin.domain.shoptoOrderable.dto; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + + +@JsonNaming(SnakeCaseStrategy.class) +public record ShopToOrderableRequest( + + @Schema(description = "최소 주문 금액", example = "5000", requiredMode = REQUIRED) + @NotNull(message = "최소 주문 금액은 필수입니다.") + @Min(value = 0, message = "최소 주문 금액은 0원 이상이어야 합니다.") + Integer minimumOrderAmount, + + @Schema(description = "포장 가능 여부", example = "true", requiredMode = REQUIRED) + @NotNull(message = "포장 가능 여부는 필수입니다.") + Boolean takeout, + + @Schema(description = "배달 옵션 (CAMPUS/OUTSIDE/BOTH)", example = "BOTH", requiredMode = REQUIRED) + @NotNull(message = "배달 옵션은 필수입니다.") + String deliveryOption, + + @Schema(description = "교내 배달팁", example = "1000", requiredMode = REQUIRED) + @NotNull(message = "교내 배달팁은 필수입니다.") + @Min(value = 0, message = "교내 배달팁은 0원 이상이어야 합니다.") + Integer campusDeliveryTip, + + @Schema(description = "교외 배달팁", example = "2000", requiredMode = REQUIRED) + @NotNull(message = "교외 배달팁은 필수입니다.") + @Min(value = 0, message = "교외 배달팁은 0원 이상이어야 합니다.") + Integer outsideDeliveryTip, + + @Schema(description = "가게 운영 여부", example = "true", requiredMode = REQUIRED) + @NotNull(message = "가게 운영 여부는 필수입니다.") + Boolean isOpen, + + @Schema(description = "사업자 등록증 URL", example = "https://example.com/business_license.jpg", requiredMode = REQUIRED) + @NotBlank(message = "사업자 등록증 URL은 필수입니다.") + String businessLicenseUrl, + + @Schema(description = "영업 신고증 URL", example = "https://example.com/business_certificate.jpg", requiredMode = REQUIRED) + @NotBlank(message = "영업 신고증 URL은 필수입니다.") + String businessCertificateUrl, + + @Schema(description = "통장 사본 URL", example = "https://example.com/bank_copy.jpg", requiredMode = REQUIRED) + @NotBlank(message = "통장 사본 URL은 필수입니다.") + String bankCopyUrl, + + @Schema(description = "은행명", example = "국민은행", requiredMode = REQUIRED) + @NotBlank(message = "은행명은 필수입니다.") + @Size(max = 10, message = "은행명은 10자 이하여야 합니다.") + String bank, + + @Schema(description = "계좌번호", example = "123-456-789", requiredMode = REQUIRED) + @NotBlank(message = "계좌번호는 필수입니다.") + @Size(max = 20, message = "계좌번호는 20자 이하여야 합니다.") + String accountNumber +) { +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java new file mode 100644 index 000000000..4d150a735 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -0,0 +1,121 @@ +package in.koreatech.koin.domain.shoptoOrderable.model; + +import static lombok.AccessLevel.PROTECTED; + +import in.koreatech.koin.common.model.BaseEntity; +import in.koreatech.koin.domain.shop.model.shop.Shop; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.Size; + +import java.time.LocalDateTime; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "shop_to_orderable") +@NoArgsConstructor(access = PROTECTED) +public class ShopToOrderable extends BaseEntity { + // 임시 필드들 (추후 최종본에선 변경) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "shop_id", nullable = false) + private Shop shop; + + @Column(nullable = false) + private Integer minimumOrderAmount; + + @Column(nullable = false) + private Boolean takeout; + + @Column(nullable = false) + private String deliveryOption; + + @Column(nullable = false) + private Integer campusDeliveryTip; + + @Column(nullable = false) + private Integer outsideDeliveryTip; + + @Column(nullable = false) + private Boolean isOpen; + + @Column(nullable = false) + private String businessLicenseUrl; + + @Column(nullable = false) + private String businessCertificateUrl; + + @Column(nullable = false) + private String bankCopyUrl; + + @Size(max = 10) + @Column(name = "bank", length = 10) + private String bank; + + @Size(max = 20) + @Column(name = "account_number", length = 20) + private String accountNumber; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private ShopToOrderableRequestStatus requestStatus; + + @Column(name = "approved_at", nullable = false, updatable = false, columnDefinition = "TIMESTAMP") + private LocalDateTime approvedAt; + + @Builder + public ShopToOrderable( + Shop shop, + Integer minimumOrderAmount, + Boolean takeout, + String deliveryOption, + Integer campusDeliveryTip, + Integer outsideDeliveryTip, + Boolean isOpen, + String businessLicenseUrl, + String businessCertificateUrl, + String bankCopyUrl, + String bank, + String accountNumber + ) { + this.shop = shop; + this.minimumOrderAmount = minimumOrderAmount; + this.takeout = takeout; + this.deliveryOption = deliveryOption; + this.campusDeliveryTip = campusDeliveryTip; + this.outsideDeliveryTip = outsideDeliveryTip; + this.isOpen = isOpen; + this.requestStatus = ShopToOrderableRequestStatus.PENDING; + this.businessLicenseUrl = businessLicenseUrl; + this.businessCertificateUrl = businessCertificateUrl; + this.bankCopyUrl = bankCopyUrl; + this.bank = bank; + this.accountNumber = accountNumber; + this.approvedAt = null; + } + + public void approveRequest() { + this.requestStatus = ShopToOrderableRequestStatus.APPROVED; + this.approvedAt = LocalDateTime.now(); + } + + public void rejectRequest() { + this.requestStatus = ShopToOrderableRequestStatus.REJECTED; + } +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java new file mode 100644 index 000000000..df4f37e56 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java @@ -0,0 +1,5 @@ +package in.koreatech.koin.domain.shoptoOrderable.model; + +public enum ShopToOrderableRequestStatus { + PENDING, APPROVED, REJECTED +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java new file mode 100644 index 000000000..e4d4998cf --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java @@ -0,0 +1,16 @@ +package in.koreatech.koin.domain.shoptoOrderable.repository; + +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; + +import org.springframework.data.repository.Repository; + +import java.util.Optional; + +public interface ShopToOrderableRepository extends Repository { + + ShopToOrderable save(ShopToOrderable shopToOrderable); + + Optional findByShopId(Integer shopId); + + boolean existsByShopId(Integer shopId); +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java new file mode 100644 index 000000000..242926c8b --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -0,0 +1,54 @@ +package in.koreatech.koin.domain.shoptoOrderable.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.domain.shop.model.shop.Shop; +import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; +import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; +import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; +import in.koreatech.koin.global.code.ApiResponseCode; +import lombok.RequiredArgsConstructor; +import in.koreatech.koin.global.exception.CustomException; + +@Service +@RequiredArgsConstructor +public class ShopToOrderableService { + + private final ShopToOrderableRepository shopToOrderableRepository; + private final ShopRepository shopRepository; + + @Transactional + public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest request, Integer shopId) { + Shop shop = shopRepository.findById(shopId) + .orElseThrow( + () -> CustomException.of(ApiResponseCode.NOT_FOUND_SHOP, "shopId: " + shopId)); + + // 이미 신청한 내역이 있는지 확인 + if (shopToOrderableRepository.existsByShopId(shopId)) { + throw CustomException.of(ApiResponseCode.ALREADY_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); + } + + // 가게 사장님인지 확인 + + // 이미 주문가능 상점인지 확인 + + ShopToOrderable shopToOrderable = ShopToOrderable.builder() + .shop(shop) + .minimumOrderAmount(request.minimumOrderAmount()) + .takeout(request.takeout()) + .deliveryOption(request.deliveryOption()) + .campusDeliveryTip(request.campusDeliveryTip()) + .outsideDeliveryTip(request.outsideDeliveryTip()) + .isOpen(request.isOpen()) + .businessLicenseUrl(request.businessLicenseUrl()) + .businessCertificateUrl(request.businessCertificateUrl()) + .bankCopyUrl(request.bankCopyUrl()) + .bank(request.bank()) + .accountNumber(request.accountNumber()) + .build(); + + shopToOrderableRepository.save(shopToOrderable); + } +} diff --git a/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java b/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java index 828c4483a..9f2ac73ca 100644 --- a/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java +++ b/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java @@ -97,7 +97,7 @@ public enum ApiResponseCode { NOT_FOUND_CLUB_RECRUITMENT(HttpStatus.NOT_FOUND, "동아리 모집 공고가 존재하지 않습니다."), NOT_FOUND_CLUB_EVENT(HttpStatus.NOT_FOUND, "동아리 행사가 존재하지 않습니다."), NOT_FOUND_DELIVERY_ADDRESS(HttpStatus.NOT_FOUND, "주소가 존재하지 않습니다."), - NOT_FOUND_ORDERABLE_SHOP(HttpStatus.NOT_FOUND, "상점이 존재하지 않습니다."), + NOT_FOUND_ORDERABLE_SHOP(HttpStatus.NOT_FOUND, "주문 가능 상점이 존재하지 않습니다."), NOT_FOUND_ORDERABLE_SHOP_MENU(HttpStatus.NOT_FOUND, "메뉴가 존재하지 않습니다"), NOT_FOUND_ORDERABLE_SHOP_MENU_PRICE(HttpStatus.NOT_FOUND, "유효하지 않은 가격 ID 입니다."), NOT_FOUND_ORDERABLE_SHOP_MENU_OPTION(HttpStatus.NOT_FOUND, "유효하지 않은 옵션 ID 입니다."), @@ -108,6 +108,7 @@ public enum ApiResponseCode { NOT_FOUND_TEMPORARY_PAYMENT(HttpStatus.NOT_FOUND, "임시 결제 정보가 존재하지 않습니다."), NOT_FOUND_PAYMENT(HttpStatus.NOT_FOUND, "결제 정보가 존재하지 않습니다."), NOT_FOUND_ORDER(HttpStatus.NOT_FOUND, "주문 정보가 존재하지 않습니다."), + NOT_FOUND_SHOP(HttpStatus.NOT_FOUND, "상점이 존재하지 않습니다."), /** * 409 CONFLICT (중복 혹은 충돌) @@ -119,6 +120,7 @@ public enum ApiResponseCode { REQUEST_TOO_FAST(HttpStatus.CONFLICT, "요청이 너무 빠릅니다. 다시 요청해주세요."), OPTIMISTIC_LOCKING_FAILURE(HttpStatus.CONFLICT, "이미 처리된 요청입니다."), DUPLICATE_CLUB_RECRUITMENT(HttpStatus.CONFLICT, "동아리 공고가 이미 존재합니다."), + ALREADY_REQUESTED_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 전환 신청이 접수된 상점입니다."), /** * 429 Too Many Requests (요청량 초과) From c8e0d7e53e3e092446c22e3ea6837ada79e26643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Mon, 27 Oct 2025 20:33:40 +0900 Subject: [PATCH 02/23] =?UTF-8?q?feat:=20=EC=A3=BC=EB=AC=B8=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=20=EC=83=81=EC=A0=90=20=EC=A0=84=ED=99=98(=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B0=80=EC=9E=85)?= =?UTF-8?q?=20POST=20API=20=EA=B2=80=EC=A6=9D=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#2048)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 검증 추가 * feat: 주문가능상점변환 테스트 추가 * refact: 매개변수 stirng -> ShopToOrderableRequestStatus로 변경 * refact: 예외 명 변경 --- .../repository/ShopToOrderableRepository.java | 3 + .../service/ShopToOrderableService.java | 9 +- .../koin/global/code/ApiResponseCode.java | 4 +- .../ShopToOrderableServiceTest.java | 161 ++++++++++++++++++ .../koin/unit/fixture/ShopFixture.java | 24 +++ 5 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java create mode 100644 src/test/java/in/koreatech/koin/unit/fixture/ShopFixture.java diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java index e4d4998cf..50f5e8982 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java @@ -1,6 +1,7 @@ package in.koreatech.koin.domain.shoptoOrderable.repository; import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; import org.springframework.data.repository.Repository; @@ -13,4 +14,6 @@ public interface ShopToOrderableRepository extends Repository findByShopId(Integer shopId); boolean existsByShopId(Integer shopId); + + boolean existsByShopIdAndRequestStatus(Integer shopId, ShopToOrderableRequestStatus requestStatus); } diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index 242926c8b..884f9d68c 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -7,6 +7,7 @@ import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; import in.koreatech.koin.global.code.ApiResponseCode; import lombok.RequiredArgsConstructor; @@ -27,12 +28,18 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque // 이미 신청한 내역이 있는지 확인 if (shopToOrderableRepository.existsByShopId(shopId)) { - throw CustomException.of(ApiResponseCode.ALREADY_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); + throw CustomException.of(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); } // 가게 사장님인지 확인 + if (!shop.getOwner().getId().equals(ownerId)) { + throw CustomException.of(ApiResponseCode.FORBIDDEN_SHOP_OWNER, "ownerId: " + ownerId + ", shopId: " + shopId); + } // 이미 주문가능 상점인지 확인 + if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.APPROVED)) { + throw CustomException.of(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP, "shopId: " + shopId); + } ShopToOrderable shopToOrderable = ShopToOrderable.builder() .shop(shop) diff --git a/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java b/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java index 9f2ac73ca..bb25f9ab0 100644 --- a/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java +++ b/src/main/java/in/koreatech/koin/global/code/ApiResponseCode.java @@ -85,6 +85,7 @@ public enum ApiResponseCode { FORBIDDEN_BLOCKED_USER(HttpStatus.FORBIDDEN, "차단된 사용자입니다."), PAYMENT_ACCESS_DENIED(HttpStatus.FORBIDDEN, "결제 정보 접근 권한이 없습니다."), FORBIDDEN_ORDER(HttpStatus.FORBIDDEN, "주문 정보 접근 권한이 없습니다."), + FORBIDDEN_SHOP_OWNER(HttpStatus.FORBIDDEN, "상점의 사장님이 아닙니다."), /** * 404 Not Found (리소스를 찾을 수 없음) @@ -120,7 +121,8 @@ public enum ApiResponseCode { REQUEST_TOO_FAST(HttpStatus.CONFLICT, "요청이 너무 빠릅니다. 다시 요청해주세요."), OPTIMISTIC_LOCKING_FAILURE(HttpStatus.CONFLICT, "이미 처리된 요청입니다."), DUPLICATE_CLUB_RECRUITMENT(HttpStatus.CONFLICT, "동아리 공고가 이미 존재합니다."), - ALREADY_REQUESTED_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 전환 신청이 접수된 상점입니다."), + DUPLICATE_REQUESTED_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 전환 신청이 접수된 상점입니다."), + DUPLICATE_ORDERABLE_SHOP(HttpStatus.CONFLICT, "이미 주문 가능한 상점입니다."), /** * 429 Too Many Requests (요청량 초과) diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java new file mode 100644 index 000000000..337793c9a --- /dev/null +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -0,0 +1,161 @@ +package in.koreatech.koin.unit.domain.ShopToOrderable; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import in.koreatech.koin.domain.shop.model.shop.Shop; +import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; +import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; +import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; +import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService; +import in.koreatech.koin.domain.owner.model.Owner; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.global.code.ApiResponseCode; +import in.koreatech.koin.global.exception.CustomException; +import in.koreatech.koin.unit.fixture.OwnerFixture; +import in.koreatech.koin.unit.fixture.ShopFixture; + +import org.springframework.test.util.ReflectionTestUtils; + +@ExtendWith(MockitoExtension.class) +class ShopToOrderableServiceTest { + + @InjectMocks + private ShopToOrderableService shopToOrderableService; + + @Mock + private ShopToOrderableRepository shopToOrderableRepository; + + @Mock + private ShopRepository shopRepository; + + private Owner owner; + private User user; + private Shop shop; + private ShopToOrderableRequest request; + + @BeforeEach + void setUp() { + owner = OwnerFixture.성빈_사장님(); + ReflectionTestUtils.setField(owner, "id", 1); + shop = ShopFixture.주문전환_이전_상점(owner); + + request = new ShopToOrderableRequest( + 5000, + true, + "BOTH", + 1000, + 2000, + true, + "https://example.com/business_license.jpg", + "https://example.com/business_certificate.jpg", + "https://example.com/bank_copy.jpg", + "국민은행", + "123-456-789" + ); + } + + @Nested + @DisplayName("주문 가능 상점 신청 생성 테스트") + class CreateOrderableRequestTest { + + @Test + @DisplayName("정상적으로 주문 가능 상점 신청이 생성된다") + void createOrderableRequestSuccessfully() { + // given + when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopToOrderableRepository.existsByShopId(100)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(false); + when(shopToOrderableRepository.save(any(ShopToOrderable.class))).thenAnswer( + invocation -> invocation.getArgument(0)); + + // when + shopToOrderableService.createOrderableRequest(1, request, 100); + + // then + ArgumentCaptor captor = ArgumentCaptor.forClass(ShopToOrderable.class); + verify(shopToOrderableRepository).save(captor.capture()); + + ShopToOrderable saved = captor.getValue(); + assertThat(saved.getShop()).isEqualTo(shop); + assertThat(saved.getMinimumOrderAmount()).isEqualTo(5000); + assertThat(saved.getTakeout()).isTrue(); + assertThat(saved.getDeliveryOption()).isEqualTo("BOTH"); + assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000); + assertThat(saved.getOutsideDeliveryTip()).isEqualTo(2000); + assertThat(saved.getIsOpen()).isTrue(); + assertThat(saved.getBank()).isEqualTo("국민은행"); + assertThat(saved.getAccountNumber()).isEqualTo("123-456-789"); + } + + @Test + @DisplayName("존재하지 않는 상점 ID로 신청하면 예외가 발생한다") + void throwExceptionWhenShopNotFound() { + // given + when(shopRepository.findById(999)).thenReturn(Optional.empty()); + + // when & then + CustomException exception = assertThrows(CustomException.class, + () -> shopToOrderableService.createOrderableRequest(1, request, 999)); + + assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.NOT_FOUND_SHOP); + } + + @Test + @DisplayName("이미 신청한 내역이 있으면 예외가 발생한다") + void throwExceptionWhenAlreadyRequested() { + // given + when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopToOrderableRepository.existsByShopId(100)).thenReturn(true); + + // when & then + CustomException exception = assertThrows(CustomException.class, + () -> shopToOrderableService.createOrderableRequest(1, request, 100)); + + assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP); + } + + @Test + @DisplayName("상점 사장님이 아닌 경우 예외가 발생한다") + void throwExceptionWhenNotShopOwner() { + // given + when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + + // when & then + CustomException exception = assertThrows(CustomException.class, + () -> shopToOrderableService.createOrderableRequest(2, request, 100)); + assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.FORBIDDEN_SHOP_OWNER); + } + + @Test + @DisplayName("이미 승인된 상점이 있으면 예외가 발생한다") + void throwExceptionWhenAlreadyApproved() { + // given + when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopToOrderableRepository.existsByShopId(100)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(true); + + // when & then + CustomException exception = assertThrows(CustomException.class, + () -> shopToOrderableService.createOrderableRequest(1, request, 100)); + assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP); + } + } +} diff --git a/src/test/java/in/koreatech/koin/unit/fixture/ShopFixture.java b/src/test/java/in/koreatech/koin/unit/fixture/ShopFixture.java new file mode 100644 index 000000000..73235c698 --- /dev/null +++ b/src/test/java/in/koreatech/koin/unit/fixture/ShopFixture.java @@ -0,0 +1,24 @@ +package in.koreatech.koin.unit.fixture; + +import org.springframework.test.util.ReflectionTestUtils; + +import in.koreatech.koin.domain.owner.model.Owner; +import in.koreatech.koin.domain.shop.model.shop.Shop; + +public class ShopFixture { + + private ShopFixture() {} + + public static Shop 주문전환_이전_상점(Owner owner) { + Shop shop = Shop.builder() + .name("테스트 상점") + .owner(owner) + .phone("041-123-4567") + .address("천안시 동남구") + .description("테스트 상점입니다") + .build(); + ReflectionTestUtils.setField(shop, "id", 100); + + return shop; + } +} From 1c2c0215efe5861fdd5c86c9ac2ca60c67d0f00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:02:12 +0900 Subject: [PATCH 03/23] =?UTF-8?q?feat=20:=20flyway=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=AC=EB=9F=BC=20=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/ShopToOrderable.java | 24 +++++++++---------- .../V226__add_shop_to_orderable_request.sql | 22 +++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 4d150a735..dd59e92c1 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -37,43 +37,43 @@ public class ShopToOrderable extends BaseEntity { @JoinColumn(name = "shop_id", nullable = false) private Shop shop; - @Column(nullable = false) + @Column(name="minimum_order_amount", nullable = false) private Integer minimumOrderAmount; - @Column(nullable = false) + @Column(name="takeout" ,nullable = false) private Boolean takeout; - @Column(nullable = false) + @Column(name="delivery_option", nullable = false) private String deliveryOption; - @Column(nullable = false) + @Column(name="campus_delivery_tip", nullable = false) private Integer campusDeliveryTip; - @Column(nullable = false) + @Column(name="outside_delivery_tip", nullable = false) private Integer outsideDeliveryTip; - @Column(nullable = false) + @Column(name="is_open", nullable = false) private Boolean isOpen; - @Column(nullable = false) + @Column(name = "business_license_url", nullable = false) private String businessLicenseUrl; - @Column(nullable = false) + @Column(name = "business_certificate_url", nullable = false) private String businessCertificateUrl; - @Column(nullable = false) + @Column(name = "bank_copy_url", nullable = false) private String bankCopyUrl; @Size(max = 10) - @Column(name = "bank", length = 10) + @Column(name = "bank", length = 10, nullable = false) private String bank; @Size(max = 20) - @Column(name = "account_number", length = 20) + @Column(name = "account_number", length = 20, nullable = false) private String accountNumber; @Enumerated(EnumType.STRING) - @Column(nullable = false) + @Column(name = "request_status", nullable = false) private ShopToOrderableRequestStatus requestStatus; @Column(name = "approved_at", nullable = false, updatable = false, columnDefinition = "TIMESTAMP") diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql new file mode 100644 index 000000000..ec82f69e7 --- /dev/null +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -0,0 +1,22 @@ +CREATE TABLE IF NOT EXISTS `shop_to_orderable` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '고유 ID', + `shop_id` INT UNSIGNED NOT NULL COMMENT '식당 ID', + `minimum_order_amount` INT UNSIGNED NOT NULL COMMENT '최소 주문 금액', + `takeout` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '포장 여부', + `delivery_option` VARCHAR(50) NOT NULL COMMENT '배달 옵션', + `campus_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 내 배달 팁', + `outside_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 외 배달 팁', + `is_open` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '영업 여부', + `business_license_url` VARCHAR(255) NOT NULL COMMENT '사업자 등록증 URL', + `business_certificate_url` VARCHAR(255) NOT NULL COMMENT '영업 신고증 URL', + `bank_copy_url` VARCHAR(255) NOT NULL COMMENT '통장 사본 URL', + `bank` VARCHAR(10) NOT NULL COMMENT '은행명', + `account_number` VARCHAR(20) NOT NULL COMMENT '계좌 번호', + `request_status` VARCHAR(50) NOT NULL COMMENT '요청 상태', + `approved_at` TIMESTAMP NULL DEFAULT NULL COMMENT '승인 일자', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 일자', + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 일자', + PRIMARY KEY (`id`), + KEY `idx_shop_id` (`shop_id`) +) + From b93a4131ed61523f5fa454f021954872f0a71daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:11:49 +0900 Subject: [PATCH 04/23] =?UTF-8?q?refact=20:=20isOpen=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/shoptoOrderable/dto/ShopToOrderableRequest.java | 4 ---- .../koin/domain/shoptoOrderable/model/ShopToOrderable.java | 5 ----- .../shoptoOrderable/service/ShopToOrderableService.java | 1 - .../db/migration/V226__add_shop_to_orderable_request.sql | 1 - 4 files changed, 11 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java index 47458d1db..7f48e65ab 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java @@ -38,10 +38,6 @@ public record ShopToOrderableRequest( @Min(value = 0, message = "교외 배달팁은 0원 이상이어야 합니다.") Integer outsideDeliveryTip, - @Schema(description = "가게 운영 여부", example = "true", requiredMode = REQUIRED) - @NotNull(message = "가게 운영 여부는 필수입니다.") - Boolean isOpen, - @Schema(description = "사업자 등록증 URL", example = "https://example.com/business_license.jpg", requiredMode = REQUIRED) @NotBlank(message = "사업자 등록증 URL은 필수입니다.") String businessLicenseUrl, diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index dd59e92c1..1c47eecd4 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -52,9 +52,6 @@ public class ShopToOrderable extends BaseEntity { @Column(name="outside_delivery_tip", nullable = false) private Integer outsideDeliveryTip; - @Column(name="is_open", nullable = false) - private Boolean isOpen; - @Column(name = "business_license_url", nullable = false) private String businessLicenseUrl; @@ -87,7 +84,6 @@ public ShopToOrderable( String deliveryOption, Integer campusDeliveryTip, Integer outsideDeliveryTip, - Boolean isOpen, String businessLicenseUrl, String businessCertificateUrl, String bankCopyUrl, @@ -100,7 +96,6 @@ public ShopToOrderable( this.deliveryOption = deliveryOption; this.campusDeliveryTip = campusDeliveryTip; this.outsideDeliveryTip = outsideDeliveryTip; - this.isOpen = isOpen; this.requestStatus = ShopToOrderableRequestStatus.PENDING; this.businessLicenseUrl = businessLicenseUrl; this.businessCertificateUrl = businessCertificateUrl; diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index 884f9d68c..addfbbb60 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -48,7 +48,6 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque .deliveryOption(request.deliveryOption()) .campusDeliveryTip(request.campusDeliveryTip()) .outsideDeliveryTip(request.outsideDeliveryTip()) - .isOpen(request.isOpen()) .businessLicenseUrl(request.businessLicenseUrl()) .businessCertificateUrl(request.businessCertificateUrl()) .bankCopyUrl(request.bankCopyUrl()) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index ec82f69e7..e37999618 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -6,7 +6,6 @@ CREATE TABLE IF NOT EXISTS `shop_to_orderable` ( `delivery_option` VARCHAR(50) NOT NULL COMMENT '배달 옵션', `campus_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 내 배달 팁', `outside_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 외 배달 팁', - `is_open` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '영업 여부', `business_license_url` VARCHAR(255) NOT NULL COMMENT '사업자 등록증 URL', `business_certificate_url` VARCHAR(255) NOT NULL COMMENT '영업 신고증 URL', `bank_copy_url` VARCHAR(255) NOT NULL COMMENT '통장 사본 URL', From 4c79e3cb78b3bddcce5181058dc7b7c4ec964379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 00:43:36 +0900 Subject: [PATCH 05/23] =?UTF-8?q?fix=20:=20isOpen=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java index 337793c9a..58336ff34 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -63,7 +63,6 @@ void setUp() { "BOTH", 1000, 2000, - true, "https://example.com/business_license.jpg", "https://example.com/business_certificate.jpg", "https://example.com/bank_copy.jpg", @@ -100,7 +99,6 @@ void createOrderableRequestSuccessfully() { assertThat(saved.getDeliveryOption()).isEqualTo("BOTH"); assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000); assertThat(saved.getOutsideDeliveryTip()).isEqualTo(2000); - assertThat(saved.getIsOpen()).isTrue(); assertThat(saved.getBank()).isEqualTo("국민은행"); assertThat(saved.getAccountNumber()).isEqualTo("123-456-789"); } From a5e9b71061c566d619157472de192a120b5c69a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:12:16 +0900 Subject: [PATCH 06/23] =?UTF-8?q?refact=20:=20idx=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V226__add_shop_to_orderable_request.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index e37999618..cef6d338e 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS `shop_to_orderable` ( +CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '고유 ID', `shop_id` INT UNSIGNED NOT NULL COMMENT '식당 ID', `minimum_order_amount` INT UNSIGNED NOT NULL COMMENT '최소 주문 금액', @@ -16,6 +16,5 @@ CREATE TABLE IF NOT EXISTS `shop_to_orderable` ( `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 일자', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 일자', PRIMARY KEY (`id`), - KEY `idx_shop_id` (`shop_id`) ) From 813e8d71db4b4a289fcaff1cd01410b2ec01d12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:13:55 +0900 Subject: [PATCH 07/23] =?UTF-8?q?refact=20:=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EC=A4=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V226__add_shop_to_orderable_request.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index cef6d338e..e75104fc5 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -1,4 +1,5 @@ -CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` ( +CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` +( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '고유 ID', `shop_id` INT UNSIGNED NOT NULL COMMENT '식당 ID', `minimum_order_amount` INT UNSIGNED NOT NULL COMMENT '최소 주문 금액', @@ -15,6 +16,5 @@ CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` ( `approved_at` TIMESTAMP NULL DEFAULT NULL COMMENT '승인 일자', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 일자', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 일자', - PRIMARY KEY (`id`), -) - + PRIMARY KEY (`id`) +); From e8286ba58e797fcb3402e97524d5f43fbb380220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:30:34 +0900 Subject: [PATCH 08/23] =?UTF-8?q?feat=20:=20fk=5Fshop=5Fto=5Forderable=5Fs?= =?UTF-8?q?hop=20=EC=99=B8=EB=9E=98=ED=82=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V226__add_shop_to_orderable_request.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index e75104fc5..f77e664a3 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -16,5 +16,6 @@ CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` `approved_at` TIMESTAMP NULL DEFAULT NULL COMMENT '승인 일자', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 일자', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 일자', - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + CONSTRAINT fk_shop_to_orderable_shop FOREIGN KEY (shop_id) REFERENCES `koin`.`shop`(id) ON DELETE CASCADE ); From e2613fc37e6f600765cbb9976c5c65f39921f84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:32:10 +0900 Subject: [PATCH 09/23] =?UTF-8?q?fix=20:=20approvedAt=20NULL=20=EB=B6=88?= =?UTF-8?q?=EC=9D=BC=EC=B9=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/shoptoOrderable/model/ShopToOrderable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 1c47eecd4..1b6d87093 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -73,7 +73,7 @@ public class ShopToOrderable extends BaseEntity { @Column(name = "request_status", nullable = false) private ShopToOrderableRequestStatus requestStatus; - @Column(name = "approved_at", nullable = false, updatable = false, columnDefinition = "TIMESTAMP") + @Column(name = "approved_at", columnDefinition = "TIMESTAMP") private LocalDateTime approvedAt; @Builder From c713291e66dfe6489fc7786ed00b965f49329042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:40:00 +0900 Subject: [PATCH 10/23] =?UTF-8?q?feat:=20=EC=83=81=EC=A0=90=20@ManyToOne?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/shoptoOrderable/model/ShopToOrderable.java | 5 ++--- .../repository/ShopToOrderableRepository.java | 3 --- .../shoptoOrderable/service/ShopToOrderableService.java | 2 +- .../db/migration/V226__add_shop_to_orderable_request.sql | 2 +- .../domain/ShopToOrderable/ShopToOrderableServiceTest.java | 6 +++--- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 1b6d87093..1a96452ba 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -13,7 +13,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; +import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.Size; @@ -28,12 +28,11 @@ @Table(name = "shop_to_orderable") @NoArgsConstructor(access = PROTECTED) public class ShopToOrderable extends BaseEntity { - // 임시 필드들 (추후 최종본에선 변경) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; - @OneToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "shop_id", nullable = false) private Shop shop; diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java index 50f5e8982..78b07c24c 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java @@ -5,14 +5,11 @@ import org.springframework.data.repository.Repository; -import java.util.Optional; public interface ShopToOrderableRepository extends Repository { ShopToOrderable save(ShopToOrderable shopToOrderable); - Optional findByShopId(Integer shopId); - boolean existsByShopId(Integer shopId); boolean existsByShopIdAndRequestStatus(Integer shopId, ShopToOrderableRequestStatus requestStatus); diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index addfbbb60..bda20b310 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -27,7 +27,7 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque () -> CustomException.of(ApiResponseCode.NOT_FOUND_SHOP, "shopId: " + shopId)); // 이미 신청한 내역이 있는지 확인 - if (shopToOrderableRepository.existsByShopId(shopId)) { + if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.PENDING)) { throw CustomException.of(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); } diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index f77e664a3..963a9ee37 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` `bank_copy_url` VARCHAR(255) NOT NULL COMMENT '통장 사본 URL', `bank` VARCHAR(10) NOT NULL COMMENT '은행명', `account_number` VARCHAR(20) NOT NULL COMMENT '계좌 번호', - `request_status` VARCHAR(50) NOT NULL COMMENT '요청 상태', + `request_status` VARCHAR(50) NOT NULL DEFAULT 'PENDING' COMMENT '요청 상태', `approved_at` TIMESTAMP NULL DEFAULT NULL COMMENT '승인 일자', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 일자', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 일자', diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java index 58336ff34..501a7c002 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -80,7 +80,7 @@ class CreateOrderableRequestTest { void createOrderableRequestSuccessfully() { // given when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); - when(shopToOrderableRepository.existsByShopId(100)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(false); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(false); when(shopToOrderableRepository.save(any(ShopToOrderable.class))).thenAnswer( invocation -> invocation.getArgument(0)); @@ -121,7 +121,7 @@ void throwExceptionWhenShopNotFound() { void throwExceptionWhenAlreadyRequested() { // given when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); - when(shopToOrderableRepository.existsByShopId(100)).thenReturn(true); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(true); // when & then CustomException exception = assertThrows(CustomException.class, @@ -147,7 +147,7 @@ void throwExceptionWhenNotShopOwner() { void throwExceptionWhenAlreadyApproved() { // given when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); - when(shopToOrderableRepository.existsByShopId(100)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(false); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(true); // when & then From a45169ed69d5c941f588e16c811b69616c873bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:53:36 +0900 Subject: [PATCH 11/23] =?UTF-8?q?feat:=20ShopToOrderableDeliveryOption=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/shoptoOrderable/dto/ShopToOrderableRequest.java | 3 ++- .../koin/domain/shoptoOrderable/model/ShopToOrderable.java | 5 +++-- .../model/ShopToOrderableDeliveryOption.java | 7 +++++++ .../domain/ShopToOrderable/ShopToOrderableServiceTest.java | 5 +++-- 4 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java index 7f48e65ab..8fa17659c 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableDeliveryOption; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; @@ -26,7 +27,7 @@ public record ShopToOrderableRequest( @Schema(description = "배달 옵션 (CAMPUS/OUTSIDE/BOTH)", example = "BOTH", requiredMode = REQUIRED) @NotNull(message = "배달 옵션은 필수입니다.") - String deliveryOption, + ShopToOrderableDeliveryOption deliveryOption, @Schema(description = "교내 배달팁", example = "1000", requiredMode = REQUIRED) @NotNull(message = "교내 배달팁은 필수입니다.") diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 1a96452ba..786b7258c 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -42,8 +42,9 @@ public class ShopToOrderable extends BaseEntity { @Column(name="takeout" ,nullable = false) private Boolean takeout; + @Enumerated(EnumType.STRING) @Column(name="delivery_option", nullable = false) - private String deliveryOption; + private ShopToOrderableDeliveryOption deliveryOption; @Column(name="campus_delivery_tip", nullable = false) private Integer campusDeliveryTip; @@ -80,7 +81,7 @@ public ShopToOrderable( Shop shop, Integer minimumOrderAmount, Boolean takeout, - String deliveryOption, + ShopToOrderableDeliveryOption deliveryOption, Integer campusDeliveryTip, Integer outsideDeliveryTip, String businessLicenseUrl, diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java new file mode 100644 index 000000000..5a5ab7ccd --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java @@ -0,0 +1,7 @@ +package in.koreatech.koin.domain.shoptoOrderable.model; + +public enum ShopToOrderableDeliveryOption { + CAMPUS, + OUTSIDE, + BOTH +} diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java index 501a7c002..a44a5704c 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -22,6 +22,7 @@ import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; +import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableDeliveryOption; import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService; @@ -60,7 +61,7 @@ void setUp() { request = new ShopToOrderableRequest( 5000, true, - "BOTH", + ShopToOrderableDeliveryOption.BOTH, 1000, 2000, "https://example.com/business_license.jpg", @@ -96,7 +97,7 @@ void createOrderableRequestSuccessfully() { assertThat(saved.getShop()).isEqualTo(shop); assertThat(saved.getMinimumOrderAmount()).isEqualTo(5000); assertThat(saved.getTakeout()).isTrue(); - assertThat(saved.getDeliveryOption()).isEqualTo("BOTH"); + assertThat(saved.getDeliveryOption()).isEqualTo(ShopToOrderableDeliveryOption.BOTH); assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000); assertThat(saved.getOutsideDeliveryTip()).isEqualTo(2000); assertThat(saved.getBank()).isEqualTo("국민은행"); From b67f462a1c78f21f1056bcbfbe08c6109303c985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:09:12 +0900 Subject: [PATCH 12/23] =?UTF-8?q?refact:=20@NotNULL=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/ShopToOrderable.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 786b7258c..0db33b949 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -15,6 +15,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import java.time.LocalDateTime; @@ -28,53 +29,62 @@ @Table(name = "shop_to_orderable") @NoArgsConstructor(access = PROTECTED) public class ShopToOrderable extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "shop_id", nullable = false) private Shop shop; - @Column(name="minimum_order_amount", nullable = false) + @NotNull + @Column(name = "minimum_order_amount", nullable = false) private Integer minimumOrderAmount; - @Column(name="takeout" ,nullable = false) - private Boolean takeout; + @Column(name = "takeout", nullable = false) + private Boolean takeout = false; + @NotNull @Enumerated(EnumType.STRING) - @Column(name="delivery_option", nullable = false) + @Column(name = "delivery_option", nullable = false) private ShopToOrderableDeliveryOption deliveryOption; - @Column(name="campus_delivery_tip", nullable = false) - private Integer campusDeliveryTip; + @Column(name = "campus_delivery_tip", nullable = false) + private Integer campusDeliveryTip = 0; - @Column(name="outside_delivery_tip", nullable = false) - private Integer outsideDeliveryTip; + @Column(name = "outside_delivery_tip", nullable = false) + private Integer outsideDeliveryTip = 0; + @NotNull @Column(name = "business_license_url", nullable = false) private String businessLicenseUrl; + @NotNull @Column(name = "business_certificate_url", nullable = false) private String businessCertificateUrl; + @NotNull @Column(name = "bank_copy_url", nullable = false) private String bankCopyUrl; + @NotNull @Size(max = 10) @Column(name = "bank", length = 10, nullable = false) private String bank; + @NotNull @Size(max = 20) @Column(name = "account_number", length = 20, nullable = false) private String accountNumber; @Enumerated(EnumType.STRING) @Column(name = "request_status", nullable = false) - private ShopToOrderableRequestStatus requestStatus; + private ShopToOrderableRequestStatus requestStatus = ShopToOrderableRequestStatus.PENDING; @Column(name = "approved_at", columnDefinition = "TIMESTAMP") - private LocalDateTime approvedAt; + private LocalDateTime approvedAt = null; @Builder public ShopToOrderable( @@ -96,7 +106,6 @@ public ShopToOrderable( this.deliveryOption = deliveryOption; this.campusDeliveryTip = campusDeliveryTip; this.outsideDeliveryTip = outsideDeliveryTip; - this.requestStatus = ShopToOrderableRequestStatus.PENDING; this.businessLicenseUrl = businessLicenseUrl; this.businessCertificateUrl = businessCertificateUrl; this.bankCopyUrl = bankCopyUrl; From dee35e5ac3f57b128a96eccfdc5c0f8c354946cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:12:31 +0900 Subject: [PATCH 13/23] =?UTF-8?q?refact:=20=EA=B2=80=EC=A6=9D=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=A9=94=EC=84=9C=EB=93=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ShopToOrderableService.java | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index bda20b310..58b2d1bff 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -26,20 +26,7 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque .orElseThrow( () -> CustomException.of(ApiResponseCode.NOT_FOUND_SHOP, "shopId: " + shopId)); - // 이미 신청한 내역이 있는지 확인 - if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.PENDING)) { - throw CustomException.of(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); - } - - // 가게 사장님인지 확인 - if (!shop.getOwner().getId().equals(ownerId)) { - throw CustomException.of(ApiResponseCode.FORBIDDEN_SHOP_OWNER, "ownerId: " + ownerId + ", shopId: " + shopId); - } - - // 이미 주문가능 상점인지 확인 - if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.APPROVED)) { - throw CustomException.of(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP, "shopId: " + shopId); - } + validateCreateOrderableRequest(ownerId, shopId, shop); ShopToOrderable shopToOrderable = ShopToOrderable.builder() .shop(shop) @@ -57,4 +44,26 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque shopToOrderableRepository.save(shopToOrderable); } + + private void validateCreateOrderableRequest( + Integer ownerId, + Integer shopId, + Shop shop + ) { + // 이미 신청한 내역이 있는지 확인 + if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.PENDING)) { + throw CustomException.of(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); + } + + // 가게 사장님인지 확인 + if (!shop.getOwner().getId().equals(ownerId)) { + throw CustomException.of(ApiResponseCode.FORBIDDEN_SHOP_OWNER, + "ownerId: " + ownerId + ", shopId: " + shopId); + } + + // 이미 주문가능 상점인지 확인 + if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.APPROVED)) { + throw CustomException.of(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP, "shopId: " + shopId); + } + } } From ad017d3e83f509d8e1cd2b51c1ec9a241c2f6b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:14:13 +0900 Subject: [PATCH 14/23] =?UTF-8?q?refact:=20=EA=B0=9C=ED=96=89=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java | 1 - .../shoptoOrderable/repository/ShopToOrderableRepository.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java index 8fa17659c..f3e40986c 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java @@ -12,7 +12,6 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; - @JsonNaming(SnakeCaseStrategy.class) public record ShopToOrderableRequest( diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java index 78b07c24c..1075050ce 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java @@ -5,7 +5,6 @@ import org.springframework.data.repository.Repository; - public interface ShopToOrderableRepository extends Repository { ShopToOrderable save(ShopToOrderable shopToOrderable); From 697929e8f8cb3e480acc5625eb31a86c7dc7d638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:19:00 +0900 Subject: [PATCH 15/23] =?UTF-8?q?fix=20:=20flyway=20fk=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EB=AA=85=20=EB=B6=88=EC=9D=BC=EC=B9=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V226__add_shop_to_orderable_request.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index 963a9ee37..8930676e4 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -17,5 +17,5 @@ CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 일자', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 일자', PRIMARY KEY (`id`), - CONSTRAINT fk_shop_to_orderable_shop FOREIGN KEY (shop_id) REFERENCES `koin`.`shop`(id) ON DELETE CASCADE + CONSTRAINT fk_shop_to_orderable_shop FOREIGN KEY (`shop_id`) REFERENCES `koin`.`shops`(`id`) ON DELETE CASCADE ); From 769d0694e7f6fc359a3e3af09a1418c2592c5be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:20:07 +0900 Subject: [PATCH 16/23] =?UTF-8?q?feat:=20Swagger=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/in/koreatech/koin/global/config/SwaggerGroupConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java b/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java index da4bf6382..caf9603fb 100644 --- a/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java +++ b/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java @@ -41,6 +41,7 @@ public GroupedOpenApi businessApi() { "in.koreatech.koin.domain.ownershop", "in.koreatech.koin.domain.shop", "in.koreatech.koin.domain.land", + "in.koreatech.koin.domain.shoptoOrderable", }); } From 14d34713d0f82ee3f61f3dabf01a6148b37c7a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:30:16 +0900 Subject: [PATCH 17/23] =?UTF-8?q?refact:=20takeout=20->=20isTakeout=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=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 --- .../dto/ShopToOrderableRequest.java | 2 +- .../shoptoOrderable/model/ShopToOrderable.java | 8 ++++---- .../service/ShopToOrderableService.java | 2 +- .../V226__add_shop_to_orderable_request.sql | 2 +- .../ShopToOrderableServiceTest.java | 17 +++++++++++------ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java index f3e40986c..831219e30 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java @@ -22,7 +22,7 @@ public record ShopToOrderableRequest( @Schema(description = "포장 가능 여부", example = "true", requiredMode = REQUIRED) @NotNull(message = "포장 가능 여부는 필수입니다.") - Boolean takeout, + Boolean isTakeout, @Schema(description = "배달 옵션 (CAMPUS/OUTSIDE/BOTH)", example = "BOTH", requiredMode = REQUIRED) @NotNull(message = "배달 옵션은 필수입니다.") diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 0db33b949..5592af1bb 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -43,8 +43,8 @@ public class ShopToOrderable extends BaseEntity { @Column(name = "minimum_order_amount", nullable = false) private Integer minimumOrderAmount; - @Column(name = "takeout", nullable = false) - private Boolean takeout = false; + @Column(name = "is_takeout", nullable = false) + private Boolean isTakeout = false; @NotNull @Enumerated(EnumType.STRING) @@ -90,7 +90,7 @@ public class ShopToOrderable extends BaseEntity { public ShopToOrderable( Shop shop, Integer minimumOrderAmount, - Boolean takeout, + Boolean isTakeout, ShopToOrderableDeliveryOption deliveryOption, Integer campusDeliveryTip, Integer outsideDeliveryTip, @@ -102,7 +102,7 @@ public ShopToOrderable( ) { this.shop = shop; this.minimumOrderAmount = minimumOrderAmount; - this.takeout = takeout; + this.isTakeout = isTakeout; this.deliveryOption = deliveryOption; this.campusDeliveryTip = campusDeliveryTip; this.outsideDeliveryTip = outsideDeliveryTip; diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index 58b2d1bff..57f84bdf3 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -31,7 +31,7 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque ShopToOrderable shopToOrderable = ShopToOrderable.builder() .shop(shop) .minimumOrderAmount(request.minimumOrderAmount()) - .takeout(request.takeout()) + .isTakeout(request.isTakeout()) .deliveryOption(request.deliveryOption()) .campusDeliveryTip(request.campusDeliveryTip()) .outsideDeliveryTip(request.outsideDeliveryTip()) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index 8930676e4..27def0627 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '고유 ID', `shop_id` INT UNSIGNED NOT NULL COMMENT '식당 ID', `minimum_order_amount` INT UNSIGNED NOT NULL COMMENT '최소 주문 금액', - `takeout` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '포장 여부', + `is_takeout` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '포장 여부', `delivery_option` VARCHAR(50) NOT NULL COMMENT '배달 옵션', `campus_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 내 배달 팁', `outside_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 외 배달 팁', diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java index a44a5704c..3dd3858c8 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -81,8 +81,10 @@ class CreateOrderableRequestTest { void createOrderableRequestSuccessfully() { // given when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(false); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, + ShopToOrderableRequestStatus.PENDING)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, + ShopToOrderableRequestStatus.APPROVED)).thenReturn(false); when(shopToOrderableRepository.save(any(ShopToOrderable.class))).thenAnswer( invocation -> invocation.getArgument(0)); @@ -96,7 +98,7 @@ void createOrderableRequestSuccessfully() { ShopToOrderable saved = captor.getValue(); assertThat(saved.getShop()).isEqualTo(shop); assertThat(saved.getMinimumOrderAmount()).isEqualTo(5000); - assertThat(saved.getTakeout()).isTrue(); + assertThat(saved.getIsTakeout()).isTrue(); assertThat(saved.getDeliveryOption()).isEqualTo(ShopToOrderableDeliveryOption.BOTH); assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000); assertThat(saved.getOutsideDeliveryTip()).isEqualTo(2000); @@ -122,7 +124,8 @@ void throwExceptionWhenShopNotFound() { void throwExceptionWhenAlreadyRequested() { // given when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(true); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, + ShopToOrderableRequestStatus.PENDING)).thenReturn(true); // when & then CustomException exception = assertThrows(CustomException.class, @@ -148,8 +151,10 @@ void throwExceptionWhenNotShopOwner() { void throwExceptionWhenAlreadyApproved() { // given when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(false); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.APPROVED)).thenReturn(true); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, + ShopToOrderableRequestStatus.PENDING)).thenReturn(false); + when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, + ShopToOrderableRequestStatus.APPROVED)).thenReturn(true); // when & then CustomException exception = assertThrows(CustomException.class, From 67d540c49256085ad469d69195c26058ac0c03e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:33:32 +0900 Subject: [PATCH 18/23] =?UTF-8?q?refact:=20outside=5Fdelivery=5Ftip=20->?= =?UTF-8?q?=20off=5Fcampus=5Fdelivery=5Ftip=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 --- .../shoptoOrderable/dto/ShopToOrderableRequest.java | 2 +- .../domain/shoptoOrderable/model/ShopToOrderable.java | 8 ++++---- .../shoptoOrderable/service/ShopToOrderableService.java | 2 +- .../db/migration/V226__add_shop_to_orderable_request.sql | 2 +- .../ShopToOrderable/ShopToOrderableServiceTest.java | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java index 831219e30..680dc8f63 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java @@ -36,7 +36,7 @@ public record ShopToOrderableRequest( @Schema(description = "교외 배달팁", example = "2000", requiredMode = REQUIRED) @NotNull(message = "교외 배달팁은 필수입니다.") @Min(value = 0, message = "교외 배달팁은 0원 이상이어야 합니다.") - Integer outsideDeliveryTip, + Integer offCampusDeliveryTip, @Schema(description = "사업자 등록증 URL", example = "https://example.com/business_license.jpg", requiredMode = REQUIRED) @NotBlank(message = "사업자 등록증 URL은 필수입니다.") diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java index 5592af1bb..655d7e9f5 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java @@ -54,8 +54,8 @@ public class ShopToOrderable extends BaseEntity { @Column(name = "campus_delivery_tip", nullable = false) private Integer campusDeliveryTip = 0; - @Column(name = "outside_delivery_tip", nullable = false) - private Integer outsideDeliveryTip = 0; + @Column(name = "off_campus_delivery_tip", nullable = false) + private Integer offCampusDeliveryTip = 0; @NotNull @Column(name = "business_license_url", nullable = false) @@ -93,7 +93,7 @@ public ShopToOrderable( Boolean isTakeout, ShopToOrderableDeliveryOption deliveryOption, Integer campusDeliveryTip, - Integer outsideDeliveryTip, + Integer offCampusDeliveryTip, String businessLicenseUrl, String businessCertificateUrl, String bankCopyUrl, @@ -105,7 +105,7 @@ public ShopToOrderable( this.isTakeout = isTakeout; this.deliveryOption = deliveryOption; this.campusDeliveryTip = campusDeliveryTip; - this.outsideDeliveryTip = outsideDeliveryTip; + this.offCampusDeliveryTip = offCampusDeliveryTip; this.businessLicenseUrl = businessLicenseUrl; this.businessCertificateUrl = businessCertificateUrl; this.bankCopyUrl = bankCopyUrl; diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index 57f84bdf3..916e5c3cc 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -34,7 +34,7 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque .isTakeout(request.isTakeout()) .deliveryOption(request.deliveryOption()) .campusDeliveryTip(request.campusDeliveryTip()) - .outsideDeliveryTip(request.outsideDeliveryTip()) + .offCampusDeliveryTip(request.offCampusDeliveryTip()) .businessLicenseUrl(request.businessLicenseUrl()) .businessCertificateUrl(request.businessCertificateUrl()) .bankCopyUrl(request.bankCopyUrl()) diff --git a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql index 27def0627..d5a6bcccd 100644 --- a/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql +++ b/src/main/resources/db/migration/V226__add_shop_to_orderable_request.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS `koin`.`shop_to_orderable` `is_takeout` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '포장 여부', `delivery_option` VARCHAR(50) NOT NULL COMMENT '배달 옵션', `campus_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 내 배달 팁', - `outside_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 외 배달 팁', + `off_campus_delivery_tip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '캠퍼스 외 배달 팁', `business_license_url` VARCHAR(255) NOT NULL COMMENT '사업자 등록증 URL', `business_certificate_url` VARCHAR(255) NOT NULL COMMENT '영업 신고증 URL', `bank_copy_url` VARCHAR(255) NOT NULL COMMENT '통장 사본 URL', diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java index 3dd3858c8..fe768048d 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -101,7 +101,7 @@ void createOrderableRequestSuccessfully() { assertThat(saved.getIsTakeout()).isTrue(); assertThat(saved.getDeliveryOption()).isEqualTo(ShopToOrderableDeliveryOption.BOTH); assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000); - assertThat(saved.getOutsideDeliveryTip()).isEqualTo(2000); + assertThat(saved.getOffCampusDeliveryTip()).isEqualTo(2000); assertThat(saved.getBank()).isEqualTo("국민은행"); assertThat(saved.getAccountNumber()).isEqualTo("123-456-789"); } From 683f1212a6659f8f4578d1b019741f07dc6b8671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:39:27 +0900 Subject: [PATCH 19/23] =?UTF-8?q?feat=20:=20=EB=8F=99=EC=8B=9C=EC=84=B1=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=20@DuplicateGuard=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/shoptoOrderable/controller/ShopToOrderableApi.java | 2 ++ .../shoptoOrderable/controller/ShopToOrderableController.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java index 952203c53..a02303a95 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java @@ -11,6 +11,7 @@ import in.koreatech.koin.global.auth.Auth; import in.koreatech.koin.global.code.ApiResponseCode; import in.koreatech.koin.global.code.ApiResponseCodes; +import in.koreatech.koin.global.duplicate.DuplicateGuard; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; @@ -29,6 +30,7 @@ public interface ShopToOrderableApi { @Operation(summary = "사장님 주문 서비스 가입 요청") @SecurityRequirement(name = "Jwt Authentication") @PostMapping("/owner/shops/{shopId}/orderable-requests") + @DuplicateGuard(key = "#ownerId + ':' + #shopId + ':' + #request.toString()", timeoutSeconds = 300) ResponseEntity createOrderableRequest( @Auth(permit = {OWNER}) Integer ownerId, @PathVariable Integer shopId, diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java index a7fe27d08..d86697ad8 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java @@ -8,6 +8,7 @@ import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService; +import in.koreatech.koin.global.duplicate.DuplicateGuard; import lombok.RequiredArgsConstructor; import static in.koreatech.koin.domain.user.model.UserType.OWNER; @@ -25,6 +26,7 @@ public class ShopToOrderableController implements ShopToOrderableApi { private final ShopToOrderableService shopToOrderableService; @PostMapping("/owner/shops/{shopId}/orderable-requests") + @DuplicateGuard(key = "#ownerId + ':' + #shopId + ':' + #request.toString()", timeoutSeconds = 300) public ResponseEntity createOrderableRequest( @Auth(permit = {OWNER}) Integer ownerId, @PathVariable Integer shopId, From 77557833ac751aa8135855dd467c432142ec8a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A2=85=EB=B2=94?= <84450816+asa9874@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:42:22 +0900 Subject: [PATCH 20/23] =?UTF-8?q?refact=20:=20=EA=B0=80=EA=B2=8C=20?= =?UTF-8?q?=EC=82=AC=EC=9E=A5=20=EA=B2=80=EC=A6=9D=EB=A1=9C=EC=A7=81=20sho?= =?UTF-8?q?p=20=EB=82=B4=EB=B6=80=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/in/koreatech/koin/domain/shop/model/shop/Shop.java | 4 ++++ .../shoptoOrderable/service/ShopToOrderableService.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/domain/shop/model/shop/Shop.java b/src/main/java/in/koreatech/koin/domain/shop/model/shop/Shop.java index 1bedc35c7..3b22431ce 100644 --- a/src/main/java/in/koreatech/koin/domain/shop/model/shop/Shop.java +++ b/src/main/java/in/koreatech/koin/domain/shop/model/shop/Shop.java @@ -335,4 +335,8 @@ public void cancelDelete() { public String getFullAddress() { return String.join(" ", this.address, this.addressDetail); } + + public boolean isOwner(Integer ownerId) { + return this.owner != null && this.owner.getId().equals(ownerId); + } } diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index 916e5c3cc..f2e45bdf9 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -56,7 +56,7 @@ private void validateCreateOrderableRequest( } // 가게 사장님인지 확인 - if (!shop.getOwner().getId().equals(ownerId)) { + if (!shop.isOwner(ownerId)) { throw CustomException.of(ApiResponseCode.FORBIDDEN_SHOP_OWNER, "ownerId: " + ownerId + ", shopId: " + shopId); } From de843fd66c2ff17c4fa475c3e91147e9f54bdd80 Mon Sep 17 00:00:00 2001 From: asa9874 Date: Fri, 31 Oct 2025 01:23:14 +0900 Subject: [PATCH 21/23] =?UTF-8?q?refact:=20=EC=98=88=EC=99=B8=20Static=20i?= =?UTF-8?q?mport=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ShopToOrderableService.java | 39 +++++++++---------- .../ShopToOrderableServiceTest.java | 23 ++--------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java index f2e45bdf9..29dcc911e 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java @@ -7,11 +7,12 @@ import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; -import in.koreatech.koin.global.code.ApiResponseCode; -import lombok.RequiredArgsConstructor; import in.koreatech.koin.global.exception.CustomException; +import lombok.RequiredArgsConstructor; + +import static in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus.*; +import static in.koreatech.koin.global.code.ApiResponseCode.*; @Service @RequiredArgsConstructor @@ -22,11 +23,10 @@ public class ShopToOrderableService { @Transactional public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest request, Integer shopId) { - Shop shop = shopRepository.findById(shopId) - .orElseThrow( - () -> CustomException.of(ApiResponseCode.NOT_FOUND_SHOP, "shopId: " + shopId)); + Shop shop = shopRepository.getById(shopId); - validateCreateOrderableRequest(ownerId, shopId, shop); + validateShopOwner(shop, ownerId); + validateDuplicateRequest(shop); ShopToOrderable shopToOrderable = ShopToOrderable.builder() .shop(shop) @@ -45,25 +45,22 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque shopToOrderableRepository.save(shopToOrderable); } - private void validateCreateOrderableRequest( - Integer ownerId, - Integer shopId, - Shop shop - ) { - // 이미 신청한 내역이 있는지 확인 - if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.PENDING)) { - throw CustomException.of(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shopId); - } - + private void validateShopOwner(Shop shop, Integer ownerId) { // 가게 사장님인지 확인 if (!shop.isOwner(ownerId)) { - throw CustomException.of(ApiResponseCode.FORBIDDEN_SHOP_OWNER, - "ownerId: " + ownerId + ", shopId: " + shopId); + throw CustomException.of(FORBIDDEN_SHOP_OWNER, "ownerId: " + ownerId + ", shopId: " + shop.getId()); + } + } + + private void validateDuplicateRequest(Shop shop) { + // 이미 신청한 내역이 있는지 확인 + if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shop.getId(), PENDING)) { + throw CustomException.of(DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shop.getId()); } // 이미 주문가능 상점인지 확인 - if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shopId, ShopToOrderableRequestStatus.APPROVED)) { - throw CustomException.of(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP, "shopId: " + shopId); + if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shop.getId(), APPROVED)) { + throw CustomException.of(DUPLICATE_ORDERABLE_SHOP, "shopId: " + shop.getId()); } } } diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java index fe768048d..7424aa2ec 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java @@ -6,8 +6,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.Optional; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -80,7 +78,7 @@ class CreateOrderableRequestTest { @DisplayName("정상적으로 주문 가능 상점 신청이 생성된다") void createOrderableRequestSuccessfully() { // given - when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopRepository.getById(100)).thenReturn(shop); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(false); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, @@ -106,24 +104,11 @@ void createOrderableRequestSuccessfully() { assertThat(saved.getAccountNumber()).isEqualTo("123-456-789"); } - @Test - @DisplayName("존재하지 않는 상점 ID로 신청하면 예외가 발생한다") - void throwExceptionWhenShopNotFound() { - // given - when(shopRepository.findById(999)).thenReturn(Optional.empty()); - - // when & then - CustomException exception = assertThrows(CustomException.class, - () -> shopToOrderableService.createOrderableRequest(1, request, 999)); - - assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.NOT_FOUND_SHOP); - } - @Test @DisplayName("이미 신청한 내역이 있으면 예외가 발생한다") void throwExceptionWhenAlreadyRequested() { // given - when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopRepository.getById(100)).thenReturn(shop); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(true); @@ -138,7 +123,7 @@ void throwExceptionWhenAlreadyRequested() { @DisplayName("상점 사장님이 아닌 경우 예외가 발생한다") void throwExceptionWhenNotShopOwner() { // given - when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopRepository.getById(100)).thenReturn(shop); // when & then CustomException exception = assertThrows(CustomException.class, @@ -150,7 +135,7 @@ void throwExceptionWhenNotShopOwner() { @DisplayName("이미 승인된 상점이 있으면 예외가 발생한다") void throwExceptionWhenAlreadyApproved() { // given - when(shopRepository.findById(100)).thenReturn(Optional.of(shop)); + when(shopRepository.getById(100)).thenReturn(shop); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, ShopToOrderableRequestStatus.PENDING)).thenReturn(false); when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, From 03a277ac8c4f674dbe271f99d64744fe0b058e17 Mon Sep 17 00:00:00 2001 From: asa9874 Date: Fri, 31 Oct 2025 14:05:01 +0900 Subject: [PATCH 22/23] =?UTF-8?q?refact:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EB=AA=85=20ShopOrderServiceRequest=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ShopOrderServiceRequestApi.java} | 8 +-- .../ShopOrderServiceRequestController.java} | 15 ++--- .../dto/ShopOrderServiceRequestRequest.java} | 8 +-- .../model/ShopOrderServiceRequest.java} | 16 ++--- ...ShopOrderServiceRequestDeliveryOption.java | 7 ++ .../model/ShopOrderServiceRequestStatus.java | 5 ++ .../ShopOrderServiceRequestRepository.java | 15 +++++ .../ShopOrderServiceRequestService.java} | 24 +++---- .../model/ShopToOrderableDeliveryOption.java | 7 -- .../model/ShopToOrderableRequestStatus.java | 5 -- .../repository/ShopToOrderableRepository.java | 15 ----- .../global/config/SwaggerGroupConfig.java | 2 +- .../ShopOrderServiceRequestServiceTest.java} | 64 +++++++++---------- 13 files changed, 95 insertions(+), 96 deletions(-) rename src/main/java/in/koreatech/koin/domain/{shoptoOrderable/controller/ShopToOrderableApi.java => ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java} (84%) rename src/main/java/in/koreatech/koin/domain/{shoptoOrderable/controller/ShopToOrderableController.java => ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java} (63%) rename src/main/java/in/koreatech/koin/domain/{shoptoOrderable/dto/ShopToOrderableRequest.java => ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java} (91%) rename src/main/java/in/koreatech/koin/domain/{shoptoOrderable/model/ShopToOrderable.java => ShopOrderServiceRequest/model/ShopOrderServiceRequest.java} (86%) create mode 100644 src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java create mode 100644 src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java create mode 100644 src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java rename src/main/java/in/koreatech/koin/domain/{shoptoOrderable/service/ShopToOrderableService.java => ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java} (64%) delete mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java delete mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java delete mode 100644 src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java rename src/test/java/in/koreatech/koin/unit/domain/{ShopToOrderable/ShopToOrderableServiceTest.java => ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java} (62%) diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java similarity index 84% rename from src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java rename to src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java index a02303a95..376f37649 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableApi.java +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.shoptoOrderable.controller; +package in.koreatech.koin.domain.ShopOrderServiceRequest.controller; import static in.koreatech.koin.domain.user.model.UserType.OWNER; @@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; import in.koreatech.koin.global.auth.Auth; import in.koreatech.koin.global.code.ApiResponseCode; import in.koreatech.koin.global.code.ApiResponseCodes; @@ -18,7 +18,7 @@ import jakarta.validation.Valid; @Tag(name = "(Normal) Shop To Orderable Request: 주문 서비스 가입 요청", description = "사장님이 주문 서비스 가입을 요청하기위한 API") -public interface ShopToOrderableApi { +public interface ShopOrderServiceRequestApi { @ApiResponseCodes({ ApiResponseCode.OK, @@ -34,6 +34,6 @@ public interface ShopToOrderableApi { ResponseEntity createOrderableRequest( @Auth(permit = {OWNER}) Integer ownerId, @PathVariable Integer shopId, - @RequestBody @Valid ShopToOrderableRequest request + @RequestBody @Valid ShopOrderServiceRequestRequest request ); } diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java similarity index 63% rename from src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java rename to src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java index d86697ad8..41cd63e1d 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/controller/ShopToOrderableController.java +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.shoptoOrderable.controller; +package in.koreatech.koin.domain.ShopOrderServiceRequest.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -6,8 +6,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; -import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; -import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService; +import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.service.ShopOrderServiceRequestService; import in.koreatech.koin.global.duplicate.DuplicateGuard; import lombok.RequiredArgsConstructor; @@ -18,21 +18,20 @@ import in.koreatech.koin.global.auth.Auth; import jakarta.validation.Valid; -//Todo: ShopToOrderable 이라는 명칭은 임시임 추후 변경 @RestController @RequiredArgsConstructor -public class ShopToOrderableController implements ShopToOrderableApi { +public class ShopOrderServiceRequestController implements ShopOrderServiceRequestApi { - private final ShopToOrderableService shopToOrderableService; + private final ShopOrderServiceRequestService ShopOrderServiceRequestService; @PostMapping("/owner/shops/{shopId}/orderable-requests") @DuplicateGuard(key = "#ownerId + ':' + #shopId + ':' + #request.toString()", timeoutSeconds = 300) public ResponseEntity createOrderableRequest( @Auth(permit = {OWNER}) Integer ownerId, @PathVariable Integer shopId, - @RequestBody @Valid ShopToOrderableRequest request + @RequestBody @Valid ShopOrderServiceRequestRequest request ) { - shopToOrderableService.createOrderableRequest(ownerId, request, shopId); + ShopOrderServiceRequestService.createOrderableRequest(ownerId, request, shopId); return ResponseEntity.status(HttpStatus.CREATED).build(); } } diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java similarity index 91% rename from src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java rename to src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java index 680dc8f63..792e0ba4d 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/dto/ShopToOrderableRequest.java +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java @@ -1,11 +1,11 @@ -package in.koreatech.koin.domain.shoptoOrderable.dto; +package in.koreatech.koin.domain.ShopOrderServiceRequest.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableDeliveryOption; +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestDeliveryOption; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; @@ -13,7 +13,7 @@ import jakarta.validation.constraints.Size; @JsonNaming(SnakeCaseStrategy.class) -public record ShopToOrderableRequest( +public record ShopOrderServiceRequestRequest( @Schema(description = "최소 주문 금액", example = "5000", requiredMode = REQUIRED) @NotNull(message = "최소 주문 금액은 필수입니다.") @@ -26,7 +26,7 @@ public record ShopToOrderableRequest( @Schema(description = "배달 옵션 (CAMPUS/OUTSIDE/BOTH)", example = "BOTH", requiredMode = REQUIRED) @NotNull(message = "배달 옵션은 필수입니다.") - ShopToOrderableDeliveryOption deliveryOption, + ShopOrderServiceRequestDeliveryOption deliveryOption, @Schema(description = "교내 배달팁", example = "1000", requiredMode = REQUIRED) @NotNull(message = "교내 배달팁은 필수입니다.") diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequest.java similarity index 86% rename from src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java rename to src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequest.java index 655d7e9f5..44307e408 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderable.java +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.shoptoOrderable.model; +package in.koreatech.koin.domain.ShopOrderServiceRequest.model; import static lombok.AccessLevel.PROTECTED; @@ -28,7 +28,7 @@ @Getter @Table(name = "shop_to_orderable") @NoArgsConstructor(access = PROTECTED) -public class ShopToOrderable extends BaseEntity { +public class ShopOrderServiceRequest extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -49,7 +49,7 @@ public class ShopToOrderable extends BaseEntity { @NotNull @Enumerated(EnumType.STRING) @Column(name = "delivery_option", nullable = false) - private ShopToOrderableDeliveryOption deliveryOption; + private ShopOrderServiceRequestDeliveryOption deliveryOption; @Column(name = "campus_delivery_tip", nullable = false) private Integer campusDeliveryTip = 0; @@ -81,17 +81,17 @@ public class ShopToOrderable extends BaseEntity { @Enumerated(EnumType.STRING) @Column(name = "request_status", nullable = false) - private ShopToOrderableRequestStatus requestStatus = ShopToOrderableRequestStatus.PENDING; + private ShopOrderServiceRequestStatus requestStatus = ShopOrderServiceRequestStatus.PENDING; @Column(name = "approved_at", columnDefinition = "TIMESTAMP") private LocalDateTime approvedAt = null; @Builder - public ShopToOrderable( + public ShopOrderServiceRequest( Shop shop, Integer minimumOrderAmount, Boolean isTakeout, - ShopToOrderableDeliveryOption deliveryOption, + ShopOrderServiceRequestDeliveryOption deliveryOption, Integer campusDeliveryTip, Integer offCampusDeliveryTip, String businessLicenseUrl, @@ -115,11 +115,11 @@ public ShopToOrderable( } public void approveRequest() { - this.requestStatus = ShopToOrderableRequestStatus.APPROVED; + this.requestStatus = ShopOrderServiceRequestStatus.APPROVED; this.approvedAt = LocalDateTime.now(); } public void rejectRequest() { - this.requestStatus = ShopToOrderableRequestStatus.REJECTED; + this.requestStatus = ShopOrderServiceRequestStatus.REJECTED; } } diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java new file mode 100644 index 000000000..d21d55b7b --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java @@ -0,0 +1,7 @@ +package in.koreatech.koin.domain.ShopOrderServiceRequest.model; + +public enum ShopOrderServiceRequestDeliveryOption { + CAMPUS, + OUTSIDE, + BOTH +} diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java new file mode 100644 index 000000000..fbd569ab8 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java @@ -0,0 +1,5 @@ +package in.koreatech.koin.domain.ShopOrderServiceRequest.model; + +public enum ShopOrderServiceRequestStatus { + PENDING, APPROVED, REJECTED +} diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java new file mode 100644 index 000000000..e60d4f2d3 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java @@ -0,0 +1,15 @@ +package in.koreatech.koin.domain.ShopOrderServiceRequest.repository; + +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestStatus; + +import org.springframework.data.repository.Repository; + +public interface ShopOrderServiceRequestRepository extends Repository { + + ShopOrderServiceRequest save(ShopOrderServiceRequest ShopOrderServiceRequest); + + boolean existsByShopId(Integer shopId); + + boolean existsByShopIdAndRequestStatus(Integer shopId, ShopOrderServiceRequestStatus requestStatus); +} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java similarity index 64% rename from src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java rename to src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java index 29dcc911e..f8f0e183d 100644 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/service/ShopToOrderableService.java +++ b/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java @@ -1,34 +1,34 @@ -package in.koreatech.koin.domain.shoptoOrderable.service; +package in.koreatech.koin.domain.ShopOrderServiceRequest.service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.domain.shop.model.shop.Shop; import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; -import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; -import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; +import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.repository.ShopOrderServiceRequestRepository; import in.koreatech.koin.global.exception.CustomException; import lombok.RequiredArgsConstructor; -import static in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus.*; +import static in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestStatus.*; import static in.koreatech.koin.global.code.ApiResponseCode.*; @Service @RequiredArgsConstructor -public class ShopToOrderableService { +public class ShopOrderServiceRequestService { - private final ShopToOrderableRepository shopToOrderableRepository; + private final ShopOrderServiceRequestRepository ShopOrderServiceRequestRepository; private final ShopRepository shopRepository; @Transactional - public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest request, Integer shopId) { + public void createOrderableRequest(Integer ownerId, ShopOrderServiceRequestRequest request, Integer shopId) { Shop shop = shopRepository.getById(shopId); validateShopOwner(shop, ownerId); validateDuplicateRequest(shop); - ShopToOrderable shopToOrderable = ShopToOrderable.builder() + ShopOrderServiceRequest shopOrderServiceRequest = ShopOrderServiceRequest.builder() .shop(shop) .minimumOrderAmount(request.minimumOrderAmount()) .isTakeout(request.isTakeout()) @@ -42,7 +42,7 @@ public void createOrderableRequest(Integer ownerId, ShopToOrderableRequest reque .accountNumber(request.accountNumber()) .build(); - shopToOrderableRepository.save(shopToOrderable); + ShopOrderServiceRequestRepository.save(shopOrderServiceRequest); } private void validateShopOwner(Shop shop, Integer ownerId) { @@ -54,12 +54,12 @@ private void validateShopOwner(Shop shop, Integer ownerId) { private void validateDuplicateRequest(Shop shop) { // 이미 신청한 내역이 있는지 확인 - if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shop.getId(), PENDING)) { + if (ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(shop.getId(), PENDING)) { throw CustomException.of(DUPLICATE_REQUESTED_ORDERABLE_SHOP, "shopId: " + shop.getId()); } // 이미 주문가능 상점인지 확인 - if (shopToOrderableRepository.existsByShopIdAndRequestStatus(shop.getId(), APPROVED)) { + if (ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(shop.getId(), APPROVED)) { throw CustomException.of(DUPLICATE_ORDERABLE_SHOP, "shopId: " + shop.getId()); } } diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java deleted file mode 100644 index 5a5ab7ccd..000000000 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableDeliveryOption.java +++ /dev/null @@ -1,7 +0,0 @@ -package in.koreatech.koin.domain.shoptoOrderable.model; - -public enum ShopToOrderableDeliveryOption { - CAMPUS, - OUTSIDE, - BOTH -} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java deleted file mode 100644 index df4f37e56..000000000 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/model/ShopToOrderableRequestStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package in.koreatech.koin.domain.shoptoOrderable.model; - -public enum ShopToOrderableRequestStatus { - PENDING, APPROVED, REJECTED -} diff --git a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java b/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java deleted file mode 100644 index 1075050ce..000000000 --- a/src/main/java/in/koreatech/koin/domain/shoptoOrderable/repository/ShopToOrderableRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package in.koreatech.koin.domain.shoptoOrderable.repository; - -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; - -import org.springframework.data.repository.Repository; - -public interface ShopToOrderableRepository extends Repository { - - ShopToOrderable save(ShopToOrderable shopToOrderable); - - boolean existsByShopId(Integer shopId); - - boolean existsByShopIdAndRequestStatus(Integer shopId, ShopToOrderableRequestStatus requestStatus); -} diff --git a/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java b/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java index caf9603fb..857f58f6f 100644 --- a/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java +++ b/src/main/java/in/koreatech/koin/global/config/SwaggerGroupConfig.java @@ -41,7 +41,7 @@ public GroupedOpenApi businessApi() { "in.koreatech.koin.domain.ownershop", "in.koreatech.koin.domain.shop", "in.koreatech.koin.domain.land", - "in.koreatech.koin.domain.shoptoOrderable", + "in.koreatech.koin.domain.ShopOrderServiceRequest", }); } diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java similarity index 62% rename from src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java rename to src/test/java/in/koreatech/koin/unit/domain/ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java index 7424aa2ec..eecc53ffa 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopToOrderable/ShopToOrderableServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.unit.domain.ShopToOrderable; +package in.koreatech.koin.unit.domain.ShopOrderServiceRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -18,12 +18,12 @@ import in.koreatech.koin.domain.shop.model.shop.Shop; import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; -import in.koreatech.koin.domain.shoptoOrderable.dto.ShopToOrderableRequest; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderable; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableDeliveryOption; -import in.koreatech.koin.domain.shoptoOrderable.model.ShopToOrderableRequestStatus; -import in.koreatech.koin.domain.shoptoOrderable.repository.ShopToOrderableRepository; -import in.koreatech.koin.domain.shoptoOrderable.service.ShopToOrderableService; +import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequest; +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestDeliveryOption; +import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestStatus; +import in.koreatech.koin.domain.ShopOrderServiceRequest.repository.ShopOrderServiceRequestRepository; +import in.koreatech.koin.domain.ShopOrderServiceRequest.service.ShopOrderServiceRequestService; import in.koreatech.koin.domain.owner.model.Owner; import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.global.code.ApiResponseCode; @@ -34,13 +34,13 @@ import org.springframework.test.util.ReflectionTestUtils; @ExtendWith(MockitoExtension.class) -class ShopToOrderableServiceTest { +class ShopOrderServiceRequestServiceTest { @InjectMocks - private ShopToOrderableService shopToOrderableService; + private ShopOrderServiceRequestService ShopOrderServiceRequestService; @Mock - private ShopToOrderableRepository shopToOrderableRepository; + private ShopOrderServiceRequestRepository ShopOrderServiceRequestRepository; @Mock private ShopRepository shopRepository; @@ -48,7 +48,7 @@ class ShopToOrderableServiceTest { private Owner owner; private User user; private Shop shop; - private ShopToOrderableRequest request; + private ShopOrderServiceRequestRequest request; @BeforeEach void setUp() { @@ -56,10 +56,10 @@ void setUp() { ReflectionTestUtils.setField(owner, "id", 1); shop = ShopFixture.주문전환_이전_상점(owner); - request = new ShopToOrderableRequest( + request = new ShopOrderServiceRequestRequest( 5000, true, - ShopToOrderableDeliveryOption.BOTH, + ShopOrderServiceRequestDeliveryOption.BOTH, 1000, 2000, "https://example.com/business_license.jpg", @@ -79,25 +79,25 @@ class CreateOrderableRequestTest { void createOrderableRequestSuccessfully() { // given when(shopRepository.getById(100)).thenReturn(shop); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, - ShopToOrderableRequestStatus.PENDING)).thenReturn(false); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, - ShopToOrderableRequestStatus.APPROVED)).thenReturn(false); - when(shopToOrderableRepository.save(any(ShopToOrderable.class))).thenAnswer( + when(ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(100, + ShopOrderServiceRequestStatus.PENDING)).thenReturn(false); + when(ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(100, + ShopOrderServiceRequestStatus.APPROVED)).thenReturn(false); + when(ShopOrderServiceRequestRepository.save(any(ShopOrderServiceRequest.class))).thenAnswer( invocation -> invocation.getArgument(0)); // when - shopToOrderableService.createOrderableRequest(1, request, 100); + ShopOrderServiceRequestService.createOrderableRequest(1, request, 100); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(ShopToOrderable.class); - verify(shopToOrderableRepository).save(captor.capture()); + ArgumentCaptor captor = ArgumentCaptor.forClass(ShopOrderServiceRequest.class); + verify(ShopOrderServiceRequestRepository).save(captor.capture()); - ShopToOrderable saved = captor.getValue(); + ShopOrderServiceRequest saved = captor.getValue(); assertThat(saved.getShop()).isEqualTo(shop); assertThat(saved.getMinimumOrderAmount()).isEqualTo(5000); assertThat(saved.getIsTakeout()).isTrue(); - assertThat(saved.getDeliveryOption()).isEqualTo(ShopToOrderableDeliveryOption.BOTH); + assertThat(saved.getDeliveryOption()).isEqualTo(ShopOrderServiceRequestDeliveryOption.BOTH); assertThat(saved.getCampusDeliveryTip()).isEqualTo(1000); assertThat(saved.getOffCampusDeliveryTip()).isEqualTo(2000); assertThat(saved.getBank()).isEqualTo("국민은행"); @@ -109,12 +109,12 @@ void createOrderableRequestSuccessfully() { void throwExceptionWhenAlreadyRequested() { // given when(shopRepository.getById(100)).thenReturn(shop); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, - ShopToOrderableRequestStatus.PENDING)).thenReturn(true); + when(ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(100, + ShopOrderServiceRequestStatus.PENDING)).thenReturn(true); // when & then CustomException exception = assertThrows(CustomException.class, - () -> shopToOrderableService.createOrderableRequest(1, request, 100)); + () -> ShopOrderServiceRequestService.createOrderableRequest(1, request, 100)); assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.DUPLICATE_REQUESTED_ORDERABLE_SHOP); } @@ -127,7 +127,7 @@ void throwExceptionWhenNotShopOwner() { // when & then CustomException exception = assertThrows(CustomException.class, - () -> shopToOrderableService.createOrderableRequest(2, request, 100)); + () -> ShopOrderServiceRequestService.createOrderableRequest(2, request, 100)); assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.FORBIDDEN_SHOP_OWNER); } @@ -136,14 +136,14 @@ void throwExceptionWhenNotShopOwner() { void throwExceptionWhenAlreadyApproved() { // given when(shopRepository.getById(100)).thenReturn(shop); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, - ShopToOrderableRequestStatus.PENDING)).thenReturn(false); - when(shopToOrderableRepository.existsByShopIdAndRequestStatus(100, - ShopToOrderableRequestStatus.APPROVED)).thenReturn(true); + when(ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(100, + ShopOrderServiceRequestStatus.PENDING)).thenReturn(false); + when(ShopOrderServiceRequestRepository.existsByShopIdAndRequestStatus(100, + ShopOrderServiceRequestStatus.APPROVED)).thenReturn(true); // when & then CustomException exception = assertThrows(CustomException.class, - () -> shopToOrderableService.createOrderableRequest(1, request, 100)); + () -> ShopOrderServiceRequestService.createOrderableRequest(1, request, 100)); assertThat(exception.getErrorCode()).isEqualTo(ApiResponseCode.DUPLICATE_ORDERABLE_SHOP); } } From 3fce76b049f6e5b6adf8dfa2760bd1082d007644 Mon Sep 17 00:00:00 2001 From: asa9874 Date: Fri, 31 Oct 2025 14:10:57 +0900 Subject: [PATCH 23/23] =?UTF-8?q?refact:=20ownershop=20=ED=95=98=EC=9C=84?= =?UTF-8?q?=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ShopOrderServiceRequestApi.java | 4 ++-- .../ShopOrderServiceRequestController.java | 6 +++--- .../dto/ShopOrderServiceRequestRequest.java | 4 ++-- .../model/ShopOrderServiceRequest.java | 2 +- .../ShopOrderServiceRequestDeliveryOption.java | 2 +- .../model/ShopOrderServiceRequestStatus.java | 2 +- .../ShopOrderServiceRequestRepository.java | 6 +++--- .../service/ShopOrderServiceRequestService.java | 10 +++++----- .../ShopOrderServiceRequestServiceTest.java | 14 +++++++------- 9 files changed, 25 insertions(+), 25 deletions(-) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/controller/ShopOrderServiceRequestApi.java (91%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/controller/ShopOrderServiceRequestController.java (83%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/dto/ShopOrderServiceRequestRequest.java (94%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/model/ShopOrderServiceRequest.java (98%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/model/ShopOrderServiceRequestDeliveryOption.java (58%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/model/ShopOrderServiceRequestStatus.java (55%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/repository/ShopOrderServiceRequestRepository.java (61%) rename src/main/java/in/koreatech/koin/domain/{ShopOrderServiceRequest => ownershop}/service/ShopOrderServiceRequestService.java (84%) rename src/test/java/in/koreatech/koin/unit/domain/{ShopOrderServiceRequest => ownershop}/ShopOrderServiceRequestServiceTest.java (90%) diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java b/src/main/java/in/koreatech/koin/domain/ownershop/controller/ShopOrderServiceRequestApi.java similarity index 91% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java rename to src/main/java/in/koreatech/koin/domain/ownershop/controller/ShopOrderServiceRequestApi.java index 376f37649..cdc1a9e80 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestApi.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/controller/ShopOrderServiceRequestApi.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.controller; +package in.koreatech.koin.domain.ownershop.controller; import static in.koreatech.koin.domain.user.model.UserType.OWNER; @@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ownershop.dto.ShopOrderServiceRequestRequest; import in.koreatech.koin.global.auth.Auth; import in.koreatech.koin.global.code.ApiResponseCode; import in.koreatech.koin.global.code.ApiResponseCodes; diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java b/src/main/java/in/koreatech/koin/domain/ownershop/controller/ShopOrderServiceRequestController.java similarity index 83% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java rename to src/main/java/in/koreatech/koin/domain/ownershop/controller/ShopOrderServiceRequestController.java index 41cd63e1d..b3ba5f388 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/controller/ShopOrderServiceRequestController.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/controller/ShopOrderServiceRequestController.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.controller; +package in.koreatech.koin.domain.ownershop.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -6,8 +6,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; -import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; -import in.koreatech.koin.domain.ShopOrderServiceRequest.service.ShopOrderServiceRequestService; +import in.koreatech.koin.domain.ownershop.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ownershop.service.ShopOrderServiceRequestService; import in.koreatech.koin.global.duplicate.DuplicateGuard; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java b/src/main/java/in/koreatech/koin/domain/ownershop/dto/ShopOrderServiceRequestRequest.java similarity index 94% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java rename to src/main/java/in/koreatech/koin/domain/ownershop/dto/ShopOrderServiceRequestRequest.java index 792e0ba4d..fb177cc08 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/dto/ShopOrderServiceRequestRequest.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/dto/ShopOrderServiceRequestRequest.java @@ -1,11 +1,11 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.dto; +package in.koreatech.koin.domain.ownershop.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestDeliveryOption; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequestDeliveryOption; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequest.java b/src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequest.java similarity index 98% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequest.java rename to src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequest.java index 44307e408..535b1f965 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequest.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.model; +package in.koreatech.koin.domain.ownershop.model; import static lombok.AccessLevel.PROTECTED; diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java b/src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequestDeliveryOption.java similarity index 58% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java rename to src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequestDeliveryOption.java index d21d55b7b..af0cc7336 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestDeliveryOption.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequestDeliveryOption.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.model; +package in.koreatech.koin.domain.ownershop.model; public enum ShopOrderServiceRequestDeliveryOption { CAMPUS, diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java b/src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequestStatus.java similarity index 55% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java rename to src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequestStatus.java index fbd569ab8..cace70f3d 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/model/ShopOrderServiceRequestStatus.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/model/ShopOrderServiceRequestStatus.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.model; +package in.koreatech.koin.domain.ownershop.model; public enum ShopOrderServiceRequestStatus { PENDING, APPROVED, REJECTED diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java b/src/main/java/in/koreatech/koin/domain/ownershop/repository/ShopOrderServiceRequestRepository.java similarity index 61% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java rename to src/main/java/in/koreatech/koin/domain/ownershop/repository/ShopOrderServiceRequestRepository.java index e60d4f2d3..b1bf19ad0 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/repository/ShopOrderServiceRequestRepository.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/repository/ShopOrderServiceRequestRepository.java @@ -1,7 +1,7 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.repository; +package in.koreatech.koin.domain.ownershop.repository; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequest; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestStatus; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequest; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequestStatus; import org.springframework.data.repository.Repository; diff --git a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java b/src/main/java/in/koreatech/koin/domain/ownershop/service/ShopOrderServiceRequestService.java similarity index 84% rename from src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java rename to src/main/java/in/koreatech/koin/domain/ownershop/service/ShopOrderServiceRequestService.java index f8f0e183d..c42f2cda0 100644 --- a/src/main/java/in/koreatech/koin/domain/ShopOrderServiceRequest/service/ShopOrderServiceRequestService.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/service/ShopOrderServiceRequestService.java @@ -1,17 +1,17 @@ -package in.koreatech.koin.domain.ShopOrderServiceRequest.service; +package in.koreatech.koin.domain.ownershop.service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.domain.shop.model.shop.Shop; import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; -import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequest; -import in.koreatech.koin.domain.ShopOrderServiceRequest.repository.ShopOrderServiceRequestRepository; +import in.koreatech.koin.domain.ownershop.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequest; +import in.koreatech.koin.domain.ownershop.repository.ShopOrderServiceRequestRepository; import in.koreatech.koin.global.exception.CustomException; import lombok.RequiredArgsConstructor; -import static in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestStatus.*; +import static in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequestStatus.*; import static in.koreatech.koin.global.code.ApiResponseCode.*; @Service diff --git a/src/test/java/in/koreatech/koin/unit/domain/ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/ownershop/ShopOrderServiceRequestServiceTest.java similarity index 90% rename from src/test/java/in/koreatech/koin/unit/domain/ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java rename to src/test/java/in/koreatech/koin/unit/domain/ownershop/ShopOrderServiceRequestServiceTest.java index eecc53ffa..e7a15a84e 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/ShopOrderServiceRequest/ShopOrderServiceRequestServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/ownershop/ShopOrderServiceRequestServiceTest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.unit.domain.ShopOrderServiceRequest; +package in.koreatech.koin.unit.domain.ownershop; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -18,12 +18,12 @@ import in.koreatech.koin.domain.shop.model.shop.Shop; import in.koreatech.koin.domain.shop.repository.shop.ShopRepository; -import in.koreatech.koin.domain.ShopOrderServiceRequest.dto.ShopOrderServiceRequestRequest; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequest; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestDeliveryOption; -import in.koreatech.koin.domain.ShopOrderServiceRequest.model.ShopOrderServiceRequestStatus; -import in.koreatech.koin.domain.ShopOrderServiceRequest.repository.ShopOrderServiceRequestRepository; -import in.koreatech.koin.domain.ShopOrderServiceRequest.service.ShopOrderServiceRequestService; +import in.koreatech.koin.domain.ownershop.dto.ShopOrderServiceRequestRequest; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequest; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequestDeliveryOption; +import in.koreatech.koin.domain.ownershop.model.ShopOrderServiceRequestStatus; +import in.koreatech.koin.domain.ownershop.repository.ShopOrderServiceRequestRepository; +import in.koreatech.koin.domain.ownershop.service.ShopOrderServiceRequestService; import in.koreatech.koin.domain.owner.model.Owner; import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.global.code.ApiResponseCode;