From 6aa50c70f50418cd9225c1ae23357a578b83e732 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:34:00 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20Coupon=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=B6=94=EA=B0=80=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/team/buddyya/event/domain/Coupon.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/com/team/buddyya/event/domain/Coupon.java diff --git a/src/main/java/com/team/buddyya/event/domain/Coupon.java b/src/main/java/com/team/buddyya/event/domain/Coupon.java new file mode 100644 index 00000000..4ede1448 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/domain/Coupon.java @@ -0,0 +1,36 @@ +package com.team.buddyya.event.domain; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static jakarta.persistence.GenerationType.IDENTITY; + +@Entity +@Table(name = "coupon") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Coupon { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @Column(name = "code", length = 10, nullable = false, unique = true) + private String code; + + @Column(name = "used", nullable = false) + private Boolean isUsed; + + @Builder + public Coupon(String code) { + this.code = code; + this.isUsed = false; + } + + public void markAsUsed() { + this.isUsed = true; + } +} From 83968a639b72f8a1443229d308105af5913c67e0 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:34:50 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20Event=EA=B4=80=EB=A0=A8=20Excepti?= =?UTF-8?q?on=EA=B3=BC=20ExceptionType=20=EC=A0=95=EC=9D=98=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/exception/EventException.java | 18 ++++++++++ .../event/exception/EventExceptionType.java | 35 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/main/java/com/team/buddyya/event/exception/EventException.java create mode 100644 src/main/java/com/team/buddyya/event/exception/EventExceptionType.java diff --git a/src/main/java/com/team/buddyya/event/exception/EventException.java b/src/main/java/com/team/buddyya/event/exception/EventException.java new file mode 100644 index 00000000..eeb6c816 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/exception/EventException.java @@ -0,0 +1,18 @@ +package com.team.buddyya.event.exception; + +import com.team.buddyya.common.exception.BaseException; +import com.team.buddyya.common.exception.BaseExceptionType; + +public class EventException extends BaseException { + + private final EventExceptionType exceptionType; + + public EventException(EventExceptionType exceptionType) { + this.exceptionType = exceptionType; + } + + @Override + public BaseExceptionType exceptionType() { + return exceptionType; + } +} diff --git a/src/main/java/com/team/buddyya/event/exception/EventExceptionType.java b/src/main/java/com/team/buddyya/event/exception/EventExceptionType.java new file mode 100644 index 00000000..12587cc2 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/exception/EventExceptionType.java @@ -0,0 +1,35 @@ +package com.team.buddyya.event.exception; + +import com.team.buddyya.common.exception.BaseExceptionType; +import org.springframework.http.HttpStatus; + +public enum EventExceptionType implements BaseExceptionType { + + COUPON_NOT_FOUND(12000, HttpStatus.NOT_FOUND, "The coupon does not exist."), + COUPON_ALREADY_USED(12001, HttpStatus.BAD_REQUEST, "The coupon has already been used."); + + private final int errorCode; + private final HttpStatus httpStatus; + private final String errorMessage; + + EventExceptionType(int errorCode, HttpStatus httpStatus, String errorMessage) { + this.errorCode = errorCode; + this.httpStatus = httpStatus; + this.errorMessage = errorMessage; + } + + @Override + public int errorCode() { + return errorCode; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public String errorMessage() { + return errorMessage; + } +} From 23bf1108e70aa944493d2cf6aa7651b02a1fb92d Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:35:39 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20Coupon=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=EC=9A=94=EC=B2=AD=20=EB=B0=8F?= =?UTF-8?q?=20=EC=9D=91=EB=8B=B5=20DTO=20=EC=A0=95=EC=9D=98=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/team/buddyya/event/dto/CouponRequest.java | 4 ++++ .../com/team/buddyya/event/dto/CouponResponse.java | 12 ++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/main/java/com/team/buddyya/event/dto/CouponRequest.java create mode 100644 src/main/java/com/team/buddyya/event/dto/CouponResponse.java diff --git a/src/main/java/com/team/buddyya/event/dto/CouponRequest.java b/src/main/java/com/team/buddyya/event/dto/CouponRequest.java new file mode 100644 index 00000000..d4ddd4cb --- /dev/null +++ b/src/main/java/com/team/buddyya/event/dto/CouponRequest.java @@ -0,0 +1,4 @@ +package com.team.buddyya.event.dto; + +public record CouponRequest(String code) { +} diff --git a/src/main/java/com/team/buddyya/event/dto/CouponResponse.java b/src/main/java/com/team/buddyya/event/dto/CouponResponse.java new file mode 100644 index 00000000..3b11fe16 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/dto/CouponResponse.java @@ -0,0 +1,12 @@ +package com.team.buddyya.event.dto; + +import com.team.buddyya.point.domain.Point; +import com.team.buddyya.point.domain.PointType; + +public record CouponResponse(Integer point, + int pointChange) { + + public static CouponResponse from(Point point, PointType pointType) { + return new CouponResponse(point.getCurrentPoint(), pointType.getPointChange()); + } +} From 3827b704521451dd59dfd5d098dfedb47015419d Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:36:49 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20CouponRepository=EC=97=90=20?= =?UTF-8?q?=EC=BF=A0=ED=8F=B0=20=EC=BD=94=EB=93=9C=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=8F=B0=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=B0=BE=EB=8A=94=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buddyya/event/repository/CouponRepository.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/team/buddyya/event/repository/CouponRepository.java diff --git a/src/main/java/com/team/buddyya/event/repository/CouponRepository.java b/src/main/java/com/team/buddyya/event/repository/CouponRepository.java new file mode 100644 index 00000000..0f5394d7 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/repository/CouponRepository.java @@ -0,0 +1,10 @@ +package com.team.buddyya.event.repository; + +import com.team.buddyya.event.domain.Coupon; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface CouponRepository extends JpaRepository { + Optional findByCode(String code); +} From b87e71a1373d3fdae5d7aa803b0bcfb01cd7d1c2 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:37:07 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20CouponService=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EC=BF=A0=ED=8F=B0=20=EC=82=AC=EC=9A=A9=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buddyya/event/service/EventService.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/com/team/buddyya/event/service/EventService.java diff --git a/src/main/java/com/team/buddyya/event/service/EventService.java b/src/main/java/com/team/buddyya/event/service/EventService.java new file mode 100644 index 00000000..a08fd6d2 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/service/EventService.java @@ -0,0 +1,39 @@ +package com.team.buddyya.event.service; + +import com.team.buddyya.auth.domain.StudentInfo; +import com.team.buddyya.event.domain.Coupon; +import com.team.buddyya.event.dto.CouponRequest; +import com.team.buddyya.event.dto.CouponResponse; +import com.team.buddyya.event.exception.EventException; +import com.team.buddyya.event.exception.EventExceptionType; +import com.team.buddyya.event.repository.CouponRepository; +import com.team.buddyya.point.domain.Point; +import com.team.buddyya.point.domain.PointType; +import com.team.buddyya.point.service.UpdatePointService; +import com.team.buddyya.student.domain.Student; +import com.team.buddyya.student.service.FindStudentService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class EventService { + + private final FindStudentService findStudentService; + private final UpdatePointService updatePointService; + private final CouponRepository couponRepository; + + public CouponResponse useCoupon(StudentInfo studentInfo, CouponRequest request) { + Coupon coupon = couponRepository.findByCode(request.code()) + .orElseThrow(() -> new EventException(EventExceptionType.COUPON_NOT_FOUND)); + if (coupon.getIsUsed()) { + throw new EventException(EventExceptionType.COUPON_ALREADY_USED); + } + Student student = findStudentService.findByStudentId(studentInfo.id()); + Point updatedPoint = updatePointService.updatePoint(student, PointType.COUPON_EVENT_REWARD); + coupon.markAsUsed(); + return CouponResponse.from(updatedPoint, PointType.COUPON_EVENT_REWARD); + } +} From 366f08e1eb4d2775cd24f0c1d95a7f9beaf6b675 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:39:00 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20=EC=BF=A0=ED=8F=B0=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API=20=EA=B5=AC=ED=98=84=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/controller/EventController.java | 29 +++++++++++++++++++ .../team/buddyya/point/domain/PointType.java | 1 + 2 files changed, 30 insertions(+) create mode 100644 src/main/java/com/team/buddyya/event/controller/EventController.java diff --git a/src/main/java/com/team/buddyya/event/controller/EventController.java b/src/main/java/com/team/buddyya/event/controller/EventController.java new file mode 100644 index 00000000..0681b271 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/controller/EventController.java @@ -0,0 +1,29 @@ +package com.team.buddyya.event.controller; + +import com.team.buddyya.auth.domain.CustomUserDetails; +import com.team.buddyya.event.dto.CouponRequest; +import com.team.buddyya.event.dto.CouponResponse; +import com.team.buddyya.event.service.EventService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +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; + +@RestController +@RequestMapping("/events") +@RequiredArgsConstructor +public class EventController { + + private final EventService eventService; + + @PostMapping("/coupon") + public ResponseEntity registerCoupon(@AuthenticationPrincipal CustomUserDetails userDetails, + @RequestBody CouponRequest request) { + CouponResponse response = eventService.useCoupon(userDetails.getStudentInfo(), request); + return ResponseEntity.ok(response); + } + +} diff --git a/src/main/java/com/team/buddyya/point/domain/PointType.java b/src/main/java/com/team/buddyya/point/domain/PointType.java index dd0daf39..55915c0f 100644 --- a/src/main/java/com/team/buddyya/point/domain/PointType.java +++ b/src/main/java/com/team/buddyya/point/domain/PointType.java @@ -22,6 +22,7 @@ public enum PointType { MISSION_CERTIFICATION_REWARD("mission_certification_reward", 100, PointChangeType.MISSION), MISSION_VISIT_REWARD("mission_visit_reward", 10, PointChangeType.MISSION), FESTIVAL_REWARD("festival_reward", 50, PointChangeType.EARN), + COUPON_EVENT_REWARD("event_reward", 100, PointChangeType.EARN), EVENT_REWARD("event_reward", 10, PointChangeType.EARN); private final String displayName; From d5c1b249316fbe0d742058fee68c81a38b9dff6d Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:39:35 +0900 Subject: [PATCH 07/10] =?UTF-8?q?chore:=20coupon=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=83=9D=EC=84=B1=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/migration/V31__Create_coupon_table.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/resources/db/migration/V31__Create_coupon_table.sql diff --git a/src/main/resources/db/migration/V31__Create_coupon_table.sql b/src/main/resources/db/migration/V31__Create_coupon_table.sql new file mode 100644 index 00000000..524ccf3d --- /dev/null +++ b/src/main/resources/db/migration/V31__Create_coupon_table.sql @@ -0,0 +1,10 @@ +CREATE TABLE coupon +( + id BIGINT AUTO_INCREMENT NOT NULL, + code VARCHAR(10) NOT NULL, + used BIT(1) NOT NULL, + CONSTRAINT pk_coupon PRIMARY KEY (id) +); + +ALTER TABLE coupon + ADD CONSTRAINT uc_coupon_code UNIQUE (code); \ No newline at end of file From f817190489edd1fc33260c52fa30cd2aff83e890 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Fri, 16 May 2025 23:49:26 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EB=A7=88=EC=A7=80=EB=A7=89=20=EC=A4=84=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/team/buddyya/event/controller/EventController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/team/buddyya/event/controller/EventController.java b/src/main/java/com/team/buddyya/event/controller/EventController.java index 0681b271..33877461 100644 --- a/src/main/java/com/team/buddyya/event/controller/EventController.java +++ b/src/main/java/com/team/buddyya/event/controller/EventController.java @@ -25,5 +25,4 @@ public ResponseEntity registerCoupon(@AuthenticationPrincipal Cu CouponResponse response = eventService.useCoupon(userDetails.getStudentInfo(), request); return ResponseEntity.ok(response); } - } From d2dfc7e29463826662fdff03adb6a69708ca6306 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Sat, 17 May 2025 19:05:58 +0900 Subject: [PATCH 09/10] =?UTF-8?q?refactor:=20ENUM=20=EB=AA=85=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20COUPON=5FEVENT=5FREWARD=EC=9D=98=20display?= =?UTF-8?q?Name=20=EC=88=98=EC=A0=95=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/team/buddyya/point/domain/PointType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/team/buddyya/point/domain/PointType.java b/src/main/java/com/team/buddyya/point/domain/PointType.java index 55915c0f..d52aa47e 100644 --- a/src/main/java/com/team/buddyya/point/domain/PointType.java +++ b/src/main/java/com/team/buddyya/point/domain/PointType.java @@ -22,7 +22,7 @@ public enum PointType { MISSION_CERTIFICATION_REWARD("mission_certification_reward", 100, PointChangeType.MISSION), MISSION_VISIT_REWARD("mission_visit_reward", 10, PointChangeType.MISSION), FESTIVAL_REWARD("festival_reward", 50, PointChangeType.EARN), - COUPON_EVENT_REWARD("event_reward", 100, PointChangeType.EARN), + COUPON_EVENT_REWARD("coupon_event_reward", 100, PointChangeType.EARN), EVENT_REWARD("event_reward", 10, PointChangeType.EARN); private final String displayName; From 8c7c426d8be0736503d603a4e998bdc9067f2117 Mon Sep 17 00:00:00 2001 From: fnsl1026 Date: Sat, 17 May 2025 19:06:46 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=82=B4=20=EC=B2=AB=EB=B2=88?= =?UTF-8?q?=EC=A7=B8=20=EC=A4=84=EC=9D=80=20=EA=B0=9C=ED=96=89=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/team/buddyya/event/repository/CouponRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/team/buddyya/event/repository/CouponRepository.java b/src/main/java/com/team/buddyya/event/repository/CouponRepository.java index 0f5394d7..9ea28e44 100644 --- a/src/main/java/com/team/buddyya/event/repository/CouponRepository.java +++ b/src/main/java/com/team/buddyya/event/repository/CouponRepository.java @@ -6,5 +6,6 @@ import java.util.Optional; public interface CouponRepository extends JpaRepository { + Optional findByCode(String code); }