|
|
@@ -1,5 +1,9 @@
|
|
|
package com.zsElectric.boot.business.service.impl;
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.zsElectric.boot.core.exception.CouponException;
|
|
|
+import com.zsElectric.boot.business.mapper.CouponTemplateMapper;
|
|
|
+import com.zsElectric.boot.business.model.entity.CouponTemplate;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
@@ -13,18 +17,20 @@ import com.zsElectric.boot.business.model.query.CouponQuery;
|
|
|
import com.zsElectric.boot.business.model.vo.CouponVO;
|
|
|
import com.zsElectric.boot.business.converter.CouponConverter;
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.List;
|
|
|
-import java.util.stream.Collectors;
|
|
|
|
|
|
import cn.hutool.core.lang.Assert;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
/**
|
|
|
* 优惠劵服务实现类
|
|
|
*
|
|
|
* @author zsElectric
|
|
|
- * @since 2025-12-15 09:38
|
|
|
+ * @since 2025-12-19 09:58
|
|
|
*/
|
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
|
@@ -32,6 +38,8 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
|
|
|
|
|
|
private final CouponConverter couponConverter;
|
|
|
|
|
|
+ private final CouponTemplateMapper couponTemplateMapper;
|
|
|
+
|
|
|
/**
|
|
|
* 获取优惠劵分页列表
|
|
|
*
|
|
|
@@ -46,7 +54,7 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
|
|
|
);
|
|
|
return pageVO;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 获取优惠劵表单数据
|
|
|
*
|
|
|
@@ -58,7 +66,7 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
|
|
|
Coupon entity = this.getById(id);
|
|
|
return couponConverter.toForm(entity);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 新增优惠劵
|
|
|
*
|
|
|
@@ -70,7 +78,7 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
|
|
|
Coupon entity = couponConverter.toEntity(formData);
|
|
|
return this.save(entity);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 更新优惠劵
|
|
|
*
|
|
|
@@ -83,7 +91,7 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
|
|
|
Coupon entity = couponConverter.toEntity(formData);
|
|
|
return this.updateById(entity);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 删除优惠劵
|
|
|
*
|
|
|
@@ -100,4 +108,310 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> impleme
|
|
|
return this.removeByIds(idList);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 领取优惠券
|
|
|
+ *
|
|
|
+ * @param templateId 优惠券模板ID
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param takeType 领取类型(1-用户领取,2-后台发放)
|
|
|
+ * @return 优惠券ID
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Long takeCoupon(Long templateId, Long userId, Integer takeType) {
|
|
|
+ // 获取优惠券模板
|
|
|
+ CouponTemplate template = couponTemplateMapper.selectById(templateId);
|
|
|
+ if (template == null) {
|
|
|
+ throw new CouponException("优惠券模板不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查模板状态(1-上线,2-下线)
|
|
|
+ if (template.getStatus() != 1) {
|
|
|
+ throw new CouponException("优惠券模板已下线");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查发放数量限制
|
|
|
+ if (template.getTotalCount() != -1) { // -1表示不限制
|
|
|
+ if (template.getTotalCountAll() != null && template.getTotalCountAll() >= template.getTotalCount()) {
|
|
|
+ throw new CouponException("优惠券已领完");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建优惠券
|
|
|
+ Coupon coupon = new Coupon();
|
|
|
+ coupon.setTemplateId(templateId);
|
|
|
+ coupon.setName(template.getName());
|
|
|
+ coupon.setUserId(userId);
|
|
|
+ coupon.setTakeType(takeType);
|
|
|
+ coupon.setTakeTime(LocalDateTime.now());
|
|
|
+ coupon.setStatus(1); // 未使用
|
|
|
+
|
|
|
+ // 设置过期时间
|
|
|
+ if (template.getFailureTime() != null && template.getFailureTime() > 0) {
|
|
|
+ LocalDateTime expireTime = LocalDateTime.now().plusDays(template.getFailureTime());
|
|
|
+ coupon.setExpireTime(expireTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存优惠券
|
|
|
+ this.save(coupon);
|
|
|
+
|
|
|
+ // 更新模板已发放数量
|
|
|
+ template.setTotalCountAll(template.getTotalCountAll() != null ? template.getTotalCountAll() + 1 : 1);
|
|
|
+ couponTemplateMapper.updateById(template);
|
|
|
+
|
|
|
+ return coupon.getId();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算优惠金额
|
|
|
+ *
|
|
|
+ * @param coupon 优惠券
|
|
|
+ * @param orderAmount 订单金额
|
|
|
+ * @return 优惠金额
|
|
|
+ */
|
|
|
+ public BigDecimal calculateDiscount(Coupon coupon, BigDecimal orderAmount) {
|
|
|
+ // 检查优惠券状态
|
|
|
+ if (!isValidCoupon(coupon.getId())) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取优惠券模板
|
|
|
+ CouponTemplate template = couponTemplateMapper.selectById(coupon.getTemplateId());
|
|
|
+ if (template == null) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据优惠券类型计算优惠金额
|
|
|
+ switch (template.getType()) {
|
|
|
+ case 1: // 折扣券
|
|
|
+ return calculateDiscountCoupon(template, orderAmount);
|
|
|
+ case 2: // 满减券
|
|
|
+ return calculateReductionCoupon(template, orderAmount);
|
|
|
+ case 3: // 无门槛券
|
|
|
+ return calculateNoThresholdCoupon(template);
|
|
|
+ default:
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算折扣券优惠金额
|
|
|
+ *
|
|
|
+ * @param template 优惠券模板
|
|
|
+ * @param orderAmount 订单金额
|
|
|
+ * @return 优惠金额
|
|
|
+ */
|
|
|
+ private BigDecimal calculateDiscountCoupon(CouponTemplate template, BigDecimal orderAmount) {
|
|
|
+ // 检查是否满足使用条件
|
|
|
+ if (template.getUsePrice() != null && orderAmount.compareTo(template.getUsePrice()) < 0) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算折扣金额 = 订单金额 × 折扣率
|
|
|
+ if (template.getDiscountPrice() != null) {
|
|
|
+ return orderAmount.multiply(template.getDiscountPrice()).divide(BigDecimal.valueOf(100));
|
|
|
+ }
|
|
|
+
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算满减券优惠金额
|
|
|
+ *
|
|
|
+ * @param template 优惠券模板
|
|
|
+ * @param orderAmount 订单金额
|
|
|
+ * @return 优惠金额
|
|
|
+ */
|
|
|
+ private BigDecimal calculateReductionCoupon(CouponTemplate template, BigDecimal orderAmount) {
|
|
|
+ // 检查是否满足使用条件
|
|
|
+ if (template.getUsePrice() != null && orderAmount.compareTo(template.getUsePrice()) < 0) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 返回减免金额
|
|
|
+ return template.getDiscountPrice() != null ? template.getDiscountPrice() : BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算无门槛券优惠金额
|
|
|
+ *
|
|
|
+ * @param template 优惠券模板
|
|
|
+ * @return 优惠金额
|
|
|
+ */
|
|
|
+ private BigDecimal calculateNoThresholdCoupon(CouponTemplate template) {
|
|
|
+ // 直接返回减免金额
|
|
|
+ return template.getDiscountPrice() != null ? template.getDiscountPrice() : BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查优惠券是否有效
|
|
|
+ *
|
|
|
+ * @param couponId 优惠券ID
|
|
|
+ * @return 是否有效
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean isValidCoupon(Long couponId) {
|
|
|
+ Coupon coupon = this.getById(couponId);
|
|
|
+ if (coupon == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查优惠券状态(1-未使用,2-已使用,3-已过期)
|
|
|
+ if (coupon.getStatus() != 1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否过期
|
|
|
+ if (coupon.getExpireTime().isBefore(LocalDateTime.now())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查优惠券是否可以使用
|
|
|
+ *
|
|
|
+ * @param couponId 优惠券ID
|
|
|
+ * @param orderAmount 订单金额
|
|
|
+ * @return 是否可以使用
|
|
|
+ */
|
|
|
+ public boolean canUseCoupon(Long couponId, BigDecimal orderAmount) {
|
|
|
+ // 获取优惠券
|
|
|
+ Coupon coupon = this.getById(couponId);
|
|
|
+ if (coupon == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查优惠券状态
|
|
|
+ if (coupon.getStatus() != 1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否过期
|
|
|
+ if (coupon.getExpireTime().isBefore(LocalDateTime.now())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取优惠券模板
|
|
|
+ CouponTemplate template = couponTemplateMapper.selectById(coupon.getTemplateId());
|
|
|
+ if (template == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据优惠券类型检查使用条件
|
|
|
+ switch (template.getType()) {
|
|
|
+ case 1: // 折扣券
|
|
|
+ // 检查是否满足使用条件
|
|
|
+ if (template.getUsePrice() != null && orderAmount.compareTo(template.getUsePrice()) < 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ case 2: // 满减券
|
|
|
+ // 检查是否满足使用条件
|
|
|
+ if (template.getUsePrice() != null && orderAmount.compareTo(template.getUsePrice()) < 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ case 3: // 无门槛券
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用优惠券
|
|
|
+ *
|
|
|
+ * @param couponId 优惠券ID
|
|
|
+ * @param orderId 订单ID
|
|
|
+ * @param orderAmount 订单金额
|
|
|
+ * @return 实际优惠金额
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public BigDecimal useCoupon(Long couponId, Long orderId, BigDecimal orderAmount) {
|
|
|
+ // 获取优惠券
|
|
|
+ Coupon coupon = this.getById(couponId);
|
|
|
+ if (coupon == null) {
|
|
|
+ throw new CouponException("优惠券不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查优惠券状态
|
|
|
+ if (coupon.getStatus() != 1) {
|
|
|
+ throw new CouponException("优惠券状态不正确");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否过期
|
|
|
+ if (coupon.getExpireTime().isBefore(LocalDateTime.now())) {
|
|
|
+ throw new CouponException("优惠券已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算优惠金额
|
|
|
+ BigDecimal discountAmount = calculateDiscount(coupon, orderAmount);
|
|
|
+
|
|
|
+ // 更新优惠券状态为已使用
|
|
|
+ coupon.setStatus(2); // 已使用
|
|
|
+ coupon.setUseOrderId(orderId);
|
|
|
+ coupon.setUseTime(LocalDateTime.now());
|
|
|
+ this.updateById(coupon);
|
|
|
+
|
|
|
+ return discountAmount;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 过期优惠券处理
|
|
|
+ *
|
|
|
+ * @param couponId 优惠券ID
|
|
|
+ * @return 是否处理成功
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean expireCoupon(Long couponId) {
|
|
|
+ Coupon coupon = this.getById(couponId);
|
|
|
+ if (coupon == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查优惠券状态
|
|
|
+ if (coupon.getStatus() != 1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否过期
|
|
|
+ if (coupon.getExpireTime().isAfter(LocalDateTime.now())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新优惠券状态为已过期
|
|
|
+ coupon.setStatus(3); // 已过期
|
|
|
+ return this.updateById(coupon);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户指定状态的优惠券列表
|
|
|
+ *
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param status 优惠券状态
|
|
|
+ * @return 优惠券列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<Coupon> getUserCouponsByStatus(Long userId, Integer status) {
|
|
|
+ return this.list(Wrappers.lambdaQuery(Coupon.class)
|
|
|
+ .eq(Coupon::getUserId, userId)
|
|
|
+ .eq(Coupon::getStatus, status));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据模板ID和用户ID获取用户持有的优惠券
|
|
|
+ *
|
|
|
+ * @param templateId 模板ID
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @return 优惠券列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<Coupon> getCouponsByTemplateAndUser(Long templateId, Long userId) {
|
|
|
+ return this.list(Wrappers.lambdaQuery(Coupon.class)
|
|
|
+ .eq(Coupon::getTemplateId, templateId)
|
|
|
+ .eq(Coupon::getUserId, userId));
|
|
|
+ }
|
|
|
+
|
|
|
}
|