From 2eb51f7f80461bc455835913622d3675fc16a568 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 16:18:23 +0900 Subject: [PATCH 01/15] =?UTF-8?q?[UPLUS-57]=20=ED=95=A0=EC=9D=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=A0=9C=EA=B1=B0=20=EA=B5=AC=ED=98=84(CO?= =?UTF-8?q?NTROLLER=20=EA=B5=AC=ED=98=84=EC=A4=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/controller/CustomerController.java | 55 +++++++++-- .../infra/entity/discount/DiscountPolicy.java | 54 ++++++++++ .../entity/discount/SubscriptionDiscount.java | 98 +++++++++++++++++++ .../infra/entity/discount/enums/Active.java | 6 ++ .../infra/entity/discount/enums/Category.java | 7 ++ .../entity/discount/enums/DiscountType.java | 6 ++ .../infra/entity/discount/enums/Status.java | 6 ++ .../entity/discount/enums/TargetScope.java | 6 ++ .../entity/subscription/Subscription.java | 9 +- .../customer/CustomerRepository.java | 3 +- .../discount/DiscountPolicyRepository.java | 9 ++ .../SubscriptionDiscountRepository.java | 13 +++ .../subscription/SubscriptionRepository.java | 5 + .../project/core/service/CustomerService.java | 23 +++-- .../project/core/service/DiscountService.java | 79 +++++++++++++++ .../core/service/SubscriptionService.java | 40 ++++++++ 16 files changed, 403 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java create mode 100644 src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java create mode 100644 src/main/java/com/project/core/infra/entity/discount/enums/Active.java create mode 100644 src/main/java/com/project/core/infra/entity/discount/enums/Category.java create mode 100644 src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java create mode 100644 src/main/java/com/project/core/infra/entity/discount/enums/Status.java create mode 100644 src/main/java/com/project/core/infra/entity/discount/enums/TargetScope.java create mode 100644 src/main/java/com/project/core/infra/repository/discount/DiscountPolicyRepository.java create mode 100644 src/main/java/com/project/core/infra/repository/discount/SubscriptionDiscountRepository.java create mode 100644 src/main/java/com/project/core/service/DiscountService.java create mode 100644 src/main/java/com/project/core/service/SubscriptionService.java diff --git a/src/main/java/com/project/core/controller/CustomerController.java b/src/main/java/com/project/core/controller/CustomerController.java index c94fa9d..5e1dd14 100644 --- a/src/main/java/com/project/core/controller/CustomerController.java +++ b/src/main/java/com/project/core/controller/CustomerController.java @@ -1,11 +1,20 @@ package com.project.core.controller; +import java.util.List; + +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.project.core.controller.dto.request.FindCustomerRequest; +import com.project.core.controller.dto.request.ChangeEmailRequest; +import com.project.core.controller.dto.request.ChangeGradeRequest; +import com.project.core.controller.dto.response.ChangeEmailResponse; +import com.project.core.controller.dto.response.ChangeGradeResponse; import com.project.core.infra.entity.customer.Customer; import com.project.core.service.CustomerService; @@ -15,12 +24,44 @@ @RequestMapping("/user") @RequiredArgsConstructor public class CustomerController { - private final CustomerService customerService; + private final CustomerService customerService; + + /** + * 고객 조회 (전화번호 기준) + */ + @GetMapping + public ResponseEntity> loadByContactEnc( + @RequestParam String contactEnc + ) { + List customers = customerService.loadByContactEnc(contactEnc); + return ResponseEntity.ok(customers); + } + + /** + * 이메일 변경 + * @throws Exception + */ + @PostMapping("/{userId}/email") + public ResponseEntity changeEmail( + @PathVariable Long userId, + @RequestBody ChangeEmailRequest request + ) throws Exception { + ChangeEmailResponse response = + customerService.changeEmailEnc(userId, request); + return ResponseEntity.ok(response); + } - @GetMapping - public Long save(@RequestBody FindCustomerRequest request) { - Customer customer = customerService.loadByContactEnc(request.contactEnc()); - return customer.getCustomerId(); - } + /** + * 고객 등급 변경 + */ + @PostMapping("/{userId}/grade") + public ResponseEntity changeGrade( + @PathVariable Long userId, + @RequestBody ChangeGradeRequest request + ) { + ChangeGradeResponse response = + customerService.changeUserGrade(userId, request); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java b/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java new file mode 100644 index 0000000..b0b3b97 --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java @@ -0,0 +1,54 @@ +package com.project.core.infra.entity.discount; + +import java.util.ArrayList; +import java.util.List; + +import com.project.core.infra.entity.discount.enums.Active; +import com.project.core.infra.entity.discount.enums.Category; +import com.project.core.infra.entity.discount.enums.DiscountType; +import com.project.core.infra.entity.discount.enums.TargetScope; +import com.project.core.infra.entity.vas.SubscriptionVas; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "discount_policy") +public class DiscountPolicy { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "discount_id") + private Long discountId; + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "discount_type", nullable = false) + private DiscountType discountType; + + @Column(name = "value", nullable = false) + private Double value; + + @Column(name = "category", nullable = false) + private Category category; + + @Column(name = "target_scope", nullable = false) + private TargetScope targetScope; + + @Column(name = "active", nullable = false) + private Active active; +//------------------------------------------------------------------ + @OneToMany(mappedBy = "discount_policy", cascade = CascadeType.ALL) + private List discountHistory = new ArrayList<>(); +} diff --git a/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java b/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java new file mode 100644 index 0000000..1032222 --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java @@ -0,0 +1,98 @@ +package com.project.core.infra.entity.discount; + +import java.time.LocalDateTime; + +import com.project.core.infra.entity.discount.enums.Active; +import com.project.core.infra.entity.discount.enums.Category; +import com.project.core.infra.entity.discount.enums.DiscountType; +import com.project.core.infra.entity.discount.enums.Status; +import com.project.core.infra.entity.discount.enums.TargetScope; +import com.project.core.infra.entity.plan.Plan; +import com.project.core.infra.entity.plan.SubscriptionPlan; +import com.project.core.infra.entity.subscription.Subscription; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "subscription_discount") +public class SubscriptionDiscount { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "sd_id") + private Long sdId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "discount_id", nullable = false) + private DiscountPolicy discountPolicy; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "sub_id", nullable = false) + private Subscription subscription; + + @Column(name = "discount_type", nullable = false) + private DiscountType discountType; + + @Column(name = "value", nullable = false) + private Double value; + + @Column(name = "target_scope", nullable = false) + private TargetScope targetScope; + + @Column(name = "start_date", nullable = false) + private LocalDateTime startDate; + + @Column(name = "end_date") + private LocalDateTime endDate; + + @Column(name = "status", nullable = false) + private Status status; + + public void setEndDate(LocalDateTime endDate) { + this.endDate = endDate; + } + public void setStatusTerminated() { + this.status = Status.TERMINATED; + } + + @Builder + private SubscriptionDiscount( + DiscountPolicy discountPolicy, + Subscription subscription, + DiscountType discountType, + Double value, + TargetScope targetScope, + LocalDateTime startDate + ) { + if (discountPolicy == null) throw new IllegalArgumentException("discountPolicy는 필수입니다."); + if (subscription == null) throw new IllegalArgumentException("subscription은 필수입니다."); + if (discountType == null) throw new IllegalArgumentException("discountType는 필수입니다."); + if (value == null) throw new IllegalArgumentException("value는 필수입니다."); + if (targetScope == null) throw new IllegalArgumentException("targetScope는 필수입니다."); + + this.discountPolicy = discountPolicy; + this.subscription = subscription; + this.discountType = discountType; + this.value = value; + this.targetScope = targetScope; + this.startDate = startDate != null ? startDate : LocalDateTime.now(); + + // 기본값 + this.endDate = null; + this.status = Status.ACTIVE; + } +} diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/Active.java b/src/main/java/com/project/core/infra/entity/discount/enums/Active.java new file mode 100644 index 0000000..8e98ff0 --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/enums/Active.java @@ -0,0 +1,6 @@ +package com.project.core.infra.entity.discount.enums; + +public enum Active { + ACTIVE + ,INACTIVE +} diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/Category.java b/src/main/java/com/project/core/infra/entity/discount/enums/Category.java new file mode 100644 index 0000000..5439a78 --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/enums/Category.java @@ -0,0 +1,7 @@ +package com.project.core.infra.entity.discount.enums; + +public enum Category { + 복지 + ,프로모션 + ,결합 +} diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java b/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java new file mode 100644 index 0000000..aad9fc1 --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java @@ -0,0 +1,6 @@ +package com.project.core.infra.entity.discount.enums; + +public enum DiscountType { + Rate + ,Fixed +} diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/Status.java b/src/main/java/com/project/core/infra/entity/discount/enums/Status.java new file mode 100644 index 0000000..7d193ba --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/enums/Status.java @@ -0,0 +1,6 @@ +package com.project.core.infra.entity.discount.enums; + +public enum Status { + ACTIVE + ,TERMINATED +} diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/TargetScope.java b/src/main/java/com/project/core/infra/entity/discount/enums/TargetScope.java new file mode 100644 index 0000000..341e477 --- /dev/null +++ b/src/main/java/com/project/core/infra/entity/discount/enums/TargetScope.java @@ -0,0 +1,6 @@ +package com.project.core.infra.entity.discount.enums; + +public enum TargetScope { + PLAN_FEE + ,TOTAL_AMOUNT +} diff --git a/src/main/java/com/project/core/infra/entity/subscription/Subscription.java b/src/main/java/com/project/core/infra/entity/subscription/Subscription.java index 0c6fc6b..d9de4e0 100644 --- a/src/main/java/com/project/core/infra/entity/subscription/Subscription.java +++ b/src/main/java/com/project/core/infra/entity/subscription/Subscription.java @@ -1,6 +1,7 @@ package com.project.core.infra.entity.subscription; import com.project.core.infra.entity.customer.Customer; +import com.project.core.infra.entity.discount.SubscriptionDiscount; import com.project.core.infra.entity.plan.SubscriptionPlan; import com.project.core.infra.entity.subscription.enums.SubscriptionStatus; import com.project.core.infra.entity.vas.SubscriptionVas; @@ -57,7 +58,9 @@ public class Subscription { private List vasHistory = new ArrayList<>(); // 할인 이력 (1:N) - + @OneToMany(mappedBy = "subscription", cascade = CascadeType.ALL) + private List discountHistory = new ArrayList<>(); + @Builder public Subscription(Customer customer, String phoneNumber) { this.customer = customer; @@ -72,4 +75,8 @@ public void terminate() { this.status = SubscriptionStatus.TERMINATED; this.endDate = LocalDateTime.now(); } + //전화번호 마스킹된 버전 삽입 + public void setPhoneNumber(String maskedNum) { + this.phoneNumber = maskedNum; + } } diff --git a/src/main/java/com/project/core/infra/repository/customer/CustomerRepository.java b/src/main/java/com/project/core/infra/repository/customer/CustomerRepository.java index 45f604c..d6975f5 100644 --- a/src/main/java/com/project/core/infra/repository/customer/CustomerRepository.java +++ b/src/main/java/com/project/core/infra/repository/customer/CustomerRepository.java @@ -1,5 +1,6 @@ package com.project.core.infra.repository.customer; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -8,5 +9,5 @@ public interface CustomerRepository extends JpaRepository { - Optional findByContactEnc(String contactEnc); + List findByContactEnc(String contactEnc); } diff --git a/src/main/java/com/project/core/infra/repository/discount/DiscountPolicyRepository.java b/src/main/java/com/project/core/infra/repository/discount/DiscountPolicyRepository.java new file mode 100644 index 0000000..962e7a1 --- /dev/null +++ b/src/main/java/com/project/core/infra/repository/discount/DiscountPolicyRepository.java @@ -0,0 +1,9 @@ +package com.project.core.infra.repository.discount; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.project.core.infra.entity.discount.DiscountPolicy; + +public interface DiscountPolicyRepository extends JpaRepository { + +} diff --git a/src/main/java/com/project/core/infra/repository/discount/SubscriptionDiscountRepository.java b/src/main/java/com/project/core/infra/repository/discount/SubscriptionDiscountRepository.java new file mode 100644 index 0000000..de12366 --- /dev/null +++ b/src/main/java/com/project/core/infra/repository/discount/SubscriptionDiscountRepository.java @@ -0,0 +1,13 @@ +package com.project.core.infra.repository.discount; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.project.core.infra.entity.discount.SubscriptionDiscount; + +public interface SubscriptionDiscountRepository extends JpaRepository { + List findBySubId(Long subId); + Optional findBySdId(Long sdId); +} diff --git a/src/main/java/com/project/core/infra/repository/subscription/SubscriptionRepository.java b/src/main/java/com/project/core/infra/repository/subscription/SubscriptionRepository.java index f85dcb9..2f89474 100644 --- a/src/main/java/com/project/core/infra/repository/subscription/SubscriptionRepository.java +++ b/src/main/java/com/project/core/infra/repository/subscription/SubscriptionRepository.java @@ -1,12 +1,17 @@ package com.project.core.infra.repository.subscription; import com.project.core.infra.entity.customer.Customer; +import com.project.core.infra.entity.discount.SubscriptionDiscount; import com.project.core.infra.entity.subscription.Subscription; import com.project.core.infra.entity.subscription.enums.SubscriptionStatus; + +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; public interface SubscriptionRepository extends JpaRepository { long countByCustomerAndStatus(Customer customer, SubscriptionStatus status); boolean existsByPhoneNumberAndStatus(String phoneNumber, SubscriptionStatus status); boolean existsByPhoneNumber(String phoneNumber); + List findByCostomerId(Long customerId); } diff --git a/src/main/java/com/project/core/service/CustomerService.java b/src/main/java/com/project/core/service/CustomerService.java index 3e0d5c7..1cf42e5 100644 --- a/src/main/java/com/project/core/service/CustomerService.java +++ b/src/main/java/com/project/core/service/CustomerService.java @@ -1,5 +1,7 @@ package com.project.core.service; +import java.util.List; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -8,29 +10,36 @@ import com.project.core.controller.dto.response.ChangeEmailResponse; import com.project.core.controller.dto.response.ChangeGradeResponse; import com.project.core.infra.entity.customer.Customer; +import com.project.core.infra.entity.subscription.Subscription; import com.project.core.infra.repository.customer.CustomerRepository; +import com.project.global.util.AESUtil; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class CustomerService { private final CustomerRepository customerRepository; + private final AESUtil aesUtil; @Transactional - public Customer loadByContactEnc(String contactEnc) { - return customerRepository.findByContactEnc(contactEnc).orElseThrow(()->new IllegalArgumentException("사용자를 찾을 수 없습니다")); + public List loadByContactEnc(String contactEnc) {//유저 조회 + List customers = customerRepository.findByContactEnc(contactEnc); + if(customers.isEmpty()) { + throw new IllegalStateException("해당하는 사용자가 없습니다"); + } + return customers; } @Transactional - public ChangeEmailResponse changeEmailEnc(Long userId, ChangeEmailRequest request) { + public ChangeEmailResponse changeEmailEnc(Long userId, ChangeEmailRequest request) throws Exception {//이메일 변경 Customer customer = customerRepository.findById(userId).orElseThrow(()->new IllegalArgumentException("사용자를 찾을 수 없습니다")); - - customer.changeEmailEnc(request.emailEnc()); - return new ChangeEmailResponse(customer.getEmailEnc()); + String encEmail = aesUtil.encrypt(request.emailEnc()); + customer.changeEmailEnc(encEmail); + return new ChangeEmailResponse(encEmail); } @Transactional - public ChangeGradeResponse changeUserGrade(Long userId, ChangeGradeRequest request) { + public ChangeGradeResponse changeUserGrade(Long userId, ChangeGradeRequest request) {//등급 변경 Customer customer = customerRepository.findById(userId).orElseThrow(()->new IllegalArgumentException("사용자를 찾을 수 없습니다")); customer.changeGrade(request.grade()); diff --git a/src/main/java/com/project/core/service/DiscountService.java b/src/main/java/com/project/core/service/DiscountService.java new file mode 100644 index 0000000..d8b0eaf --- /dev/null +++ b/src/main/java/com/project/core/service/DiscountService.java @@ -0,0 +1,79 @@ +package com.project.core.service; + +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.project.core.infra.entity.discount.DiscountPolicy; +import com.project.core.infra.entity.discount.SubscriptionDiscount; +import com.project.core.infra.entity.discount.enums.DiscountType; +import com.project.core.infra.entity.discount.enums.Status; +import com.project.core.infra.entity.discount.enums.TargetScope; +import com.project.core.infra.entity.subscription.Subscription; +import com.project.core.infra.repository.discount.DiscountPolicyRepository; +import com.project.core.infra.repository.discount.SubscriptionDiscountRepository; +import com.project.core.infra.repository.subscription.SubscriptionRepository; + +import lombok.RequiredArgsConstructor; + +@Service @RequiredArgsConstructor +public class DiscountService { + + private final SubscriptionDiscountRepository subscriptionDiscountRepository; + private final SubscriptionRepository subscriptionRepository; + private final DiscountPolicyRepository discountPolicyRepository; + + @Transactional(readOnly = true) //회선에 적용 된 할인 정책 확인 + public List loadRequiredBySubId(Long subId) { + + List discounts = + subscriptionDiscountRepository.findBySubId(subId); + + if (discounts.isEmpty()) { + throw new IllegalStateException("해당 회선에 적용된 할인이 없습니다"); + } + + return discounts; + } + + @Transactional + public Long addDiscount(Long subId, Long discountId) { + + Subscription subscription = + subscriptionRepository.findById(subId) + .orElseThrow(() -> new IllegalArgumentException("회선이 존재하지 않습니다")); + + DiscountPolicy policy = + discountPolicyRepository.findById(discountId) + .orElseThrow(() -> new IllegalArgumentException("할인 정책이 존재하지 않습니다")); + + SubscriptionDiscount discount = + SubscriptionDiscount.builder() + .discountPolicy(policy) + .subscription(subscription) + .discountType(policy.getDiscountType()) + .value(policy.getValue()) + .targetScope(policy.getTargetScope()) + .startDate(LocalDateTime.now()) + .build(); + + subscriptionDiscountRepository.save(discount); + + return discount.getSdId(); + } + + + @Transactional + public Long changeDiscount(Long discountId,Long sdId) { //할인 변경 + SubscriptionDiscount discount = + subscriptionDiscountRepository.findBySdId(sdId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 할인입니다.")); + //내부 정보 수정 - 기존 할인 종료 + discount.setEndDate(LocalDateTime.now()); + discount.setStatusTerminated(); + //새로운 할인 적용 + return addDiscount(discount.getSubscription().getSubId(),discountId); //생성된 할인 테이블 아이디 반환 + } +} diff --git a/src/main/java/com/project/core/service/SubscriptionService.java b/src/main/java/com/project/core/service/SubscriptionService.java new file mode 100644 index 0000000..0a401e1 --- /dev/null +++ b/src/main/java/com/project/core/service/SubscriptionService.java @@ -0,0 +1,40 @@ +package com.project.core.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.project.core.infra.entity.discount.SubscriptionDiscount; +import com.project.core.infra.entity.subscription.Subscription; +import com.project.core.infra.repository.discount.SubscriptionDiscountRepository; +import com.project.core.infra.repository.subscription.SubscriptionRepository; +import com.project.global.util.AESUtil; + +import lombok.RequiredArgsConstructor; + +@Service @RequiredArgsConstructor +public class SubscriptionService { + + private final SubscriptionRepository subscriptionRepository; + private final AESUtil aesUtil; + + @Transactional + public List findSubscription(Long customerId) throws Exception { + List subscriptions = + subscriptionRepository.findByCostomerId(customerId); + + if (subscriptions.isEmpty()) { + throw new IllegalStateException("보유중인 회선이 없습니다."); + }else { + //복호화 -> 마스킹 + for (int i = 0; i < subscriptions.size(); i++) { + String num = aesUtil.decrypt(subscriptions.get(i).getPhoneNumber()); + String maskedNum = num.substring(0,4)+"**"+num.substring(6,9)+"**"+num.substring(11,12); //"010-**34-**78" + subscriptions.get(i).setPhoneNumber(maskedNum); + } + } + + return subscriptions; + } +} From 2fd535a9e65ffb569648d4ccd0cfa44bc81a87c7 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 17:47:15 +0900 Subject: [PATCH 02/15] =?UTF-8?q?UPLUS-57=20refactor:=20=EB=A7=88=EC=8A=A4?= =?UTF-8?q?=ED=82=B9=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8=EA=B0=80=20db?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5=EB=90=98=EB=8A=94=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/controller/DiscountController.java | 99 +++++++++++++++++++ .../dto/request/ChangeEmailRequest.java | 2 +- .../SubscriptionDiscountResponse.java | 74 ++++++++++++++ .../entity/subscription/Subscription.java | 3 - .../project/core/service/CustomerService.java | 8 +- .../project/core/service/DiscountService.java | 8 +- .../core/service/SubscriptionService.java | 11 +-- .../code/domain/core/CoreErrorCode.java | 8 +- 8 files changed, 195 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/project/core/controller/DiscountController.java create mode 100644 src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java diff --git a/src/main/java/com/project/core/controller/DiscountController.java b/src/main/java/com/project/core/controller/DiscountController.java new file mode 100644 index 0000000..a76cb7a --- /dev/null +++ b/src/main/java/com/project/core/controller/DiscountController.java @@ -0,0 +1,99 @@ +package com.project.core.controller; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.project.core.controller.dto.response.SubscriptionDiscountResponse; +import com.project.core.infra.entity.discount.SubscriptionDiscount; +import com.project.core.service.DiscountService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/discounts") +@RequiredArgsConstructor +public class DiscountController { + + private final DiscountService discountService; + + /** + * 회선(subId)에 적용된 할인 목록 조회 + * GET /discounts/subscriptions/{subId} + */ + @GetMapping("/subscriptions/{subId}") + public ResponseEntity> getDiscountsBySubscription(@PathVariable Long subId) { + List discounts = discountService.loadRequiredBySubId(subId); + + List response = discounts.stream() + .map(SubscriptionDiscountResponse::from) + .toList(); + + return ResponseEntity.ok(response); + } + + /** + * 회선(subId)에 할인 정책(discountId) 추가(적용) + * POST /discounts + */ + @PostMapping + public ResponseEntity addDiscount(@RequestBody CreateDiscountRequest request) { + Long sdId = discountService.addDiscount(request.getSubId(), request.getDiscountId()); + return ResponseEntity.status(HttpStatus.CREATED).body(new CreateDiscountResponse(sdId)); + } + + /** + * 기존 할인(sdId)을 종료하고, 새로운 할인 정책(discountId)로 변경 + * PATCH /discounts/{sdId} + */ + @PatchMapping("/{sdId}") + public ResponseEntity changeDiscount( + @PathVariable Long sdId, + @RequestBody ChangeDiscountRequest request + ) { + Long newSdId = discountService.changeDiscount(request.getDiscountId(), sdId); + return ResponseEntity.ok(new ChangeDiscountResponse(newSdId)); + } + + // ===== DTOs ===== + + public static class CreateDiscountRequest { + private Long subId; + private Long discountId; + + public Long getSubId() { return subId; } + public Long getDiscountId() { return discountId; } + + public void setSubId(Long subId) { this.subId = subId; } + public void setDiscountId(Long discountId) { this.discountId = discountId; } + } + + public static class CreateDiscountResponse { + private final Long sdId; + + public CreateDiscountResponse(Long sdId) { this.sdId = sdId; } + public Long getSdId() { return sdId; } + } + + public static class ChangeDiscountRequest { + private Long discountId; + + public Long getDiscountId() { return discountId; } + public void setDiscountId(Long discountId) { this.discountId = discountId; } + } + + public static class ChangeDiscountResponse { + private final Long newSdId; + + public ChangeDiscountResponse(Long newSdId) { this.newSdId = newSdId; } + public Long getNewSdId() { return newSdId; } + } +} \ No newline at end of file diff --git a/src/main/java/com/project/core/controller/dto/request/ChangeEmailRequest.java b/src/main/java/com/project/core/controller/dto/request/ChangeEmailRequest.java index fcaebeb..36bf90c 100644 --- a/src/main/java/com/project/core/controller/dto/request/ChangeEmailRequest.java +++ b/src/main/java/com/project/core/controller/dto/request/ChangeEmailRequest.java @@ -1,3 +1,3 @@ package com.project.core.controller.dto.request; -public record ChangeEmailRequest(String emailEnc) {} +public record ChangeEmailRequest(String email) {} diff --git a/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java b/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java new file mode 100644 index 0000000..3129904 --- /dev/null +++ b/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java @@ -0,0 +1,74 @@ +package com.project.core.controller.dto.response; + +import java.time.LocalDateTime; + +import com.project.core.infra.entity.discount.SubscriptionDiscount; +import com.project.core.infra.entity.discount.enums.DiscountType; +import com.project.core.infra.entity.discount.enums.Status; +import com.project.core.infra.entity.discount.enums.TargetScope; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class SubscriptionDiscountResponse { + + private Long sdId; + private Long subId; + private String maskedPhoneNumber; + + private Long discountId; + private DiscountType discountType; + private Double value; + private TargetScope targetScope; + + private LocalDateTime startDate; + private LocalDateTime endDate; + private Status status; + + public static SubscriptionDiscountResponse from(SubscriptionDiscount sd) { + + // ⚠️ Subscription 엔티티 실제 getter명으로 수정 + String phone = sd.getSubscription().getPhoneNumber(); + + return SubscriptionDiscountResponse.builder() + .sdId(sd.getSdId()) + .subId(sd.getSubscription().getSubId()) + .maskedPhoneNumber(maskPhone(phone)) + + .discountId(sd.getDiscountPolicy().getDiscountId()) + .discountType(sd.getDiscountType()) + .value(sd.getValue()) + .targetScope(sd.getTargetScope()) + + .startDate(sd.getStartDate()) + .endDate(sd.getEndDate()) + .status(sd.getStatus()) + .build(); + } + + /** + * 010-1234-1212 -> 010-**12-**12 + * 01012341212 -> 010-**12-**12 + */ + private static String maskPhone(String phone) { + if (phone == null || phone.isBlank()) return null; + + // 숫자만 추출 + String digits = phone.replaceAll("\\D", ""); + if (digits.length() != 11) { + return "****"; // 예상 못 한 형식은 전체 마스킹 + } + + String p1 = digits.substring(0, 3); // 010 + String p2 = digits.substring(3, 7); // 1234 + String p3 = digits.substring(7, 11); // 1212 + + // **12 / **12 + String masked2 = "**" + p2.substring(2); + String masked3 = "**" + p3.substring(2); + + return String.format("%s-%s-%s", p1, masked2, masked3); + } +} diff --git a/src/main/java/com/project/core/infra/entity/subscription/Subscription.java b/src/main/java/com/project/core/infra/entity/subscription/Subscription.java index d2f725a..7aadc3b 100644 --- a/src/main/java/com/project/core/infra/entity/subscription/Subscription.java +++ b/src/main/java/com/project/core/infra/entity/subscription/Subscription.java @@ -74,7 +74,4 @@ public void terminate(Clock clock) { this.endDate = LocalDateTime.now(clock); } - public void setPhoneNumber(String maskedNum) { - this.phoneNumber = maskedNum; - } } \ No newline at end of file diff --git a/src/main/java/com/project/core/service/CustomerService.java b/src/main/java/com/project/core/service/CustomerService.java index 0613629..9b91a1a 100644 --- a/src/main/java/com/project/core/service/CustomerService.java +++ b/src/main/java/com/project/core/service/CustomerService.java @@ -13,13 +13,16 @@ import com.project.core.infra.repository.customer.CustomerRepository; import com.project.global.exception.code.domain.core.CoreErrorCode; import com.project.global.exception.core.EntityNotFoundException; +import com.project.global.util.AESUtil; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class CustomerService { - private final CustomerRepository customerRepository; + private final CustomerRepository customerRepository; + private final AESUtil aesUtil; + @Transactional public List loadByContactEnc(String contactEnc) {//유저 조회 List customers = customerRepository.findByContactEnc(contactEnc); @@ -34,7 +37,8 @@ public List loadByContactEnc(String contactEnc) {//유저 조회 public ChangeEmailResponse changeEmailEnc(Long userId, ChangeEmailRequest request) {//이메일 변경 Customer customer = customerRepository.findById(userId).orElseThrow(()-> new EntityNotFoundException(CoreErrorCode.CUSTOMER_NOT_FOUND)); - customer.changeEmailEnc(request.emailEnc()); + String emailEnc = aesUtil.encrypt(request.email()); + customer.changeEmailEnc(emailEnc); return new ChangeEmailResponse(customer.getEmailEnc()); } diff --git a/src/main/java/com/project/core/service/DiscountService.java b/src/main/java/com/project/core/service/DiscountService.java index 4423a22..9f1a1f0 100644 --- a/src/main/java/com/project/core/service/DiscountService.java +++ b/src/main/java/com/project/core/service/DiscountService.java @@ -12,6 +12,8 @@ import com.project.core.infra.repository.discount.DiscountPolicyRepository; import com.project.core.infra.repository.discount.SubscriptionDiscountRepository; import com.project.core.infra.repository.subscription.SubscriptionRepository; +import com.project.global.exception.code.domain.core.CoreErrorCode; +import com.project.global.exception.core.EntityNotFoundException; import lombok.RequiredArgsConstructor; @@ -40,11 +42,11 @@ public Long addDiscount(Long subId, Long discountId) { Subscription subscription = subscriptionRepository.findById(subId) - .orElseThrow(() -> new IllegalArgumentException("회선이 존재하지 않습니다")); + .orElseThrow(() -> new EntityNotFoundException(CoreErrorCode.SUBSCRIPTION_NOT_FOUND)); DiscountPolicy policy = discountPolicyRepository.findById(discountId) - .orElseThrow(() -> new IllegalArgumentException("할인 정책이 존재하지 않습니다")); + .orElseThrow(() -> new EntityNotFoundException(CoreErrorCode.DISCOUNT_POLICY_NOT_FOUND)); SubscriptionDiscount discount = SubscriptionDiscount.builder() @@ -66,7 +68,7 @@ public Long addDiscount(Long subId, Long discountId) { public Long changeDiscount(Long discountId,Long sdId) { //할인 변경 SubscriptionDiscount discount = subscriptionDiscountRepository.findBySdId(sdId) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 할인입니다.")); + .orElseThrow(() -> new EntityNotFoundException(CoreErrorCode.DISCOUNT_NOT_FOUND)); //내부 정보 수정 - 기존 할인 종료 discount.setEndDate(LocalDateTime.now()); discount.setStatusTerminated(); diff --git a/src/main/java/com/project/core/service/SubscriptionService.java b/src/main/java/com/project/core/service/SubscriptionService.java index 12ff840..3484906 100644 --- a/src/main/java/com/project/core/service/SubscriptionService.java +++ b/src/main/java/com/project/core/service/SubscriptionService.java @@ -7,6 +7,8 @@ import com.project.core.infra.entity.subscription.Subscription; import com.project.core.infra.repository.subscription.SubscriptionRepository; +import com.project.global.exception.code.domain.core.CoreErrorCode; +import com.project.global.exception.core.EntityNotFoundException; import com.project.global.util.AESUtil; import lombok.RequiredArgsConstructor; @@ -23,14 +25,7 @@ public List findSubscription(Long customerId) throws Exception { subscriptionRepository.findByCustomer_CustomerId(customerId); if (subscriptions.isEmpty()) { - throw new IllegalStateException("보유중인 회선이 없습니다."); - }else { - //복호화 -> 마스킹 - for (int i = 0; i < subscriptions.size(); i++) { - String num = aesUtil.decrypt(subscriptions.get(i).getPhoneNumber()); - String maskedNum = num.substring(0,4)+"**"+num.substring(6,9)+"**"+num.substring(11,12); //"010-**34-**78" - subscriptions.get(i).setPhoneNumber(maskedNum); - } + throw new EntityNotFoundException(CoreErrorCode.SUBSCRIPTION_NOT_FOUND); } return subscriptions; diff --git a/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java b/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java index 3f8a4e9..7ed0f3f 100644 --- a/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java +++ b/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java @@ -10,7 +10,13 @@ public enum CoreErrorCode implements BaseErrorCode { // 고객 CUSTOMER_NOT_FOUND(HttpStatus.NOT_FOUND, "CUSTOMER_001", "존재하지 않는 고객입니다."), - + + // 할인정책 + DISCOUNT_POLICY_NOT_FOUND(HttpStatus.NOT_FOUND, "DISCOUNT_POLICY_001", "존재하지 않는 할인 정책입니다."), + + // 할인 + DISCOUNT_NOT_FOUND(HttpStatus.NOT_FOUND, "DISCOUNT_001", "존재하지 않는 할인입니다."), + // 요금제 PLAN_NOT_FOUND(HttpStatus.NOT_FOUND, "PLAN_001", "존재하지 않는 요금제입니다."), From 7632ca3b6c6e6c0340b071de0b33987de454a4c4 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:02:10 +0900 Subject: [PATCH 03/15] =?UTF-8?q?UPLUS-57=20refactor:=20=ED=95=A0=EC=9D=B8?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=AA=A8=EB=8D=B8=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/core/infra/entity/discount/DiscountPolicy.java | 3 ++- .../core/infra/entity/discount/SubscriptionDiscount.java | 5 +++-- .../project/core/infra/entity/discount/enums/Category.java | 6 +++--- .../core/infra/entity/discount/enums/DiscountType.java | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java b/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java index ae58430..00be3a6 100644 --- a/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java +++ b/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java @@ -1,5 +1,6 @@ package com.project.core.infra.entity.discount; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -37,7 +38,7 @@ public class DiscountPolicy { private DiscountType discountType; @Column(name = "value", nullable = false) - private Double value; + private BigDecimal value; @Column(name = "category", nullable = false) private Category category; diff --git a/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java b/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java index cfa6930..ec17be1 100644 --- a/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java +++ b/src/main/java/com/project/core/infra/entity/discount/SubscriptionDiscount.java @@ -1,5 +1,6 @@ package com.project.core.infra.entity.discount; +import java.math.BigDecimal; import java.time.LocalDateTime; import com.project.core.infra.entity.discount.enums.DiscountType; @@ -44,7 +45,7 @@ public class SubscriptionDiscount { private DiscountType discountType; @Column(name = "value", nullable = false) - private Double value; + private BigDecimal value; @Column(name = "target_scope", nullable = false) private TargetScope targetScope; @@ -70,7 +71,7 @@ private SubscriptionDiscount( DiscountPolicy discountPolicy, Subscription subscription, DiscountType discountType, - Double value, + BigDecimal value, TargetScope targetScope, LocalDateTime startDate ) { diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/Category.java b/src/main/java/com/project/core/infra/entity/discount/enums/Category.java index 5439a78..531608a 100644 --- a/src/main/java/com/project/core/infra/entity/discount/enums/Category.java +++ b/src/main/java/com/project/core/infra/entity/discount/enums/Category.java @@ -1,7 +1,7 @@ package com.project.core.infra.entity.discount.enums; public enum Category { - 복지 - ,프로모션 - ,결합 + WELFARE, + PROMOTION, + COMBINATION } diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java b/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java index aad9fc1..5443035 100644 --- a/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java +++ b/src/main/java/com/project/core/infra/entity/discount/enums/DiscountType.java @@ -1,6 +1,6 @@ package com.project.core.infra.entity.discount.enums; public enum DiscountType { - Rate - ,Fixed + RATE, + FIXED } From 4bee79dbaa4d9f5f33f1559f099ffbb6f2472b1d Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:02:28 +0900 Subject: [PATCH 04/15] =?UTF-8?q?UPLUS-57=20feat:=20=EA=B3=A0=EA=B0=9D=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/CustomerResponse.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/project/core/controller/dto/response/CustomerResponse.java diff --git a/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java b/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java new file mode 100644 index 0000000..c29b506 --- /dev/null +++ b/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java @@ -0,0 +1,27 @@ +package com.project.core.controller.dto.response; + +import com.project.core.infra.entity.customer.Customer; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class CustomerResponse { + + private Long customerId; + private String name; + private String grade; + + // 필요하면 마스킹된 연락처만 제공(원본/암호화값은 노출 금지) + private String maskedContact; + + public static CustomerResponse from(Customer c) { + return CustomerResponse.builder() + .customerId(c.getCustomerId()) // ✅ 실제 PK getter명으로 맞추기 + .name(c.getName()) // ✅ 실제 필드명으로 맞추기 + .grade(String.valueOf(c.getGrade())) // grade가 enum이면 적절히 변환 + // maskedContact는 "복호화 가능한 원본"이 있을 때만 넣는 걸 권장 + .maskedContact(null) + .build(); + } +} From ddffb27f2e2d17ca7c038acc7e7f0b16f703f188 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:02:47 +0900 Subject: [PATCH 05/15] =?UTF-8?q?UPLUS-57=20refactor:=20=ED=95=A0=EC=9D=B8?= =?UTF-8?q?=20=EC=9D=91=EB=8B=B5=20DTO=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/response/SubscriptionDiscountResponse.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java b/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java index 3129904..f3a6d69 100644 --- a/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java +++ b/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java @@ -1,5 +1,6 @@ package com.project.core.controller.dto.response; +import java.math.BigDecimal; import java.time.LocalDateTime; import com.project.core.infra.entity.discount.SubscriptionDiscount; @@ -20,7 +21,7 @@ public class SubscriptionDiscountResponse { private Long discountId; private DiscountType discountType; - private Double value; + private BigDecimal value; private TargetScope targetScope; private LocalDateTime startDate; From 2545b0a65cc93eaf5e9b8508f6a7b92c985d1b47 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:02:57 +0900 Subject: [PATCH 06/15] =?UTF-8?q?UPLUS-57=20refactor:=20=ED=95=A0=EC=9D=B8?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/project/core/service/DiscountService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/project/core/service/DiscountService.java b/src/main/java/com/project/core/service/DiscountService.java index 9f1a1f0..46e32f1 100644 --- a/src/main/java/com/project/core/service/DiscountService.java +++ b/src/main/java/com/project/core/service/DiscountService.java @@ -31,7 +31,7 @@ public List loadRequiredBySubId(Long subId) { subscriptionDiscountRepository.findBySubscription_SubId(subId); if (discounts.isEmpty()) { - throw new IllegalStateException("해당 회선에 적용된 할인이 없습니다"); + throw new EntityNotFoundException(CoreErrorCode.DISCOUNT_NOT_FOUND); } return discounts; From 24575e3f9f9a7ee8b12839263d7311366e302a8a Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:03:09 +0900 Subject: [PATCH 07/15] =?UTF-8?q?UPLUS-57=20refactor:=20=EA=B3=A0=EA=B0=9D?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/controller/CustomerController.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/project/core/controller/CustomerController.java b/src/main/java/com/project/core/controller/CustomerController.java index 28c628a..33ea2e6 100644 --- a/src/main/java/com/project/core/controller/CustomerController.java +++ b/src/main/java/com/project/core/controller/CustomerController.java @@ -3,52 +3,48 @@ import java.util.List; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; import com.project.core.controller.dto.request.ChangeEmailRequest; import com.project.core.controller.dto.request.ChangeGradeRequest; import com.project.core.controller.dto.response.ChangeEmailResponse; import com.project.core.controller.dto.response.ChangeGradeResponse; +import com.project.core.controller.dto.response.CustomerResponse; import com.project.core.infra.entity.customer.Customer; import com.project.core.service.CustomerService; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - +import lombok.RequiredArgsConstructor; @RestController @RequestMapping("/user") @RequiredArgsConstructor public class CustomerController { - private final CustomerService customerService; + + private final CustomerService customerService; /** * 고객 조회 (전화번호 기준) */ @GetMapping - public ResponseEntity> loadByContactEnc( - @RequestParam String contactEnc - ) { + public ResponseEntity> loadByContactEnc(@RequestParam String contactEnc) { List customers = customerService.loadByContactEnc(contactEnc); - return ResponseEntity.ok(customers); + + List response = customers.stream() + .map(CustomerResponse::from) + .toList(); + + return ResponseEntity.ok(response); } /** * 이메일 변경 - * @throws Exception */ @PostMapping("/{userId}/email") public ResponseEntity changeEmail( @PathVariable Long userId, @RequestBody ChangeEmailRequest request - ) throws Exception { - ChangeEmailResponse response = - customerService.changeEmailEnc(userId, request); + ) { + ChangeEmailResponse response = customerService.changeEmailEnc(userId, request); return ResponseEntity.ok(response); } @@ -60,9 +56,7 @@ public ResponseEntity changeGrade( @PathVariable Long userId, @RequestBody ChangeGradeRequest request ) { - ChangeGradeResponse response = - customerService.changeUserGrade(userId, request); + ChangeGradeResponse response = customerService.changeUserGrade(userId, request); return ResponseEntity.ok(response); } - } From 37f47968b429bccc5f4dfb205c9b11ffd9bcd7f8 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:14:08 +0900 Subject: [PATCH 08/15] Update src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../com/project/core/infra/entity/discount/DiscountPolicy.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java b/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java index 00be3a6..cc36da4 100644 --- a/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java +++ b/src/main/java/com/project/core/infra/entity/discount/DiscountPolicy.java @@ -48,7 +48,6 @@ public class DiscountPolicy { @Column(name = "active", nullable = false) private Active active; -//------------------------------------------------------------------ - @OneToMany(mappedBy = "discountPolicy", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "discountPolicy") private List discountHistory = new ArrayList<>(); } From 7376af34e7035525363475af75df64d50e976caa Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:14:28 +0900 Subject: [PATCH 09/15] Update src/main/java/com/project/core/service/SubscriptionService.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/main/java/com/project/core/service/SubscriptionService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/project/core/service/SubscriptionService.java b/src/main/java/com/project/core/service/SubscriptionService.java index 3484906..d68ba39 100644 --- a/src/main/java/com/project/core/service/SubscriptionService.java +++ b/src/main/java/com/project/core/service/SubscriptionService.java @@ -20,7 +20,7 @@ public class SubscriptionService { private final AESUtil aesUtil; @Transactional - public List findSubscription(Long customerId) throws Exception { + public List findSubscription(Long customerId) { List subscriptions = subscriptionRepository.findByCustomer_CustomerId(customerId); From 2328c0be47b81a827d6463108c81501d06c706c2 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:14:44 +0900 Subject: [PATCH 10/15] Update src/main/java/com/project/core/infra/entity/discount/enums/Status.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../com/project/core/infra/entity/discount/enums/Status.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/Status.java b/src/main/java/com/project/core/infra/entity/discount/enums/Status.java index 7d193ba..13d4d59 100644 --- a/src/main/java/com/project/core/infra/entity/discount/enums/Status.java +++ b/src/main/java/com/project/core/infra/entity/discount/enums/Status.java @@ -1,6 +1,6 @@ package com.project.core.infra.entity.discount.enums; public enum Status { - ACTIVE - ,TERMINATED + ACTIVE, + TERMINATED } From 12c2f3312c79bac1556bf0319552f63a0cd93373 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:14:56 +0900 Subject: [PATCH 11/15] Update src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../controller/dto/response/SubscriptionDiscountResponse.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java b/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java index f3a6d69..45c06c9 100644 --- a/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java +++ b/src/main/java/com/project/core/controller/dto/response/SubscriptionDiscountResponse.java @@ -30,7 +30,6 @@ public class SubscriptionDiscountResponse { public static SubscriptionDiscountResponse from(SubscriptionDiscount sd) { - // ⚠️ Subscription 엔티티 실제 getter명으로 수정 String phone = sd.getSubscription().getPhoneNumber(); return SubscriptionDiscountResponse.builder() From c2729ba4082975f52eab07ec1408d8356fef5272 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:15:07 +0900 Subject: [PATCH 12/15] Update src/main/java/com/project/core/controller/dto/response/CustomerResponse.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../core/controller/dto/response/CustomerResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java b/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java index c29b506..e43cee4 100644 --- a/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java +++ b/src/main/java/com/project/core/controller/dto/response/CustomerResponse.java @@ -17,8 +17,8 @@ public class CustomerResponse { public static CustomerResponse from(Customer c) { return CustomerResponse.builder() - .customerId(c.getCustomerId()) // ✅ 실제 PK getter명으로 맞추기 - .name(c.getName()) // ✅ 실제 필드명으로 맞추기 + .customerId(c.getCustomerId()) + .name(c.getName()) .grade(String.valueOf(c.getGrade())) // grade가 enum이면 적절히 변환 // maskedContact는 "복호화 가능한 원본"이 있을 때만 넣는 걸 권장 .maskedContact(null) From 7ec6c00f63096a134abea9d0c9e52d8ec4cc6f76 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:15:23 +0900 Subject: [PATCH 13/15] Update src/main/java/com/project/core/infra/entity/discount/enums/Active.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../com/project/core/infra/entity/discount/enums/Active.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/core/infra/entity/discount/enums/Active.java b/src/main/java/com/project/core/infra/entity/discount/enums/Active.java index 8e98ff0..8c3aa27 100644 --- a/src/main/java/com/project/core/infra/entity/discount/enums/Active.java +++ b/src/main/java/com/project/core/infra/entity/discount/enums/Active.java @@ -1,6 +1,6 @@ package com.project.core.infra.entity.discount.enums; public enum Active { - ACTIVE - ,INACTIVE + ACTIVE, + INACTIVE } From a2c0bfd302b049a8be61ebc66348d49c8ec329f8 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:16:05 +0900 Subject: [PATCH 14/15] Update src/main/java/com/project/core/service/DiscountService.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../java/com/project/core/service/DiscountService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/project/core/service/DiscountService.java b/src/main/java/com/project/core/service/DiscountService.java index 46e32f1..596baa5 100644 --- a/src/main/java/com/project/core/service/DiscountService.java +++ b/src/main/java/com/project/core/service/DiscountService.java @@ -64,8 +64,14 @@ public Long addDiscount(Long subId, Long discountId) { } + /** + * 기존 할인을 종료하고 새로운 할인으로 교체합니다. + * @param discountId 새로 적용할 할인 정책 ID + * @param sdId 종료할 기존 구독 할인 ID + * @return 새로 생성된 구독 할인 ID + */ @Transactional - public Long changeDiscount(Long discountId,Long sdId) { //할인 변경 + public Long changeDiscount(Long discountId, Long sdId) { SubscriptionDiscount discount = subscriptionDiscountRepository.findBySdId(sdId) .orElseThrow(() -> new EntityNotFoundException(CoreErrorCode.DISCOUNT_NOT_FOUND)); From df856465701c9f9d25e5d7bdde679ce513394521 Mon Sep 17 00:00:00 2001 From: arlen02-01 Date: Wed, 14 Jan 2026 18:17:21 +0900 Subject: [PATCH 15/15] Update src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../global/exception/code/domain/core/CoreErrorCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java b/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java index 7ed0f3f..de40fe8 100644 --- a/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java +++ b/src/main/java/com/project/global/exception/code/domain/core/CoreErrorCode.java @@ -13,10 +13,10 @@ public enum CoreErrorCode implements BaseErrorCode { // 할인정책 DISCOUNT_POLICY_NOT_FOUND(HttpStatus.NOT_FOUND, "DISCOUNT_POLICY_001", "존재하지 않는 할인 정책입니다."), - + // 할인 DISCOUNT_NOT_FOUND(HttpStatus.NOT_FOUND, "DISCOUNT_001", "존재하지 않는 할인입니다."), - + // 요금제 PLAN_NOT_FOUND(HttpStatus.NOT_FOUND, "PLAN_001", "존재하지 않는 요금제입니다."),