Переглянути джерело

Merge remote-tracking branch 'origin/sit' into uat-20260202

dujian 1 день тому
батько
коміт
b91b004e21

+ 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;
 

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreClockInVo.java

@@ -45,6 +45,9 @@ public class StoreClockInVo extends StoreClockIn {
     @ApiModelProperty(value = "评论数量")
     private int commentCount;
 
+    @ApiModelProperty(value = "店铺评价条数(common_rating,字段对应库表语义 rating_count;未删除且审核非失败)")
+    private int ratingCount;
+
     @ApiModelProperty(value = "商家评分")
     private double score;
 

+ 11 - 4
alien-entity/src/main/java/shop/alien/mapper/StoreClockInMapper.java

@@ -31,10 +31,17 @@ public interface StoreClockInMapper extends BaseMapper<StoreClockIn> {
             " where uorder.store_id = store.id and uorder.status = '1' and uorder.delete_flag = 0 " +
             ") perCapitaPrice, " +
             "( " +
-            " select ifnull(round(avg(score), 1), 0) " +
-            " from store_evaluation eval " +
-            " where eval.store_id = store.id and eval.delete_flag = 0 " +
-            ") score " +
+            " select ifnull(round(avg(cr_avg.score), 1), 0) " +
+            " from common_rating cr_avg " +
+            " where cr_avg.business_type = 1 and cr_avg.business_id = store.id and cr_avg.delete_flag = 0 " +
+            " and (cr_avg.audit_status is null or cr_avg.audit_status <> 2) " +
+            ") score, " +
+            "( " +
+            " select ifnull(count(1), 0) " +
+            " from common_rating cr_cnt " +
+            " where cr_cnt.business_type = 1 and cr_cnt.business_id = store.id and cr_cnt.delete_flag = 0 " +
+            " and (cr_cnt.audit_status is null or cr_cnt.audit_status <> 2) " +
+            ") rating_count " +
             "from store_clock_in clock " +
             "join life_user user on user.id = clock.user_id " +
             "join store_info store on store.id = clock.store_id " +

+ 6 - 0
alien-store/src/main/java/shop/alien/store/service/LifeDiscountCouponStoreFriendService.java

@@ -36,6 +36,12 @@ public interface LifeDiscountCouponStoreFriendService extends IService<LifeDisco
      */
     List<LifeDiscountCouponStoreFriendVo> issueFriendCoupon(LifeDiscountCouponStoreFriendDto lifeDiscountCouponStoreFriendDto);
 
+
+    /**
+     * (新)消费后发放好友优惠券并返回列表
+     */
+    List<LifeDiscountCouponStoreFriendVo> newIssueFriendCoupon(LifeDiscountCouponStoreFriendDto lifeDiscountCouponStoreFriendDto);
+
     /**
      * 获取该订单发放的好友列表
      */

+ 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-收藏店铺

+ 176 - 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;
@@ -82,6 +84,8 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
 
     private final CommonRatingMapper commonRatingMapper;
 
+    private final UserReservationOrderMapper userReservationOrderMapper;
+
     @Override
     public List<LifeDiscountCouponStoreFriendVo> getFriendCouponList(UserLoginInfo userLoginInfo, String friendUserId) {
         List<LifeDiscountCouponStoreFriendVo> result = new ArrayList<>();
@@ -386,8 +390,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-好友赠送
@@ -456,6 +465,155 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
     }
 
     @Override
+    public List<LifeDiscountCouponStoreFriendVo> newIssueFriendCoupon(LifeDiscountCouponStoreFriendDto lifeDiscountCouponStoreFriendDto) {
+        // 用于存储最终成功发放的优惠券信息的列表
+        List<LifeDiscountCouponStoreFriendVo> result = new ArrayList<>();
+
+        //判断该订单是否已经发放过好友店铺优惠券
+//        LifeUserOrder lifeUserOrder = lifeUserOrderMapper.selectById(lifeDiscountCouponStoreFriendDto.getOrderId());
+//        if (lifeUserOrder.getSendDiscountCouponFlag() == 1) {
+//            throw new RuntimeException("已发放优惠券");
+//        }
+        UserReservationOrder userReservationOrder = userReservationOrderMapper.selectById(lifeDiscountCouponStoreFriendDto.getOrderId());
+
+
+        //送券规则
+        List<Integer> couponList = new ArrayList<>();
+        LambdaQueryWrapper<LifeDiscountCouponFriendRule> ruleLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        ruleLambdaQueryWrapper.eq(LifeDiscountCouponFriendRule::getStoreId, userReservationOrder.getStoreId())
+                .eq(LifeDiscountCouponFriendRule::getDeleteFlag, 0);
+        List<LifeDiscountCouponFriendRule> lifeDiscountCouponFriendRules = lifeDiscountCouponFriendRuleMapper.selectList(ruleLambdaQueryWrapper);
+        lifeDiscountCouponFriendRules = lifeDiscountCouponFriendRules.stream().filter(i -> i.getMoneyLow().compareTo(userReservationOrder.getDepositAmount()) <= 0 && i.getMoneyHigh().compareTo(userReservationOrder.getDepositAmount()) >= 0).collect(Collectors.toList());
+        if (ObjectUtils.isNotEmpty(lifeDiscountCouponFriendRules)) {
+            LambdaQueryWrapper<LifeDiscountCouponFriendRuleDetail> detailLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            detailLambdaQueryWrapper.in(LifeDiscountCouponFriendRuleDetail::getRuleId, lifeDiscountCouponFriendRules.stream().map(LifeDiscountCouponFriendRule::getId).collect(Collectors.toList()));
+            List<LifeDiscountCouponFriendRuleDetail> lifeDiscountCouponFriendRuleDetails = lifeDiscountCouponFriendRuleDetailMapper.selectList(detailLambdaQueryWrapper);
+            couponList = lifeDiscountCouponFriendRuleDetails.stream().map(LifeDiscountCouponFriendRuleDetail::getCouponId).collect(Collectors.toList());
+        }
+
+
+//        lifeUserOrder.setSendDiscountCouponFlag(1);
+//        lifeUserOrderMapper.updateById(lifeUserOrder);
+
+        //有符合规则的优惠券
+        if (ObjectUtils.isNotEmpty(couponList) && !couponList.isEmpty()) {
+            // 获取当前消费用户的ID
+            int userId = Integer.parseInt(userReservationOrder.getUserId().toString());
+            LifeUser lifeUser = lifeUserMapper.selectById(userId);
+            // 从传入的DTO对象中获取店铺ID
+            StoreUser storeUser =
+                    storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, userReservationOrder.getStoreId()));
+            Integer storeUserId = storeUser.getId();
+            Integer storeId = storeUser.getStoreId();
+            StoreInfo storeInfo = storeInfoMapper.selectById(storeUser.getStoreId());
+
+            // 根据店铺ID从数据库中查询该店铺为好友设定的所有优惠券信息
+            List<LifeDiscountCouponStoreFriend> lifeDiscountCouponStoreFriends = lifeDiscountCouponStoreFriendMapper.selectList(
+                    // 使用LambdaQueryWrapper构建查询条件,筛选出店铺ID等于指定店铺ID的优惠券记录,并且发布状态为已发布
+                    new LambdaQueryWrapper<LifeDiscountCouponStoreFriend>().eq(LifeDiscountCouponStoreFriend::getStoreUserId, storeId)
+                            .eq(LifeDiscountCouponStoreFriend::getReleaseType, 1)
+                            .in(LifeDiscountCouponStoreFriend::getCouponId, couponList));
+
+            // 获取当前日期,用于后续判断优惠券是否在有效期内
+            LocalDate currentDate = LocalDate.now();
+            if (!lifeDiscountCouponStoreFriends.isEmpty()) {
+                // 遍历该店铺为好友设定的所有优惠券信息
+                for (LifeDiscountCouponStoreFriend coupon : lifeDiscountCouponStoreFriends) {
+                    // 创建一个Lambda查询包装器,用于构建查询优惠券的条件
+                    LambdaQueryWrapper<LifeDiscountCoupon> queryWrapper = new LambdaQueryWrapper<>();
+                    // 设置查询条件:优惠券的领取状态为可领取
+                    queryWrapper.eq(LifeDiscountCoupon::getGetStatus, DiscountCouponEnum.CAN_GET.getValue());
+                    // 设置查询条件:优惠券的ID等于当前遍历到的优惠券ID
+                    queryWrapper.eq(LifeDiscountCoupon::getId, coupon.getCouponId());
+                    // 设置查询条件:优惠券的结束日期大于等于当前日期,即优惠券在有效期内
+                    queryWrapper.ge(LifeDiscountCoupon::getValidDate, currentDate);
+                    // 设置查询条件:优惠券还有库存
+                    queryWrapper.gt(LifeDiscountCoupon::getSingleQty, 0);
+
+                    // 根据上述查询条件从数据库中查询符合条件的优惠券信息
+                    LifeDiscountCoupon lifeDiscountCoupon = lifeDiscountCouponMapper.selectOne(queryWrapper);
+
+                    // 如果查询到符合条件的优惠券,说明该优惠券可领
+                    if (lifeDiscountCoupon != null) {
+                        // 设定本次领取优惠券的数量,这里固定为1张
+                        Integer receiveQuantity = 1;
+
+                        // 循环领取优惠券,根据设定的领取数量进行多次领取操作
+                        for (int i = 0; i < receiveQuantity; i++) {
+                            // 创建一个新的用户优惠券记录对象
+                            LifeDiscountCouponUser lifeDiscountCouponUser = new LifeDiscountCouponUser();
+                            // 设置该优惠券记录的优惠券ID
+                            lifeDiscountCouponUser.setCouponId(coupon.getCouponId());
+                            // 设置该优惠券记录的用户ID为当前用户ID
+                            lifeDiscountCouponUser.setUserId(userId);
+                            // 设置该优惠券的领取时间为当前时间
+                            lifeDiscountCouponUser.setReceiveTime(new Date());
+                            // 设置该优惠券的过期时间为优惠券本身的结束日期
+                            lifeDiscountCouponUser.setExpirationTime(lifeDiscountCoupon.getValidDate());
+                            // 设置该优惠券的状态为待使用
+                            lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
+                            lifeDiscountCouponUser.setIssueSource(4); // 4-好友赠送
+                            // 将该用户优惠券记录插入到数据库中
+                            lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
+                        }
+
+                        // 削减该优惠券的库存,根据领取数量减少库存
+                        coupon.setSingleQty(coupon.getSingleQty() - receiveQuantity);
+                        // 更新数据库中该优惠券的库存信息
+                        lifeDiscountCouponStoreFriendMapper.updateById(coupon);
+
+                        // 创建一个用于存储优惠券信息的VO对象
+                        LifeDiscountCouponStoreFriendVo lifeDiscountCouponStoreFriendVo = new LifeDiscountCouponStoreFriendVo();
+                        // 将优惠券信息复制到VO对象中
+                        BeanUtils.copyProperties(coupon, lifeDiscountCouponStoreFriendVo);
+                        // 将该VO对象添加到最终结果列表中
+                        result.add(lifeDiscountCouponStoreFriendVo);
+                    }
+                }
+
+                //存储发放日志
+                LifeDiscountCouponStoreFriendSendRecord lifeDiscountCouponStoreFriendSendRecord = new LifeDiscountCouponStoreFriendSendRecord();
+                //存入门店id
+                lifeDiscountCouponStoreFriendSendRecord.setStoreId(Integer.parseInt(userReservationOrder.getStoreId().toString()));
+                //存入接收人id
+                lifeDiscountCouponStoreFriendSendRecord.setUserId(userId);
+                //存入发放人id
+                lifeDiscountCouponStoreFriendSendRecord.setStoreUserId(storeUserId);
+                //存入订单id
+                lifeDiscountCouponStoreFriendSendRecord.setOrderId(Integer.parseInt(userReservationOrder.getId().toString()));
+                //存入优惠券ids
+                String[] couponIds = result.stream()
+                        .map(LifeDiscountCouponStoreFriendVo::getCouponId)
+                        .map(String::valueOf)  // 将 Integer 转换为 String
+                        .toArray(String[]::new);
+
+                String join = String.join(",", couponIds);
+                lifeDiscountCouponStoreFriendSendRecord.setDiscountCouponIds(join);
+                lifeDiscountCouponStoreFriendSendRecordMapper.insert(lifeDiscountCouponStoreFriendSendRecord);
+
+                //发送公告消息
+                LifeNotice lifeNotice = new LifeNotice();
+                //存入发送人
+                lifeNotice.setSenderId("system");
+                //存入接收人
+                lifeNotice.setReceiverId("user_" + lifeUser.getUserPhone());
+                //存入信息
+                String text = storeInfo.getStoreName() + "赠送了您他的好友店铺优惠券,快去我的券包查看吧~";
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("message", text);
+                lifeNotice.setContext(jsonObject.toJSONString());
+                //存入类型
+                lifeNotice.setNoticeType(1);
+                lifeNotice.setTitle("赠劵通知");
+                lifeNoticeMapper.insert(lifeNotice);
+
+            }
+        }
+            // 返回成功发放的优惠券信息列表
+            return result;
+    }
+
+    @Override
     public List<LifeDiscountCouponStoreFriendVo> getSendCoupons(LifeDiscountCouponStoreFriendDto lifeDiscountCouponStoreFriendDto) {
         List<LifeDiscountCouponStoreFriendVo> result = new ArrayList<>();
         Integer orderId = lifeDiscountCouponStoreFriendDto.getOrderId();
@@ -959,12 +1117,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 +1176,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 +1207,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);

+ 8 - 7
alien-store/src/main/java/shop/alien/store/service/impl/StoreReservationServiceImpl.java

@@ -12,6 +12,7 @@ import shop.alien.entity.store.StoreInfo;
 import shop.alien.entity.store.UserReservation;
 import shop.alien.entity.store.UserReservationTable;
 import shop.alien.entity.store.UserReservationOrder;
+import shop.alien.entity.store.dto.LifeDiscountCouponStoreFriendDto;
 import shop.alien.entity.store.vo.StoreReservationListVo;
 import shop.alien.entity.store.StoreBookingBusinessHours;
 import shop.alien.entity.store.StoreBookingSettings;
@@ -22,13 +23,7 @@ import shop.alien.mapper.UserReservationTableMapper;
 import shop.alien.mapper.EssentialHolidayComparisonMapper;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.listener.RedisKeyExpirationHandler;
-import shop.alien.store.service.StoreBookingTableService;
-import shop.alien.store.service.StoreInfoService;
-import shop.alien.store.service.MerchantPaymentOrderService;
-import shop.alien.store.service.StoreReservationService;
-import shop.alien.store.service.UserReservationOrderService;
-import shop.alien.store.service.StoreBookingBusinessHoursService;
-import shop.alien.store.service.StoreBookingSettingsService;
+import shop.alien.store.service.*;
 import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategy;
 import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategyFactory;
 import shop.alien.store.util.ali.AliSms;
@@ -78,6 +73,7 @@ public class StoreReservationServiceImpl extends ServiceImpl<StoreReservationMap
     private final StoreBookingSettingsService storeBookingSettingsService;
     private final EssentialHolidayComparisonMapper essentialHolidayComparisonMapper;
     private final MerchantPaymentOrderService merchantPaymentOrderService;
+    private final LifeDiscountCouponStoreFriendService lifeDiscountCouponStoreFriendService;
 
     /** 预约状态:已取消 */
     private static final int STATUS_CANCELLED = 3;
@@ -788,6 +784,11 @@ public class StoreReservationServiceImpl extends ServiceImpl<StoreReservationMap
             throw new RuntimeException("更新订单状态失败");
         }
 
+        //发放好友优惠券
+        LifeDiscountCouponStoreFriendDto lifeDiscountCouponStoreFriendDto = new LifeDiscountCouponStoreFriendDto();
+        lifeDiscountCouponStoreFriendDto.setOrderId(Integer.parseInt(order.getId().toString()));
+        lifeDiscountCouponStoreFriendService.newIssueFriendCoupon(lifeDiscountCouponStoreFriendDto);
+
         // 核销成功后,发送订金退款短信和通知
         try {
             sendDepositRefundSmsAndNoticeInternal(reservation, order);

+ 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;
+    }
+}