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..33877461 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/controller/EventController.java @@ -0,0 +1,28 @@ +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/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; + } +} 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()); + } +} 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; + } +} 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..9ea28e44 --- /dev/null +++ b/src/main/java/com/team/buddyya/event/repository/CouponRepository.java @@ -0,0 +1,11 @@ +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); +} 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); + } +} 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..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,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("coupon_event_reward", 100, PointChangeType.EARN), EVENT_REWARD("event_reward", 10, PointChangeType.EARN); private final String displayName; 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