TASK: 推广码 添加开始生效时间;优化数据计算类型,使用BigDecimal替换float;更新paidCommission后自动计算unpaidCommission
This commit is contained in:
@@ -81,5 +81,7 @@ public class CommonConstant {
|
|||||||
|
|
||||||
public static final String TIME_FORMAT_MMM_dd_yyyy = "MMM. dd, yyyy";
|
public static final String TIME_FORMAT_MMM_dd_yyyy = "MMM. dd, yyyy";
|
||||||
|
|
||||||
|
public static final String TIME_FORMAT_yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
public static final String AFFILIATE_LINK = "https://www.aida.com.hk?ref=";
|
public static final String AFFILIATE_LINK = "https://www.aida.com.hk?ref=";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,9 +137,11 @@ public class StripeController {
|
|||||||
|
|
||||||
@ApiOperation("更新推广码信息")
|
@ApiOperation("更新推广码信息")
|
||||||
@GetMapping("/updatePromCodeInfo")
|
@GetMapping("/updatePromCodeInfo")
|
||||||
public Response<ProductCoupons> updateCouponsInfo(@RequestParam Long id, @RequestParam(required = false) Long paidCommission,
|
public Response<ProductCoupons> updateCouponsInfo(@RequestParam Long id, @RequestParam(required = false) String paidCommission,
|
||||||
@RequestParam(required = false) String cooperator, @RequestParam(required = false) String remark){
|
@RequestParam(required = false) String cooperator,
|
||||||
return Response.success(stripeService.updateCouponsInfo(id, paidCommission, cooperator, remark));
|
@RequestParam(required = false) String remark,
|
||||||
|
@RequestParam(required = false) Long startTime){
|
||||||
|
return Response.success(stripeService.updateCouponsInfo(id, paidCommission, cooperator, remark, startTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation("删除推广码")
|
@ApiOperation("删除推广码")
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import lombok.Data;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
@TableName("t_product_coupons")
|
@TableName("t_product_coupons")
|
||||||
@@ -20,6 +22,8 @@ public class ProductCoupons extends BaseEntity{
|
|||||||
private String promotionCodeId;
|
private String promotionCodeId;
|
||||||
// 对应的推广码
|
// 对应的推广码
|
||||||
private String promotionCode;
|
private String promotionCode;
|
||||||
|
// 优惠券有效期开始时间
|
||||||
|
private Long startTime;
|
||||||
// 最大兑换次数
|
// 最大兑换次数
|
||||||
private Long maxRedemptions;
|
private Long maxRedemptions;
|
||||||
// 优惠券的折扣
|
// 优惠券的折扣
|
||||||
@@ -29,13 +33,13 @@ public class ProductCoupons extends BaseEntity{
|
|||||||
// 合作者
|
// 合作者
|
||||||
private String cooperator;
|
private String cooperator;
|
||||||
// 使用了该优惠券支付的总金额
|
// 使用了该优惠券支付的总金额
|
||||||
private float totalEarnings;
|
private BigDecimal totalEarnings = BigDecimal.ZERO;
|
||||||
// 佣金
|
// 佣金
|
||||||
private float commission;
|
private BigDecimal commission = BigDecimal.ZERO;
|
||||||
// 已付佣金
|
// 已付佣金
|
||||||
private float paidCommission;
|
private BigDecimal paidCommission = BigDecimal.ZERO;
|
||||||
// 未付佣金
|
// 未付佣金
|
||||||
private float unpaidCommission;
|
private BigDecimal unpaidCommission = BigDecimal.ZERO;
|
||||||
// 备注
|
// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ public class CreateCouponDTO {
|
|||||||
@NotNull(message = "Please set the commissionRate.")
|
@NotNull(message = "Please set the commissionRate.")
|
||||||
private Float commissionRate;
|
private Float commissionRate;
|
||||||
@ApiModelProperty("推广码到期时间 秒级时间戳")
|
@ApiModelProperty("推广码到期时间 秒级时间戳")
|
||||||
private Long timestamp;
|
private Long endTime;
|
||||||
|
@ApiModelProperty("推广码开始时间 秒级时间戳")
|
||||||
|
private Long startTime;
|
||||||
@ApiModelProperty("推广码最大使用次数")
|
@ApiModelProperty("推广码最大使用次数")
|
||||||
private Long maxRedemptions;
|
private Long maxRedemptions;
|
||||||
@ApiModelProperty("合作者/机构名")
|
@ApiModelProperty("合作者/机构名")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class CheckCouponsVO {
|
public class CheckCouponsVO {
|
||||||
|
|
||||||
@ApiModelProperty("expired || invalid || valid")
|
@ApiModelProperty("expired 过期 || invalid 无效 || valid 有效 || pending 尚未生效")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public interface StripeService {
|
|||||||
|
|
||||||
CheckCouponsVO checkProductCoupon(String promotionCode, Long price);
|
CheckCouponsVO checkProductCoupon(String promotionCode, Long price);
|
||||||
|
|
||||||
ProductCoupons updateCouponsInfo(Long id, Long paidCommission, String cooperator, String remark);
|
ProductCoupons updateCouponsInfo(Long id, String paidCommission, String cooperator, String remark, Long startTime);
|
||||||
|
|
||||||
ProductCoupons getProductCoupon(String promotionCode, String promotionCodeId);
|
ProductCoupons getProductCoupon(String promotionCode, String promotionCodeId);
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,11 @@ import org.springframework.util.CollectionUtils;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -342,49 +344,66 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
|
|||||||
return baseMapper.selectOne(queryWrapper);
|
return baseMapper.selectOne(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void calcCouponsCommission(){
|
public void calcCouponsCommission() {
|
||||||
String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
|
String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
|
||||||
log.info("优惠券佣金计算,上次执行时间:{}", lastTime);
|
log.info("优惠券佣金计算,上次执行时间:{}", lastTime);
|
||||||
String currentTime = LocalDateTime.now().toString();
|
String currentTime = LocalDateTime.now().toString();
|
||||||
// 1、查上次更新之后有无使用了优惠券的新订单
|
|
||||||
|
// 1. 查询新增的优惠券订单
|
||||||
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.eq("trade_state","paid")
|
queryWrapper.eq("trade_state", "paid")
|
||||||
.lt("create_time", currentTime)
|
.lt("create_time", currentTime)
|
||||||
.isNotNull("promotion_code");
|
.isNotNull("promotion_code");
|
||||||
if (!StringUtil.isNullOrEmpty(lastTime)){
|
Optional.ofNullable(lastTime)
|
||||||
queryWrapper.gt("create_time", lastTime);
|
.filter(time -> !time.isEmpty())
|
||||||
}
|
.ifPresent(time -> queryWrapper.gt("create_time", time));
|
||||||
List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);
|
|
||||||
|
List<PaymentInfo> paymentInfos = paymentInfoService.list(queryWrapper);
|
||||||
log.info("目前,新增使用优惠券的订单数:{}", paymentInfos.size());
|
log.info("目前,新增使用优惠券的订单数:{}", paymentInfos.size());
|
||||||
|
|
||||||
// key:推广码, value:用户支付的金额
|
// 2. 按推广码汇总支付金额
|
||||||
HashMap<String, Float> codeAmount = new HashMap<>();
|
Map<String, BigDecimal> codeAmount = paymentInfos.stream()
|
||||||
if (!paymentInfos.isEmpty()){
|
.collect(Collectors.toMap(
|
||||||
for (PaymentInfo paymentInfo : paymentInfos){
|
PaymentInfo::getPromotionCode,
|
||||||
String promotionCode = paymentInfo.getPromotionCode();
|
payment -> new BigDecimal(payment.getPayerTotal()),
|
||||||
Float sum = codeAmount.get(promotionCode);
|
BigDecimal::add
|
||||||
if (sum == null || sum == 0.0f){
|
));
|
||||||
codeAmount.put(promotionCode, paymentInfo.getPayerTotal());
|
|
||||||
}else {
|
// 3. 更新佣金数据
|
||||||
codeAmount.put(promotionCode, sum + paymentInfo.getPayerTotal());
|
codeAmount.forEach((promotionCode, amount) -> {
|
||||||
}
|
ProductCoupons coupon = stripeService.getProductCoupon(promotionCode, null);
|
||||||
|
if (coupon != null) {
|
||||||
|
updateCouponCommission(coupon, amount);
|
||||||
|
productCouponsMapper.updateById(coupon);
|
||||||
}
|
}
|
||||||
for (Map.Entry<String, Float> entry : codeAmount.entrySet()){
|
});
|
||||||
String promotionCode = entry.getKey();
|
|
||||||
ProductCoupons productCoupons = stripeService.getProductCoupon(promotionCode, null);
|
// 4. 更新最后执行时间
|
||||||
if (!Objects.isNull(productCoupons)){
|
|
||||||
// 2、计算支付金额的总和,更新totalEarnings,commission,unpaidCommission
|
|
||||||
float sum = productCoupons.getTotalEarnings() + entry.getValue();
|
|
||||||
productCoupons.setTotalEarnings(sum);
|
|
||||||
float commission = sum * productCoupons.getCommissionRate() / 100;
|
|
||||||
productCoupons.setCommission(commission);
|
|
||||||
productCoupons.setUnpaidCommission(commission - productCoupons.getPaidCommission());
|
|
||||||
productCouponsMapper.updateById(productCoupons);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
|
redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新优惠券的佣金数据
|
||||||
|
*/
|
||||||
|
private void updateCouponCommission(ProductCoupons coupon, BigDecimal newAmount) {
|
||||||
|
// 总收益 = 原收益(默认0) + 新增金额
|
||||||
|
BigDecimal totalEarnings = Optional.ofNullable(coupon.getTotalEarnings())
|
||||||
|
.orElse(BigDecimal.ZERO)
|
||||||
|
.add(newAmount);
|
||||||
|
coupon.setTotalEarnings(totalEarnings);
|
||||||
|
|
||||||
|
// 佣金 = 总收益 × 佣金比例(需除以100)
|
||||||
|
BigDecimal commission = totalEarnings.multiply(
|
||||||
|
BigDecimal.valueOf(coupon.getCommissionRate())
|
||||||
|
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||||
|
coupon.setCommission(commission);
|
||||||
|
|
||||||
|
// 未支付佣金 = 总佣金 - 已支付佣金(默认0)
|
||||||
|
BigDecimal unpaidCommission = commission.subtract(
|
||||||
|
Optional.ofNullable(coupon.getPaidCommission())
|
||||||
|
.orElse(BigDecimal.ZERO));
|
||||||
|
coupon.setUnpaidCommission(unpaidCommission);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1413,8 +1413,8 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
.setDuration(CouponCreateParams.Duration.ONCE)
|
.setDuration(CouponCreateParams.Duration.ONCE)
|
||||||
// percent_off 与 amount_off 不能同时设置
|
// percent_off 与 amount_off 不能同时设置
|
||||||
.setPercentOff(BigDecimal.valueOf(createCouponDTO.getPercentOff()));
|
.setPercentOff(BigDecimal.valueOf(createCouponDTO.getPercentOff()));
|
||||||
if (Objects.nonNull(createCouponDTO.getTimestamp())){
|
if (Objects.nonNull(createCouponDTO.getEndTime())){
|
||||||
couponParams.setRedeemBy(createCouponDTO.getTimestamp());
|
couponParams.setRedeemBy(createCouponDTO.getEndTime());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// 1、创建优惠券
|
// 1、创建优惠券
|
||||||
@@ -1422,9 +1422,10 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
// 2、创建一个推广码
|
// 2、创建一个推广码
|
||||||
PromotionCode promotionCode = createPromotionCode(coupon.getId(), createCouponDTO.getMaxRedemptions());
|
PromotionCode promotionCode = createPromotionCode(coupon.getId(), createCouponDTO.getMaxRedemptions());
|
||||||
// 3、落库
|
// 3、落库
|
||||||
ProductCoupons productCoupons = new ProductCoupons(coupon.getId(), createCouponDTO.getTimestamp(), promotionCode.getId(),
|
ProductCoupons productCoupons = new ProductCoupons(coupon.getId(), createCouponDTO.getEndTime(), promotionCode.getId(),
|
||||||
promotionCode.getCode(), createCouponDTO.getMaxRedemptions(), createCouponDTO.getPercentOff(),
|
promotionCode.getCode(), createCouponDTO.getMaxRedemptions(), createCouponDTO.getPercentOff(),
|
||||||
createCouponDTO.getCommissionRate(), createCouponDTO.getCooperator(), createCouponDTO.getRemark());
|
createCouponDTO.getCommissionRate(), createCouponDTO.getCooperator(), createCouponDTO.getRemark());
|
||||||
|
productCoupons.setStartTime(createCouponDTO.getStartTime());
|
||||||
productCoupons.setCreateTime(LocalDateTime.now());
|
productCoupons.setCreateTime(LocalDateTime.now());
|
||||||
productCouponsMapper.insert(productCoupons);
|
productCouponsMapper.insert(productCoupons);
|
||||||
|
|
||||||
@@ -1450,7 +1451,7 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductCoupons updateCouponsInfo(Long id, Long paidCommission, String cooperator, String remark){
|
public ProductCoupons updateCouponsInfo(Long id, String paidCommission, String cooperator, String remark, Long startTime){
|
||||||
ProductCoupons productCoupons = productCouponsMapper.selectById(id);
|
ProductCoupons productCoupons = productCouponsMapper.selectById(id);
|
||||||
if (Objects.isNull(productCoupons)){
|
if (Objects.isNull(productCoupons)){
|
||||||
throw new BusinessException("Unknown Promotion Code");
|
throw new BusinessException("Unknown Promotion Code");
|
||||||
@@ -1465,7 +1466,22 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
if (Objects.nonNull(paidCommission)){
|
if (Objects.nonNull(paidCommission)){
|
||||||
productCoupons.setPaidCommission(paidCommission);
|
// 将 paidCommission 从 String 转换为 BigDecimal
|
||||||
|
if (!paidCommission.matches("-?\\d+(\\.\\d+)?")) {
|
||||||
|
throw new BusinessException("Invalid paidCommission value: " + paidCommission);
|
||||||
|
}
|
||||||
|
BigDecimal paidCommissionBigDecimal = new BigDecimal(paidCommission);
|
||||||
|
// 设置已支付佣金
|
||||||
|
productCoupons.setPaidCommission(paidCommissionBigDecimal);
|
||||||
|
BigDecimal commission = Objects.isNull(productCoupons.getCommission()) ? new BigDecimal(0) : productCoupons.getCommission();
|
||||||
|
// 计算未支付佣金
|
||||||
|
BigDecimal unpaidCommission = commission.subtract(paidCommissionBigDecimal);
|
||||||
|
// 设置未支付佣金,确保其不为负数
|
||||||
|
productCoupons.setUnpaidCommission(unpaidCommission.compareTo(BigDecimal.ZERO) > 0 ? unpaidCommission : BigDecimal.ZERO);
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
if (Objects.nonNull(startTime)){
|
||||||
|
productCoupons.setStartTime(startTime);
|
||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
if (flag){
|
if (flag){
|
||||||
@@ -1478,6 +1494,7 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
public ProductCoupons checkProductCoupon(String promotionCode){
|
public ProductCoupons checkProductCoupon(String promotionCode){
|
||||||
Stripe.apiKey = privateKey;
|
Stripe.apiKey = privateKey;
|
||||||
Long accountId = UserContext.getUserHolder().getId();
|
Long accountId = UserContext.getUserHolder().getId();
|
||||||
|
String language = UserContext.getUserHolder().getLanguage();
|
||||||
// 1、从数据库查找promotionCode对应的promotionCodeId
|
// 1、从数据库查找promotionCode对应的promotionCodeId
|
||||||
ProductCoupons productCoupons = productCouponsMapper.selectOne(new QueryWrapper<ProductCoupons>().eq("promotion_code", promotionCode));
|
ProductCoupons productCoupons = productCouponsMapper.selectOne(new QueryWrapper<ProductCoupons>().eq("promotion_code", promotionCode));
|
||||||
if (Objects.nonNull(productCoupons)){
|
if (Objects.nonNull(productCoupons)){
|
||||||
@@ -1486,6 +1503,11 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
Long redeemBy = productCoupons.getRedeemBy();
|
Long redeemBy = productCoupons.getRedeemBy();
|
||||||
if (redeemBy < epochSecondNow){
|
if (redeemBy < epochSecondNow){
|
||||||
throw new BusinessException("this.promotion.code.has.expired");
|
throw new BusinessException("this.promotion.code.has.expired");
|
||||||
|
} else if (Objects.nonNull(productCoupons.getStartTime()) && productCoupons.getStartTime() > epochSecondNow) {
|
||||||
|
String startTime = DateUtil.changeTimeStampFormat(productCoupons.getStartTime(), "seconds", CommonConstant.TIME_FORMAT_yyyy_MM_dd_HH_mm_ss);
|
||||||
|
String en = "This coupon will become active on " + startTime + ". Please try again then!";
|
||||||
|
String cn = "该优惠券尚未到生效时间,请在 " + startTime + " 后使用。";
|
||||||
|
throw new BusinessException(language.equals("ENGLISH") ? en : cn);
|
||||||
} else {
|
} else {
|
||||||
// 判断该用户是否有成功使用过这个推广码
|
// 判断该用户是否有成功使用过这个推广码
|
||||||
List<PaymentInfo> paymentInfoByPromCode = paymentInfoService.getPaymentInfoByPromCode(accountId, promotionCode);
|
List<PaymentInfo> paymentInfoByPromCode = paymentInfoService.getPaymentInfoByPromCode(accountId, promotionCode);
|
||||||
@@ -1507,6 +1529,7 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
public CheckCouponsVO checkProductCoupon(String promotionCode, Long price){
|
public CheckCouponsVO checkProductCoupon(String promotionCode, Long price){
|
||||||
Stripe.apiKey = privateKey;
|
Stripe.apiKey = privateKey;
|
||||||
Long accountId = UserContext.getUserHolder().getId();
|
Long accountId = UserContext.getUserHolder().getId();
|
||||||
|
String language = UserContext.getUserHolder().getLanguage();
|
||||||
CheckCouponsVO checkCouponsVO = new CheckCouponsVO();
|
CheckCouponsVO checkCouponsVO = new CheckCouponsVO();
|
||||||
// 1、从数据库查找promotionCode对应的promotionCodeId
|
// 1、从数据库查找promotionCode对应的promotionCodeId
|
||||||
ProductCoupons productCoupons = productCouponsMapper.selectOne(new QueryWrapper<ProductCoupons>().eq("promotion_code", promotionCode));
|
ProductCoupons productCoupons = productCouponsMapper.selectOne(new QueryWrapper<ProductCoupons>().eq("promotion_code", promotionCode));
|
||||||
@@ -1518,7 +1541,13 @@ public class StripeServiceImpl implements StripeService {
|
|||||||
String msg = BusinessException.getMessageFromResource("this.promotion.code.has.expired");
|
String msg = BusinessException.getMessageFromResource("this.promotion.code.has.expired");
|
||||||
checkCouponsVO.setMessage(msg);
|
checkCouponsVO.setMessage(msg);
|
||||||
checkCouponsVO.setStatus("expired");
|
checkCouponsVO.setStatus("expired");
|
||||||
}else {
|
}else if (Objects.nonNull(productCoupons.getStartTime()) && productCoupons.getStartTime() > epochSecondNow) {
|
||||||
|
String startTime = DateUtil.changeTimeStampFormat(productCoupons.getStartTime(), "seconds", CommonConstant.TIME_FORMAT_yyyy_MM_dd_HH_mm_ss);
|
||||||
|
String en = "This coupon will become active on " + startTime + ". Please try again then!";
|
||||||
|
String cn = "该优惠券尚未到生效时间,请在 " + startTime + " 后使用。";
|
||||||
|
checkCouponsVO.setMessage(language.equals("ENGLISH") ? en : cn);
|
||||||
|
checkCouponsVO.setStatus("pending");
|
||||||
|
} else {
|
||||||
// 判断该用户是否有成功使用过这个推广码
|
// 判断该用户是否有成功使用过这个推广码
|
||||||
List<PaymentInfo> paymentInfoByPromCode = paymentInfoService.getPaymentInfoByPromCode(accountId, promotionCode);
|
List<PaymentInfo> paymentInfoByPromCode = paymentInfoService.getPaymentInfoByPromCode(accountId, promotionCode);
|
||||||
if (paymentInfoByPromCode.isEmpty()) {
|
if (paymentInfoByPromCode.isEmpty()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user