浏览代码

评论有礼,收藏领券 的有效期时间 变为 领取时间加有效期天数

lutong 3 天之前
父节点
当前提交
ce4d561c57

+ 9 - 12
alien-dining/src/main/java/shop/alien/dining/service/impl/DiningServiceImpl.java

@@ -14,9 +14,11 @@ import shop.alien.mapper.*;
 import shop.alien.dining.config.BaseRedisService;
 import shop.alien.dining.service.CartService;
 import shop.alien.dining.service.DiningService;
+import shop.alien.util.coupon.DiscountCouponExpirationUtil;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -299,19 +301,14 @@ public class DiningServiceImpl implements DiningService {
         userCoupon.setUserId(userId);
         userCoupon.setCouponId(couponId);
         userCoupon.setReceiveTime(new Date());
+        userCoupon.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                userCoupon.getReceiveTime(),
+                coupon.getSpecifiedDay(),
+                coupon.getExpirationDate(),
+                coupon.getValidDate(),
+                coupon.getEndDate(),
+                ZoneId.systemDefault()));
         userCoupon.setStatus(0); // 待使用
-        // 注意:如果需要设置 issueSource,需要确保 LifeDiscountCouponUser 实体类已包含该字段
-        if (coupon.getSpecifiedDay() != null && !coupon.getSpecifiedDay().isEmpty()) {
-            try {
-                int days = Integer.parseInt(coupon.getSpecifiedDay());
-                LocalDate expirationDate = LocalDate.now().plusDays(days);
-                userCoupon.setExpirationTime(expirationDate);
-            } catch (NumberFormatException e) {
-                userCoupon.setExpirationTime(coupon.getEndDate());
-            }
-        } else {
-            userCoupon.setExpirationTime(coupon.getEndDate());
-        }
         lifeDiscountCouponUserMapper.insert(userCoupon);
 
         // 更新库存

+ 1 - 1
alien-entity/src/main/java/shop/alien/entity/store/LifeDiscountCouponUser.java

@@ -44,7 +44,7 @@ public class LifeDiscountCouponUser extends Model<LifeDiscountCouponUser> {
     @TableField(value = "receive_time")
     private Date receiveTime;
 
-    @ApiModelProperty(value = "到期时间(等同于优惠券主表该优惠券截止时间)")
+    @ApiModelProperty(value = "到期时间(按领取日 + 券模板有效期天数计算;无天数时回退模板有效/结束日期)")
     @TableField(value = "expiration_time")
     private LocalDate expirationTime;
 

+ 43 - 31
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java

@@ -24,9 +24,11 @@ import shop.alien.store.service.LifeDiscountCouponQuantumRulesService;
 import shop.alien.store.service.LifeDiscountCouponService;
 import shop.alien.store.service.LifeDiscountCouponUserService;
 import shop.alien.util.common.constant.DiscountCouponEnum;
+import shop.alien.util.coupon.DiscountCouponExpirationUtil;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -991,7 +993,16 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         vo.setVoucherId(lc.getId());
         // quantityClaimed表示该券的总领取数量,在用户券列表中不设置或设置为0
         vo.setQuantityClaimed(null);
-        vo.setExpirationTime(userCoupon.getExpirationTime());
+        LocalDate exp = userCoupon.getExpirationTime();
+        if (exp == null) {
+            exp = DiscountCouponExpirationUtil.resolveLifeCouponVoucherExpiration(
+                    userCoupon.getReceiveTime(),
+                    lc.getExpirationDate(),
+                    lc.getEndDate(),
+                    zoneId);
+        }
+        vo.setExpirationTime(exp);
+        vo.setValidDate(exp);
         vo.setReachUseTimeFlag(1);
         
         // 判断是否到了使用开始时间
@@ -1002,14 +1013,10 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
             }
         }
         
-        // 设置有效期
-        if (lc.getEndDate() != null) {
-            vo.setValidDate(lc.getEndDate().toInstant().atZone(zoneId).toLocalDate());
-        }
-        
         // 设置店铺信息
         StoreInfo storeInfo = storeInfoMap.get(lc.getStoreId());
         if (storeInfo != null) {
+            vo.setStoreName(storeInfo.getStoreName());
             vo.setBusinessSection(storeInfo.getBusinessSection());
             vo.setBusinessSectionName(storeInfo.getBusinessSectionName());
         }
@@ -1030,22 +1037,19 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         
         // quantityClaimed表示该券的总领取数量,在用户券列表中不设置或设置为0
         vo.setQuantityClaimed(null);
-        vo.setExpirationTime(userCoupon.getExpirationTime());
-        
-        // 设置有效期(根据指定天数计算)
-        String specifiedDay = coupon.getSpecifiedDay();
-        if (!StringUtils.isEmpty(specifiedDay)) {
-            try {
-                int sDay = Integer.parseInt(specifiedDay);
-                if (sDay > 0 && userCoupon.getReceiveTime() != null) {
-                    Date validDate = addDaysToDateJava8(userCoupon.getReceiveTime(), sDay);
-                    vo.setValidDate(validDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
-                }
-            } catch (NumberFormatException e) {
-                // 忽略解析错误
-            }
-        }
-        
+        LocalDate exp = userCoupon.getExpirationTime();
+        if (exp == null) {
+            exp = DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                    userCoupon.getReceiveTime(),
+                    coupon.getSpecifiedDay(),
+                    coupon.getExpirationDate(),
+                    coupon.getValidDate(),
+                    coupon.getEndDate(),
+                    zoneId);
+        }
+        vo.setExpirationTime(exp);
+        vo.setValidDate(exp);
+
         // 判断是否到了优惠券的开始时间
         vo.setReachUseTimeFlag(1);
         if (coupon.getBeginGetDate() != null && localNow.isBefore(coupon.getBeginGetDate())) {
@@ -1056,6 +1060,7 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         if (!StringUtils.isEmpty(coupon.getStoreId())) {
             StoreInfo storeInfo = storeInfoMap.get(coupon.getStoreId());
             if (storeInfo != null) {
+                vo.setStoreName(storeInfo.getStoreName());
                 vo.setBusinessSection(storeInfo.getBusinessSection());
                 vo.setBusinessSectionName(storeInfo.getBusinessSectionName());
             }
@@ -1651,7 +1656,8 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         if (couponType != null && couponType == 2) {
             // 折扣券:显示折扣率和最低消费
             if (lifeDiscountCoupon.getDiscountRate() != null) {
-                forbiddenRule += "折扣率:" + lifeDiscountCoupon.getDiscountRate() + "折;";
+                BigDecimal zheShu = lifeDiscountCoupon.getDiscountRate().divide(BigDecimal.TEN, 2, RoundingMode.HALF_UP);
+                forbiddenRule += "折扣率:" + zheShu.stripTrailingZeros().toPlainString() + "折;";
             }
         } else {
             // 满减券(默认):显示面值和最低消费
@@ -1874,7 +1880,13 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
             lifeDiscountCouponUser.setUserId(userId);
             lifeDiscountCouponUser.setCouponId(couponId);
             lifeDiscountCouponUser.setReceiveTime(new Date());
-            lifeDiscountCouponUser.setExpirationTime(LocalDate.now().plusDays(Long.parseLong(lifeDiscountCoupon.getSpecifiedDay())));
+            lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                    lifeDiscountCouponUser.getReceiveTime(),
+                    lifeDiscountCoupon.getSpecifiedDay(),
+                    lifeDiscountCoupon.getExpirationDate(),
+                    lifeDiscountCoupon.getValidDate(),
+                    lifeDiscountCoupon.getEndDate(),
+                    ZoneId.systemDefault()));
             lifeDiscountCouponUser.setStatus(0);
             lifeDiscountCouponUser.setDeleteFlag(0);
             lifeDiscountCouponUser.setCreatedTime(new Date());
@@ -2051,13 +2063,13 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
                     lifeDiscountCouponUser.setCouponId(latestCoupon.getId());
                     lifeDiscountCouponUser.setUserId(userId);
                     lifeDiscountCouponUser.setReceiveTime(new Date());
-                    
-                    // 设置过期时间:优先使用validDate,如果为null则使用endDate
-                    LocalDate expirationTime = latestCoupon.getValidDate();
-                    if (expirationTime == null && latestCoupon.getEndDate() != null) {
-                        expirationTime = latestCoupon.getEndDate();
-                    }
-                    lifeDiscountCouponUser.setExpirationTime(expirationTime);
+                    lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                            lifeDiscountCouponUser.getReceiveTime(),
+                            latestCoupon.getSpecifiedDay(),
+                            latestCoupon.getExpirationDate(),
+                            latestCoupon.getValidDate(),
+                            latestCoupon.getEndDate(),
+                            ZoneId.systemDefault()));
                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                     lifeDiscountCouponUser.setDeleteFlag(0);
                     lifeDiscountCouponUser.setIssueSource(2); // 2-收藏店铺

+ 25 - 13
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponStoreFriendServiceImpl.java

@@ -22,8 +22,10 @@ import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.LifeDiscountCouponStoreFriendService;
 import shop.alien.util.common.constant.DiscountCouponEnum;
+import shop.alien.util.coupon.DiscountCouponExpirationUtil;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.util.ArrayList;
@@ -386,8 +388,13 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                             lifeDiscountCouponUser.setUserId(userId);
                             // 设置该优惠券的领取时间为当前时间
                             lifeDiscountCouponUser.setReceiveTime(new Date());
-                            // 设置该优惠券的过期时间为优惠券本身的结束日期
-                            lifeDiscountCouponUser.setExpirationTime(lifeDiscountCoupon.getValidDate());
+                            lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                                    lifeDiscountCouponUser.getReceiveTime(),
+                                    lifeDiscountCoupon.getSpecifiedDay(),
+                                    lifeDiscountCoupon.getExpirationDate(),
+                                    lifeDiscountCoupon.getValidDate(),
+                                    lifeDiscountCoupon.getEndDate(),
+                                    ZoneId.systemDefault()));
                             // 设置该优惠券的状态为待使用
                             lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                             lifeDiscountCouponUser.setIssueSource(4); // 4-好友赠送
@@ -959,12 +966,13 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                     lifeDiscountCouponUser.setCouponId(coupon.getId());
                     lifeDiscountCouponUser.setUserId(commenterUserId);
                     lifeDiscountCouponUser.setReceiveTime(new Date());
-                    // 设置过期时间:优先使用validDate,如果为null则使用endDate
-                    LocalDate expirationTime = coupon.getValidDate();
-                    if (expirationTime == null && coupon.getEndDate() != null) {
-                        expirationTime = coupon.getEndDate();
-                    }
-                    lifeDiscountCouponUser.setExpirationTime(expirationTime);
+                    lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                            lifeDiscountCouponUser.getReceiveTime(),
+                            coupon.getSpecifiedDay(),
+                            coupon.getExpirationDate(),
+                            coupon.getValidDate(),
+                            coupon.getEndDate(),
+                            ZoneId.systemDefault()));
                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                     lifeDiscountCouponUser.setDeleteFlag(0);
                     lifeDiscountCouponUser.setIssueSource(3); // 3-好评送券
@@ -1017,9 +1025,11 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                     lifeDiscountCouponUser.setVoucherId(lifeCoupon.getId());
                     lifeDiscountCouponUser.setUserId(commenterUserId);
                     lifeDiscountCouponUser.setReceiveTime(new Date());
-                    if (lifeCoupon.getEndDate() != null) {
-                        lifeDiscountCouponUser.setExpirationTime(lifeCoupon.getEndDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
-                    }
+                    lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeCouponVoucherExpiration(
+                            lifeDiscountCouponUser.getReceiveTime(),
+                            lifeCoupon.getExpirationDate(),
+                            lifeCoupon.getEndDate(),
+                            ZoneId.systemDefault()));
                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                     lifeDiscountCouponUser.setDeleteFlag(0);
                     lifeDiscountCouponUser.setIssueSource(3); // 3-好评送券(代金券)
@@ -1046,10 +1056,12 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                 if (lifeDiscountCoupon != null) {
                     Integer couponType = lifeDiscountCoupon.getCouponType();
                     if (couponType != null && couponType == 2) {
-                        // 折扣券
+                        // 折扣券:discountRate 为实付比例 0-100,与券模板一致(10=付10%=1折,80=8折)
                         BigDecimal discountRate = lifeDiscountCoupon.getDiscountRate();
                         if (discountRate != null) {
-                            couponDetailText = "(" + discountRate + "折";
+                            BigDecimal zheShu = discountRate.divide(BigDecimal.TEN, 2, RoundingMode.HALF_UP);
+                            String zheShuText = zheShu.stripTrailingZeros().toPlainString();
+                            couponDetailText = "(" + zheShuText + "折";
                             if (lifeDiscountCoupon.getMinimumSpendingAmount() != null) {
                                 couponDetailText += ",满" + lifeDiscountCoupon.getMinimumSpendingAmount() + "可用";
                             }

+ 9 - 30
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponUserServiceImpl.java

@@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.BeanUtils;
-import org.springframework.beans.BeansException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import shop.alien.entity.result.R;
@@ -22,8 +21,8 @@ import shop.alien.mapper.StoreInfoMapper;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.LifeDiscountCouponUserService;
 import shop.alien.util.common.constant.DiscountCouponEnum;
+import shop.alien.util.coupon.DiscountCouponExpirationUtil;
 
-import java.time.Instant;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.util.Date;
@@ -66,9 +65,8 @@ public class LifeDiscountCouponUserServiceImpl extends ServiceImpl<LifeDiscountC
             LifeDiscountCoupon lifeDiscountCoupon = lifeDiscountCouponMapper.selectById(lifeDiscountCouponUserDto.getCouponId());
             // 判断是否在领取时间内
             Date now = new Date();
-            Instant instant = now.toInstant();
             ZoneId zoneId = ZoneId.systemDefault();
-            LocalDate localNow = instant.atZone(zoneId).toLocalDate();
+            LocalDate localNow = now.toInstant().atZone(zoneId).toLocalDate();
             int startResult = localNow.compareTo(lifeDiscountCoupon.getBeginGetDate());
             if (startResult < 0) {
                 return R.fail("该优惠劵活动时间未开始");
@@ -86,25 +84,18 @@ public class LifeDiscountCouponUserServiceImpl extends ServiceImpl<LifeDiscountC
                 return R.fail("已发放完毕");
             }
 
-            String specifiedDay = lifeDiscountCoupon.getSpecifiedDay();
-
             //判断领取数量
             for (int i = 0; i < receiveQuantity; i++) {
                 LifeDiscountCouponUser lifeDiscountCouponUser = new LifeDiscountCouponUser();
                 BeanUtils.copyProperties(lifeDiscountCouponUserDto, lifeDiscountCouponUser);
                 lifeDiscountCouponUser.setReceiveTime(new Date());
-
-                // 存入过期时间, 领取时间加上specifiedDay
-                if(!StringUtils.isEmpty(specifiedDay)){
-                    int sDay = Integer.parseInt(specifiedDay);
-                    if(sDay > 0 && lifeDiscountCoupon.getBeginGetDate() != null) {
-                        Date validDate = addDaysToDateJava8(new Date(), sDay);
-                        LocalDate validDateLocalDate = validDate.toInstant()
-                                .atZone(ZoneId.systemDefault())
-                                .toLocalDate();
-                        lifeDiscountCouponUser.setExpirationTime(validDateLocalDate);
-                    }
-                }
+                lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                        lifeDiscountCouponUser.getReceiveTime(),
+                        lifeDiscountCoupon.getSpecifiedDay(),
+                        lifeDiscountCoupon.getExpirationDate(),
+                        lifeDiscountCoupon.getValidDate(),
+                        lifeDiscountCoupon.getEndDate(),
+                        ZoneId.systemDefault()));
 
                 //存入状态  待使用:0
                 lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
@@ -146,16 +137,4 @@ public class LifeDiscountCouponUserServiceImpl extends ServiceImpl<LifeDiscountC
         }
         return R.success("领取成功");
     }
-
-    public static Date addDaysToDateJava8(Date date, int days) {
-        // 将Date转换为Instant
-        Instant instant = date.toInstant();
-
-        // 转换为LocalDate并加天数
-        LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
-        LocalDate newLocalDate = localDate.plusDays(days);
-
-        // 转换回Date
-        return Date.from(newLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
-    }
 }

+ 15 - 4
alien-store/src/main/java/shop/alien/store/service/impl/LifeGroupPackageServiceImpl.java

@@ -18,6 +18,7 @@ import shop.alien.store.service.LifeGroupPackageService;
 import shop.alien.store.util.GroupConstant;
 import shop.alien.util.common.UniqueRandomNumGenerator;
 import shop.alien.util.common.constant.DiscountCouponEnum;
+import shop.alien.util.coupon.DiscountCouponExpirationUtil;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
@@ -149,8 +150,13 @@ public class LifeGroupPackageServiceImpl extends ServiceImpl<LifeGroupPackageMap
                                     lifeDiscountCouponUser.setUserId(Integer.valueOf(lifeUserOrder.getUserId()));
                                     // 设置该优惠券的领取时间为当前时间
                                     lifeDiscountCouponUser.setReceiveTime(new Date());
-                                    // 设置该优惠券的过期时间为优惠券本身的结束日期
-                                    lifeDiscountCouponUser.setExpirationTime(insertedCoupon.getEndDate());
+                                    lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                                            lifeDiscountCouponUser.getReceiveTime(),
+                                            insertedCoupon.getSpecifiedDay(),
+                                            insertedCoupon.getExpirationDate(),
+                                            insertedCoupon.getValidDate(),
+                                            insertedCoupon.getEndDate(),
+                                            ZoneId.systemDefault()));
                                     // 设置该优惠券的状态为待使用
                                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                                     lifeDiscountCouponUser.setIssueSource(5); // 5-平台发放(团购套餐补偿)
@@ -280,8 +286,13 @@ public class LifeGroupPackageServiceImpl extends ServiceImpl<LifeGroupPackageMap
                                     lifeDiscountCouponUser.setUserId(Integer.valueOf(lifeUserOrder.getUserId()));
                                     // 设置该优惠券的领取时间为当前时间
                                     lifeDiscountCouponUser.setReceiveTime(new Date());
-                                    // 设置该优惠券的过期时间为优惠券本身的结束日期
-                                    lifeDiscountCouponUser.setExpirationTime(insertedCoupon.getEndDate());
+                                    lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                                            lifeDiscountCouponUser.getReceiveTime(),
+                                            insertedCoupon.getSpecifiedDay(),
+                                            insertedCoupon.getExpirationDate(),
+                                            insertedCoupon.getValidDate(),
+                                            insertedCoupon.getEndDate(),
+                                            ZoneId.systemDefault()));
                                     // 设置该优惠券的状态为待使用
                                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                                     lifeDiscountCouponUser.setIssueSource(5); // 5-平台发放(团购套餐补偿)

+ 9 - 2
alien-store/src/main/java/shop/alien/store/service/impl/StorePlatformBenefitsServiceImpl.java

@@ -18,7 +18,9 @@ import shop.alien.mapper.*;
 import shop.alien.store.service.StorePlatformBenefitsService;
 import shop.alien.util.common.JwtUtil;
 import shop.alien.util.common.constant.DiscountCouponEnum;
+import shop.alien.util.coupon.DiscountCouponExpirationUtil;
 
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -259,8 +261,13 @@ public class StorePlatformBenefitsServiceImpl extends ServiceImpl<StorePlatformB
         lifeDiscountCouponUser.setUserId(userId);
         // 设置该优惠券的领取时间为当前时间
         lifeDiscountCouponUser.setReceiveTime(new Date());
-        // 设置该优惠券的过期时间为优惠券本身的结束日期
-        lifeDiscountCouponUser.setExpirationTime(lifeDiscountCoupon.getEndDate());
+        lifeDiscountCouponUser.setExpirationTime(DiscountCouponExpirationUtil.resolveLifeDiscountCouponExpiration(
+                lifeDiscountCouponUser.getReceiveTime(),
+                lifeDiscountCoupon.getSpecifiedDay(),
+                lifeDiscountCoupon.getExpirationDate(),
+                lifeDiscountCoupon.getValidDate(),
+                lifeDiscountCoupon.getEndDate(),
+                ZoneId.systemDefault()));
         // 设置该优惠券的状态为待使用
         lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
         lifeDiscountCouponUser.setWelfareId(benefitsId);

+ 76 - 0
alien-util/src/main/java/shop/alien/util/coupon/DiscountCouponExpirationUtil.java

@@ -0,0 +1,76 @@
+package shop.alien.util.coupon;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Date;
+
+/**
+ * 用户优惠券/代金券到期日:以领取时间为起点叠加有效期天数;无有效天数配置时再回退模板上的绝对截止日期。
+ */
+public final class DiscountCouponExpirationUtil {
+
+    private DiscountCouponExpirationUtil() {
+    }
+
+    /**
+     * 满减、折扣等 life_discount_coupon 用户券:领取日 + 有效期(天)。
+     * 天数优先取模板 {@code specifiedDay},其次 {@code expirationDate} 整型天数字段。
+     * 若均未配置正整数天数,则依次回退模板 {@code validDate}、{@code endDate}。
+     */
+    public static LocalDate resolveLifeDiscountCouponExpiration(
+            Date receiveTime,
+            String specifiedDay,
+            Integer expirationDate,
+            LocalDate templateValidDate,
+            LocalDate templateEndDate,
+            ZoneId zoneId) {
+        ZoneId z = zoneId != null ? zoneId : ZoneId.systemDefault();
+        Date rt = receiveTime != null ? receiveTime : new Date();
+        LocalDate receiveDate = rt.toInstant().atZone(z).toLocalDate();
+
+        Integer days = parseValidityDayCount(specifiedDay, expirationDate);
+        if (days != null && days > 0) {
+            return receiveDate.plusDays(days);
+        }
+        if (templateValidDate != null) {
+            return templateValidDate;
+        }
+        return templateEndDate;
+    }
+
+    /**
+     * life_coupon 代金券用户券:领取日 + {@code expirationDate}(天);无天数则用券 {@code endDate} 对应日期。
+     */
+    public static LocalDate resolveLifeCouponVoucherExpiration(
+            Date receiveTime,
+            Integer voucherExpirationDays,
+            Date voucherEndDate,
+            ZoneId zoneId) {
+        ZoneId z = zoneId != null ? zoneId : ZoneId.systemDefault();
+        Date rt = receiveTime != null ? receiveTime : new Date();
+        LocalDate receiveDate = rt.toInstant().atZone(z).toLocalDate();
+        if (voucherExpirationDays != null && voucherExpirationDays > 0) {
+            return receiveDate.plusDays(voucherExpirationDays);
+        }
+        if (voucherEndDate != null) {
+            return voucherEndDate.toInstant().atZone(z).toLocalDate();
+        }
+        return null;
+    }
+
+    private static Integer parseValidityDayCount(String specifiedDay, Integer expirationDate) {
+        if (specifiedDay != null && !specifiedDay.trim().isEmpty()) {
+            try {
+                int d = Integer.parseInt(specifiedDay.trim());
+                if (d > 0) {
+                    return d;
+                }
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        if (expirationDate != null && expirationDate > 0) {
+            return expirationDate;
+        }
+        return null;
+    }
+}