diff --git a/src/main/java/com/hyundai/app/coupon/controller/CouponController.java b/src/main/java/com/hyundai/app/coupon/controller/CouponController.java new file mode 100644 index 0000000..29b9d02 --- /dev/null +++ b/src/main/java/com/hyundai/app/coupon/controller/CouponController.java @@ -0,0 +1,34 @@ +package com.hyundai.app.coupon.controller; + +import com.hyundai.app.common.AdventureOfHeendyResponse; +import com.hyundai.app.coupon.domain.Coupon; +import com.hyundai.app.coupon.service.CouponService; +import com.hyundai.app.security.methodparam.MemberId; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import springfox.documentation.annotations.ApiIgnore; + +import java.util.List; + +/** + * @author 엄상은 + * @since 2024/02/23 + * 사용자용 쿠폰 컨트롤러 + */ +@Api("사용자용 쿠폰 관련 API") +@RequiredArgsConstructor +@RequestMapping("/api/v1/coupons") +@RestController +public class CouponController { + private final CouponService couponService; + + @ApiOperation("사용자용 쿠폰 전체 조회 API") + @GetMapping + public AdventureOfHeendyResponse> findCouponList(@ApiIgnore @MemberId Integer memberId) { + return AdventureOfHeendyResponse.success("사용자의 쿠폰 목록을 가져왔습니다.", couponService.findMemberCouponList(memberId)); + } +} diff --git a/src/main/java/com/hyundai/app/coupon/domain/MemberCoupon.java b/src/main/java/com/hyundai/app/coupon/domain/MemberCoupon.java new file mode 100644 index 0000000..9b5d0d7 --- /dev/null +++ b/src/main/java/com/hyundai/app/coupon/domain/MemberCoupon.java @@ -0,0 +1,29 @@ +package com.hyundai.app.coupon.domain; + +import com.hyundai.app.common.entity.BaseEntity; + +import java.time.LocalDate; + +/** + * @author 엄상은 + * @since 2024/02/22 + * 유저가 가지고 있는 쿠폰 엔티티 + */ +public class MemberCoupon extends BaseEntity { + private int id; + private int memberId; + private int couponId; + private int isUsed; + private String channelType; + private LocalDate expiredAt; + + public MemberCoupon(int memberId, int couponId, String channelType) { + this.memberId = memberId; + this.couponId = couponId; + this.channelType = channelType; + } + + public static MemberCoupon of(int memberId, int couponId, String channelType) { + return new MemberCoupon(memberId, couponId, channelType); + } +} diff --git a/src/main/java/com/hyundai/app/coupon/mapper/CouponMapper.java b/src/main/java/com/hyundai/app/coupon/mapper/CouponMapper.java index 445a8ad..c322a8c 100644 --- a/src/main/java/com/hyundai/app/coupon/mapper/CouponMapper.java +++ b/src/main/java/com/hyundai/app/coupon/mapper/CouponMapper.java @@ -13,4 +13,8 @@ @Mapper public interface CouponMapper { List findCouponList(int storeId); + + Coupon findById(int couponId); + + List findMemberCouponList(Integer memberId); } diff --git a/src/main/java/com/hyundai/app/coupon/mapper/MemberCouponMapper.java b/src/main/java/com/hyundai/app/coupon/mapper/MemberCouponMapper.java new file mode 100644 index 0000000..0fad3d8 --- /dev/null +++ b/src/main/java/com/hyundai/app/coupon/mapper/MemberCouponMapper.java @@ -0,0 +1,12 @@ +package com.hyundai.app.coupon.mapper; + +import com.hyundai.app.coupon.domain.MemberCoupon; + +/** + * @author 엄상은 + * @since 2024/02/22 + * 쿠폰을 가진 사용자 매퍼 + */ +public interface MemberCouponMapper { + void saveMemberCoupon(MemberCoupon memberCoupon); +} diff --git a/src/main/java/com/hyundai/app/coupon/service/CouponService.java b/src/main/java/com/hyundai/app/coupon/service/CouponService.java index a6cb9f5..2637d66 100644 --- a/src/main/java/com/hyundai/app/coupon/service/CouponService.java +++ b/src/main/java/com/hyundai/app/coupon/service/CouponService.java @@ -27,4 +27,14 @@ public List findCouponList(int storeId) { } return couponList; } + + public List findMemberCouponList(Integer memberId) { + List couponList = couponMapper.findMemberCouponList(memberId); + for (Coupon coupon: couponList) { + CouponType couponType = coupon.getCouponType(); + String description = couponType.getDescription(coupon); + coupon.updateContent(description); + } + return couponList; + } } diff --git a/src/main/java/com/hyundai/app/event/controller/EventController.java b/src/main/java/com/hyundai/app/event/controller/EventController.java index ab8641d..54ad433 100644 --- a/src/main/java/com/hyundai/app/event/controller/EventController.java +++ b/src/main/java/com/hyundai/app/event/controller/EventController.java @@ -2,17 +2,17 @@ import com.hyundai.app.common.AdventureOfHeendyResponse; import com.hyundai.app.event.dto.EventDetailResDto; +import com.hyundai.app.event.dto.EventParticipateResDto; import com.hyundai.app.event.enumType.EventType; import com.hyundai.app.event.service.EventService; +import com.hyundai.app.security.methodparam.MemberId; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; import java.util.Random; @@ -35,6 +35,13 @@ public AdventureOfHeendyResponse findCurrentEventByEventType( return AdventureOfHeendyResponse.success("이벤트 목록을 가져왔습니다.", eventService.findCurrentEventByEventType(eventType)); } + @PostMapping("{eventId}/participate") + @ApiOperation("유저용 이벤트 참여 API") + public AdventureOfHeendyResponse participateEvent(@ApiIgnore @MemberId Integer memberId, + @PathVariable int eventId){ + return AdventureOfHeendyResponse.success("이벤트 참여에 성공했습니다.", eventService.participateEvent(memberId, eventId)); + } + /** * @author 황수영 * @since 2024/02/14 diff --git a/src/main/java/com/hyundai/app/event/domain/MemberEvent.java b/src/main/java/com/hyundai/app/event/domain/MemberEvent.java index e99caf0..66deabe 100644 --- a/src/main/java/com/hyundai/app/event/domain/MemberEvent.java +++ b/src/main/java/com/hyundai/app/event/domain/MemberEvent.java @@ -11,4 +11,13 @@ public class MemberEvent extends BaseEntity { private int id; private int eventId; private int memberId; + + public MemberEvent(int eventId, int memberId) { + this.eventId = eventId; + this.memberId = memberId; + } + + public static MemberEvent of(int eventId, int memberId) { + return new MemberEvent(eventId, memberId); + } } \ No newline at end of file diff --git a/src/main/java/com/hyundai/app/event/dto/EventDetailResDto.java b/src/main/java/com/hyundai/app/event/dto/EventDetailResDto.java index 37d8539..38aa43c 100644 --- a/src/main/java/com/hyundai/app/event/dto/EventDetailResDto.java +++ b/src/main/java/com/hyundai/app/event/dto/EventDetailResDto.java @@ -32,6 +32,7 @@ public class EventDetailResDto { private LocalDate finishedAt; private int maxCount; private int visitedCount; + private int couponId; private List eventActiveTimeZoneDto; public void setActiveTimeList(List eventActiveTimeZoneDto) { diff --git a/src/main/java/com/hyundai/app/event/dto/EventParticipateResDto.java b/src/main/java/com/hyundai/app/event/dto/EventParticipateResDto.java new file mode 100644 index 0000000..62b77b8 --- /dev/null +++ b/src/main/java/com/hyundai/app/event/dto/EventParticipateResDto.java @@ -0,0 +1,32 @@ +package com.hyundai.app.event.dto; + +import com.hyundai.app.coupon.domain.Coupon; +import lombok.Getter; + +/** + * @author 엄상은 + * @since 2024/02/22 + * 이벤트 참여 응답 DTO + */ +@Getter +public class EventParticipateResDto { + EventDetailResDto eventDetailResDto; + Coupon coupon; + + public EventParticipateResDto(EventDetailResDto eventDetailResDto, Coupon coupon) { + this.eventDetailResDto = eventDetailResDto; + this.coupon = coupon; + } + + public static EventParticipateResDto from(EventDetailResDto eventDetailResDto, Coupon coupon) { + return new EventParticipateResDto(eventDetailResDto, coupon); + } + + public static EventParticipateResDto of(EventDetailResDto eventDetailResDto) { + return new EventParticipateResDto(eventDetailResDto, null); + } + + public void updateCoupon(Coupon coupon) { + this.coupon = coupon; + } +} diff --git a/src/main/java/com/hyundai/app/event/mapper/EventMapper.java b/src/main/java/com/hyundai/app/event/mapper/EventMapper.java index 9d7f768..482fee6 100644 --- a/src/main/java/com/hyundai/app/event/mapper/EventMapper.java +++ b/src/main/java/com/hyundai/app/event/mapper/EventMapper.java @@ -28,4 +28,6 @@ public interface EventMapper { void delete(int eventId); List findEventAllByEventType(EventType eventType); + + void increaseVisitedCount(int eventId); } \ No newline at end of file diff --git a/src/main/java/com/hyundai/app/event/mapper/MemberEventMapper.java b/src/main/java/com/hyundai/app/event/mapper/MemberEventMapper.java new file mode 100644 index 0000000..459e717 --- /dev/null +++ b/src/main/java/com/hyundai/app/event/mapper/MemberEventMapper.java @@ -0,0 +1,14 @@ +package com.hyundai.app.event.mapper; + +import com.hyundai.app.event.domain.MemberEvent; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author 엄상은 + * @since 2024/02/21 + * 이벤트에 참여한 회원 Mapper + */ +@Mapper +public interface MemberEventMapper { + void saveMemberEvent(MemberEvent memberEvent); +} diff --git a/src/main/java/com/hyundai/app/event/service/EventService.java b/src/main/java/com/hyundai/app/event/service/EventService.java index 902ca50..945498e 100644 --- a/src/main/java/com/hyundai/app/event/service/EventService.java +++ b/src/main/java/com/hyundai/app/event/service/EventService.java @@ -1,10 +1,17 @@ package com.hyundai.app.event.service; import com.hyundai.app.common.entity.IdWithCriteria; +import com.hyundai.app.coupon.domain.Coupon; +import com.hyundai.app.coupon.domain.MemberCoupon; +import com.hyundai.app.coupon.mapper.CouponMapper; +import com.hyundai.app.coupon.mapper.MemberCouponMapper; +import com.hyundai.app.event.domain.MemberEvent; import com.hyundai.app.event.dto.*; import com.hyundai.app.event.enumType.EventType; +import com.hyundai.app.event.enumType.RewardType; import com.hyundai.app.event.mapper.EventActiveTimeMapper; import com.hyundai.app.event.mapper.EventMapper; +import com.hyundai.app.event.mapper.MemberEventMapper; import com.hyundai.app.exception.AdventureOfHeendyException; import com.hyundai.app.exception.ErrorCode; import lombok.RequiredArgsConstructor; @@ -24,6 +31,9 @@ public class EventService { private final EventMapper eventMapper; private final EventActiveTimeMapper eventActiveTimeMapper; + private final MemberEventMapper memberEventMapper; + private final MemberCouponMapper memberCouponMapper; + private final CouponMapper couponMapper; public EventDetailResDto findCurrentEventByEventType(EventType eventType) { EventDetailResDto eventDetailResDto = eventMapper.findCurrentEventByEventType(eventType); @@ -71,6 +81,17 @@ public EventDetailResDto findEventAndValidate(int storeId, int eventId) { return eventDetailResDto; } + private EventDetailResDto findAvailableEvent(int eventId) { + EventDetailResDto eventDetailResDto = eventMapper.findById(eventId); + if (eventDetailResDto == null) { + throw new IllegalArgumentException("해당 이벤트가 존재하지 않습니다."); + } + if (eventDetailResDto.getVisitedCount() >= eventDetailResDto.getMaxCount()) { + throw new IllegalArgumentException("이벤트 참여가 마감되었습니다."); + } + return eventDetailResDto; + } + private List findEventActiveTime(int eventId) { List eventActiveTimeZoneDto = eventActiveTimeMapper.findByEventId(eventId); return eventActiveTimeZoneDto; @@ -110,6 +131,38 @@ public Void delete(int storeId, int eventId) { return null; } + public EventParticipateResDto participateEvent(Integer memberId, int eventId) { + EventDetailResDto eventDetailResDto = findAvailableEvent(eventId); + EventParticipateResDto eventVisitResDto = EventParticipateResDto.of(eventDetailResDto); + if (eventDetailResDto.getRewardType() == RewardType.COUPON) { + int couponId = eventDetailResDto.getCouponId(); + Coupon coupon = findCoupon(couponId); + eventVisitResDto.updateCoupon(coupon); + saveMemberCoupon(memberId, couponId, "OFFLINE"); + } + visitEvent(memberId, eventId); + return eventVisitResDto; + } + + private Coupon findCoupon(int couponId) { + Coupon coupon = couponMapper.findById(couponId); + if (coupon == null) { + throw new IllegalArgumentException("해당 쿠폰이 존재하지 않습니다."); + } + return coupon; + } + + private void saveMemberCoupon(int memberId, int couponId, String channelType) { + MemberCoupon memberCoupon = MemberCoupon.of(memberId, couponId, channelType); + memberCouponMapper.saveMemberCoupon(memberCoupon); + } + + private void visitEvent(int memberId, int eventId) { + MemberEvent memberEvent = MemberEvent.of(eventId, memberId); + memberEventMapper.saveMemberEvent(memberEvent); + eventMapper.increaseVisitedCount(eventId); + } + /** * @author 황수영 * @since 2024/02/20 diff --git a/src/main/resources/mapper/CouponMapper.xml b/src/main/resources/mapper/CouponMapper.xml index 78a277a..02c0bc7 100644 --- a/src/main/resources/mapper/CouponMapper.xml +++ b/src/main/resources/mapper/CouponMapper.xml @@ -17,4 +17,30 @@ FROM coupon WHERE store_id = #{storeId} + + + + diff --git a/src/main/resources/mapper/EventMapper.xml b/src/main/resources/mapper/EventMapper.xml index 9ce2b37..15ff226 100644 --- a/src/main/resources/mapper/EventMapper.xml +++ b/src/main/resources/mapper/EventMapper.xml @@ -66,6 +66,7 @@ , finished_at , max_count , visited_count + , coupon_id FROM event WHERE id = #{eventId} @@ -139,4 +140,10 @@ AND max_count >= visited_count AND is_deleted = 0 + + + UPDATE event + SET visited_count = visited_count + 1 + WHERE id = #{eventId} + diff --git a/src/main/resources/mapper/MemberCouponMapper.xml b/src/main/resources/mapper/MemberCouponMapper.xml new file mode 100644 index 0000000..660724b --- /dev/null +++ b/src/main/resources/mapper/MemberCouponMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + INSERT INTO member_coupon (member_id + , coupon_id + , channel_type) + VALUES (#{memberId} + , #{couponId} + , #{channelType}) + + diff --git a/src/main/resources/mapper/MemberEventMapper.xml b/src/main/resources/mapper/MemberEventMapper.xml new file mode 100644 index 0000000..289f33e --- /dev/null +++ b/src/main/resources/mapper/MemberEventMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + INSERT INTO member_event (member_id + , event_id) + VALUES (#{memberId} + , #{eventId}) + +