Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package NextLevel.demo.funding.controller;

import NextLevel.demo.common.SuccessResponse;
import NextLevel.demo.funding.dto.request.RequestAddCouponDto;
import NextLevel.demo.funding.dto.response.ResponseCouponDto;
import NextLevel.demo.funding.service.CouponService;
import NextLevel.demo.util.jwt.JWTUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class CouponController {

private final CouponService couponService;

@PostMapping("/admin/coupon/add")
public ResponseEntity addCoupon(@RequestBody RequestAddCouponDto dto) {
if(dto.getUserId()==null)
dto.setUserId(JWTUtil.getUserIdFromSecurityContext());
couponService.addCoupon(dto);
return ResponseEntity.ok().body(new SuccessResponse("success", null));
}

@GetMapping("/social/coupon")
public ResponseEntity getCoupon() {
List<ResponseCouponDto> coupons =
couponService.couponList(JWTUtil.getUserIdFromSecurityContext())
.stream().map(ResponseCouponDto::of).toList();
return ResponseEntity.ok().body(new SuccessResponse("success", coupons));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class FundingController {
private final FundingService fundingService;

@PostMapping
public ResponseEntity<?> funding(@RequestBody RequestFundingDto dto) {
public ResponseEntity<?> funding(@RequestBody @Valid RequestFundingDto dto) {
dto.setUserId(JWTUtil.getUserIdFromSecurityContext());

if(dto.getFree() != null)
Expand All @@ -37,7 +37,7 @@ public ResponseEntity<?> funding(@RequestBody RequestFundingDto dto) {
return ResponseEntity.ok().body(new SuccessResponse("success", null));
}

@DeleteMapping("/delete")
@PostMapping("/delete")
public ResponseEntity<?> deleteFunding(@RequestBody RequestCancelFundingDto dto) {
dto.setUserId(JWTUtil.getUserIdFromSecurityContext());
if(dto.getFundingType().equals(FundingType.FREE))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package NextLevel.demo.funding.dto.request;

import NextLevel.demo.funding.entity.CouponEntity;
import NextLevel.demo.user.entity.UserEntity;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Getter
@Setter
@AllArgsConstructor
public class RequestAddCouponDto {

private Long userId;
@NotEmpty
private String name;
@NotNull
private Long price;

public CouponEntity toEntity(UserEntity user) {
return CouponEntity
.builder()
.name(name)
.price(price)
.user(user)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import NextLevel.demo.option.OptionEntity;
import NextLevel.demo.funding.entity.OptionFundingEntity;
import NextLevel.demo.user.entity.UserEntity;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand All @@ -12,8 +13,10 @@
@NoArgsConstructor
public class RequestOptionFundingDto {

@NotNull
private Long optionId;
private Long count;
private Long count = 1L;
private Long couponId;

private Long userId;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package NextLevel.demo.funding.dto.response;

import NextLevel.demo.funding.entity.CouponEntity;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Getter
@Setter
public class ResponseCouponDto {

private Long id;
private String name;
private Long percent;

public static ResponseCouponDto of(CouponEntity couponEntity) {
ResponseCouponDto dto = new ResponseCouponDto();
dto.id = couponEntity.getId();
dto.name = couponEntity.getName();
dto.percent = couponEntity.getPrice();
return dto;
}
}
48 changes: 48 additions & 0 deletions src/main/java/NextLevel/demo/funding/entity/CouponEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package NextLevel.demo.funding.entity;

import NextLevel.demo.user.entity.UserEntity;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "coupon")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
public class CouponEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column
private String name;
@Column
private Long price;

@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private UserEntity user;

@OneToOne
@JoinColumn(name = "option_funding_id", nullable = true)
private OptionFundingEntity optionFunding;

public void rollBackUseCoupon() {
optionFunding = null;
}

public void updateProjectFundingEntity(OptionFundingEntity optionFunding) {
this.optionFunding = optionFunding;
}

}

/*
option 펀딩을 한다
user coupon <-> option funding entity 와 1대 1 관계 (관계의 주인 : coupon)
option funding을 취소 한다 -> update coupon의 option_funding_id = null

coupon list 조회 : coupon.option_funding.id is null 인 값들만 렌더링 한다
*/
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
import NextLevel.demo.BasedEntity;
import NextLevel.demo.option.OptionEntity;
import NextLevel.demo.user.entity.UserEntity;
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 jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -38,6 +30,9 @@ public class OptionFundingEntity extends BasedEntity {
@JoinColumn(name = "user_id", nullable = false)
private UserEntity user;

@OneToOne(mappedBy = "optionFunding", targetEntity = CouponEntity.class, fetch = FetchType.LAZY)
private CouponEntity coupon;

@Column
private long count;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package NextLevel.demo.funding.repository;

import NextLevel.demo.funding.entity.CouponEntity;
import NextLevel.demo.funding.entity.OptionFundingEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface CouponRepository extends JpaRepository<CouponEntity, Long> {
@Query("select c from CouponEntity c where c.user.id = :userId and c.optionFunding.option.id = :optionFundingId")
Optional<CouponEntity> findByUserIdAndOptionFundingId(@Param("userId") Long userId, @Param("optionFundingId") Long optionFundingId);

@Query("select c from CouponEntity c where c.optionFunding is null")
List<CouponEntity> findByUserIdAndOptionFundingIsNull(@Param("userId") Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ public interface FreeFundingRepository extends JpaRepository<FreeFundingEntity,
@Query("select sum(f.price) from FreeFundingEntity f where f.project.id = :projectId")
Long getTotalPriceByProject(@Param("projectId") Long projectId);

@Query("select count(ff) from FreeFundingEntity ff where ff.project.id = :projectId")
Long getFundingCount(@Param("projectId") Long projectId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ public interface OptionFundingRepository extends JpaRepository<OptionFundingEnti
"group by f")
Long getTotalPriceByProject(@Param("projectId") Long projectId);

@Query("select sum(of.count) " +
"from OptionFundingEntity of " +
"where of.option.project.id = :projectId ")
Long getTotalFundingCount(@Param("projectId") Long projectId);

}
57 changes: 57 additions & 0 deletions src/main/java/NextLevel/demo/funding/service/CouponService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package NextLevel.demo.funding.service;

import NextLevel.demo.exception.CustomException;
import NextLevel.demo.exception.ErrorCode;
import NextLevel.demo.funding.dto.request.RequestAddCouponDto;
import NextLevel.demo.funding.entity.CouponEntity;
import NextLevel.demo.funding.entity.OptionFundingEntity;
import NextLevel.demo.funding.repository.CouponRepository;
import NextLevel.demo.user.entity.UserEntity;
import NextLevel.demo.user.service.UserValidateService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
public class CouponService {

private final CouponRepository couponRepository;
private final UserValidateService userValidateService;

@Transactional
public void addCoupon(RequestAddCouponDto dto) {
UserEntity user = userValidateService.getUserInfo(dto.getUserId());
couponRepository.save(dto.toEntity(user));
}

public List<CouponEntity> couponList(long userId) {
return couponRepository.findByUserIdAndOptionFundingIsNull(userId);
}

@Transactional
public long useCoupon(long userId, long couponId, OptionFundingEntity optionFunding, long price) {
CouponEntity coupon = couponRepository.findById(couponId).orElseThrow(
()->{return new CustomException(ErrorCode.NOT_FOUND, "coupon");}
);

if(!coupon.getUser().getId().equals(userId))
throw new CustomException(ErrorCode.NOT_AUTHOR);

price -= coupon.getPrice();

coupon.updateProjectFundingEntity(optionFunding);

return price>0?price:0;
}

public long rollBackUseCoupon(CouponEntity coupon, long price) {
price -= coupon.getPrice();
coupon.rollBackUseCoupon();
couponRepository.save(coupon);
return price;
}

}
21 changes: 20 additions & 1 deletion src/main/java/NextLevel/demo/funding/service/FundingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import NextLevel.demo.funding.dto.request.RequestCancelFundingDto;
import NextLevel.demo.funding.dto.request.RequestFreeFundingDto;
import NextLevel.demo.funding.dto.request.RequestOptionFundingDto;
import NextLevel.demo.funding.entity.CouponEntity;
import NextLevel.demo.funding.entity.FreeFundingEntity;
import NextLevel.demo.option.OptionEntity;
import NextLevel.demo.funding.entity.OptionFundingEntity;
Expand Down Expand Up @@ -32,6 +33,9 @@ public class FundingService {
private final OptionFundingRepository optionFundingRepository;
private final FreeFundingRepository freeFundingRepository;

private final CouponService couponService;

@Transactional
public void cancelFreeFunding(RequestCancelFundingDto dto) {
UserEntity user = userValidateService.getUserInfo(dto.getUserId());
FreeFundingEntity funding = freeFundingRepository.findById(dto.getId()).orElseThrow(
Expand All @@ -42,28 +46,42 @@ public void cancelFreeFunding(RequestCancelFundingDto dto) {
freeFundingRepository.deleteById(dto.getId());
}

@Transactional
public void cancelOptionFunding(RequestCancelFundingDto dto) {
UserEntity user = userValidateService.getUserInfo(dto.getUserId());
OptionFundingEntity funding = optionFundingRepository.findById(dto.getId()).orElseThrow(
()->{return new CustomException(ErrorCode.NOT_FOUND, "optionFunding");}
);
long price = funding.getCount() * funding.getOption().getPrice();

if(!user.getId().equals(funding.getUser().getId()))
throw new CustomException(ErrorCode.NOT_AUTHOR);

if(funding.getCoupon() != null){
price = couponService.rollBackUseCoupon(funding.getCoupon(), price);
}

optionFundingRepository.deleteById(dto.getId());
user.updatePoint(+price);
}

@Transactional
public void optionFunding(RequestOptionFundingDto dto) {
UserEntity user = userValidateService.getUserInfo(dto.getUserId());
OptionEntity option = optionValidateService.getOption(dto.getOptionId());

OptionFundingEntity entity = dto.toEntity(user, option);

// validate price <> option.price * count
long totalPrice = option.getPrice() * dto.getCount();
if(dto.getCouponId() != null)
totalPrice = couponService.useCoupon(user.getId(), dto.getCouponId(), entity, totalPrice);

if(totalPrice > user.getPoint())
throw new CustomException(ErrorCode.NOT_ENOUGH_POINT, String.valueOf(user.getPoint()), String.valueOf(totalPrice));

optionFundingRepository.save(dto.toEntity(user, option));
optionFundingRepository.save(entity);
user.updatePoint(-totalPrice);
}

@Transactional
Expand All @@ -75,6 +93,7 @@ public void freeFunding(RequestFreeFundingDto dto) {
throw new CustomException(ErrorCode.NOT_ENOUGH_POINT, String.valueOf(user.getPoint()), String.valueOf(dto.getFreePrice()));

freeFundingRepository.save(dto.toEntity(user, project));
user.updatePoint(-dto.getFreePrice());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ public Long getTotalFundingPrice(Long projectId) {
+ (freeFundingPrice != null ? freeFundingPrice : 0);
}

public Long getTotalFundingCount(Long projectId) {
Long optionFundingPrice = optionFundingRepository.getTotalFundingCount(projectId);
Long freeFundingPrice = freeFundingRepository.getFundingCount(projectId);
return (optionFundingPrice != null ? optionFundingPrice : 0)
+ (freeFundingPrice != null ? freeFundingPrice : 0);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class ProjectController {

// 추가
@PostMapping("/api1/project")
public ResponseEntity<?> createNewProject(@ModelAttribute CreateProjectDto dto) {
public ResponseEntity<?> createNewProject(@ModelAttribute @Valid CreateProjectDto dto) {
Long userId = JWTUtil.getUserIdFromSecurityContext();
dto.setUserId(userId);

Expand Down
Loading
Loading