Преглед изворни кода

修改优惠券 评论逻辑 处理BUG

lutong пре 2 месеци
родитељ
комит
1b5562dbd8

+ 17 - 3
alien-entity/src/main/java/shop/alien/mapper/CommonRatingMapper.java

@@ -13,6 +13,7 @@ import org.apache.ibatis.annotations.Update;
 import shop.alien.entity.store.CommonRating;
 import shop.alien.entity.store.vo.StoreInfoScoreVo;
 
+import java.util.Date;
 import java.util.Map;
 
 /**
@@ -89,11 +90,24 @@ public interface CommonRatingMapper extends BaseMapper<CommonRating> {
 
     /**
      * 统计该用户在该店铺下已通过审核的好评次数(用于好评送券参与次数限制)
+     * 注意:统计包括已删除的评论,防止用户删除评论后再次获得券
+     * 只统计活动开始时间之后创建的评论,活动开始前的评论不计入参与次数
      * @param userId 用户ID
      * @param storeId 店铺ID(business_id)
-     * @return 已通过的好评条数(business_type=1, audit_status=1, score>=4.5)
+     * @param activityStartTime 活动开始时间,为null时统计所有历史好评
+     * @return 已通过的好评条数(business_type=1, audit_status=1, score>=4.5),包括已删除的评论
      */
-    @Select("SELECT COUNT(*) FROM common_rating WHERE business_type = 1 AND business_id = #{storeId} AND user_id = #{userId} AND audit_status = 1 AND score >= 4.5 AND delete_flag = 0")
-    int countPassedGoodRatingsByUserAndStore(@Param("userId") Long userId, @Param("storeId") Integer storeId);
+    @Select("<script>" +
+            "SELECT COUNT(*) FROM common_rating " +
+            "WHERE business_type = 1 " +
+            "AND business_id = #{storeId} " +
+            "AND user_id = #{userId} " +
+            "AND audit_status = 1 " +
+            "AND score >= 4.5 " +
+            "<if test='activityStartTime != null'>" +
+            "AND created_time >= #{activityStartTime} " +
+            "</if>" +
+            "</script>")
+    int countPassedGoodRatingsByUserAndStore(@Param("userId") Long userId, @Param("storeId") Integer storeId, @Param("activityStartTime") Date activityStartTime);
 }
 

+ 22 - 1
alien-store/src/main/java/shop/alien/store/controller/CommonRatingController.java

@@ -88,9 +88,30 @@ public class CommonRatingController {
     public R<Integer> add(@RequestBody CommonRating commonRating) {
         log.info("CommonRatingController.add?commonRating={}", commonRating);
         try {
-            return R.data(commonRatingService.saveCommonRating(commonRating));
+            // 参数校验
+            if (commonRating.getBusinessId() == null) {
+                return R.fail("参数错误:businessId不能为空");
+            }
+            if (commonRating.getBusinessType() == null) {
+                return R.fail("参数错误:businessType不能为空");
+            }
+            if (commonRating.getUserId() == null) {
+                return R.fail("参数错误:userId不能为空");
+            }
+            
+            Integer result = commonRatingService.saveCommonRating(commonRating);
+            if (result == 0) {
+                return R.data(result);
+            } else if (result == 2) {
+                return R.fail("评价内容包含敏感字符,审核不通过");
+            } else {
+                return R.fail("保存评价失败");
+            }
         } catch (IllegalArgumentException e) {
             return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("新增评价异常", e);
+            return R.fail("保存评价失败:" + e.getMessage());
         }
     }
 

+ 12 - 1
alien-store/src/main/java/shop/alien/store/service/LifeDiscountCouponStoreFriendService.java

@@ -67,6 +67,17 @@ public interface LifeDiscountCouponStoreFriendService extends IService<LifeDisco
 
     /**
      * 好评送券:用户好评且AI审核通过后,按运营活动配置的优惠券/代金券ID发放到用户券包,发放成功后扣减库存;按券类型发送对应通知。
+     * 方法内部会查询活动配置。
+     *
+     * @param userId   评价用户ID(life用户)
+     * @param storeId  店铺ID(businessId)
+     * @return 发放数量(优惠券+代金券),0表示未发放
+     */
+    int issueCouponForGoodRating(Integer userId, Integer storeId);
+    
+    /**
+     * 好评送券:直接按指定的优惠券/代金券ID发放到用户券包,发放成功后扣减库存;按券类型发送对应通知。
+     * 用于支持多个活动同时生效的场景。
      *
      * @param userId   评价用户ID(life用户)
      * @param storeId  店铺ID(businessId)
@@ -74,5 +85,5 @@ public interface LifeDiscountCouponStoreFriendService extends IService<LifeDisco
      * @param voucherId 代金券ID(life_coupon),为null则不发代金券
      * @return 发放数量(优惠券+代金券),0表示未发放
      */
-    int issueCouponForGoodRating(Integer userId, Integer storeId);
+    int issueCouponForGoodRating(Integer userId, Integer storeId, Integer couponId, Integer voucherId);
 }

+ 129 - 35
alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java

@@ -98,8 +98,25 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
      */
     @Override
     public Integer saveCommonRating(CommonRating commonRating) {
-        // 1. 文本审核 + 视频审核(评价有图片和视频
+        // 1. 先进行AI审核,审核通过后再保存(防止绕过审核
         try {
+            // 参数校验:必填字段不能为空
+            if (commonRating.getBusinessId() == null) {
+                log.error("保存评价失败:businessId不能为空");
+                return 1;
+            }
+            if (commonRating.getBusinessType() == null) {
+                log.error("保存评价失败:businessType不能为空");
+                return 1;
+            }
+            if (commonRating.getUserId() == null) {
+                log.error("保存评价失败:userId不能为空");
+                return 1;
+            }
+            
+            // 强制设置审核状态为待审核,防止用户绕过审核
+            commonRating.setAuditStatus(0);
+            
             // 手动存评分1,2,3
             if (StringUtils.isNotEmpty(commonRating.getOtherScore())) {
                 JSONObject parse = JSONObject.parse(commonRating.getOtherScore());
@@ -107,27 +124,47 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
                 commonRating.setScoreTwo(parse.getDouble("scoreTwo"));
                 commonRating.setScoreThree(parse.getDouble("scoreThree"));
             }
-            int i = this.save(commonRating) ? 0 : 1;
-            // 好评发券改为仅在 AI 审核通过后执行,见 doBusinessWithType 中逻辑
+            
+            // 2. 先进行文本和图片审核(同步审核,必须通过才能保存)
+            // 处理imageUrls可能为null的情况
+            String imageUrlsStr = commonRating.getImageUrls();
+            List<String> imageUrlList = new ArrayList<>();
+            if (StringUtils.isNotEmpty(imageUrlsStr)) {
+                imageUrlList = Arrays.asList(imageUrlsStr.split(","));
+            }
+            
             // 一次遍历完成分类,避免多次流式处理
-            Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(Arrays.asList(commonRating.getImageUrls().split(",")));
+            Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(imageUrlList);
+            
+            // 3. 文本和图片审核(同步,必须通过)
             AiContentModerationUtil.AuditResult auditResult = new AiContentModerationUtil.AuditResult(true, "");
-            if( StringUtils.isNotEmpty(commonRating.getContent()) || urlCategoryMap.get("image").size() > 0){
+            // 只要有内容或图片,就必须审核
+            if (StringUtils.isNotEmpty(commonRating.getContent()) || !urlCategoryMap.get("image").isEmpty()) {
                 auditResult = aiContentModerationUtil.auditContent(commonRating.getContent(), urlCategoryMap.get("image"));
             }
+            
+            // 4. 文本/图片审核不通过,直接返回,不保存
             if (!auditResult.isPassed()) {
-                // 审核不通过
-                CommonRating rating = this.getById(commonRating.getId());
-                rating.setAuditStatus(2);
-                rating.setAuditReason(auditResult.getFailureReason());
-                this.saveOrUpdate(rating);
-            } else{
+                log.warn("评价审核不通过,不保存评论。原因:{}", auditResult.getFailureReason());
+                return 2; // 返回2表示文本内容异常
+            }
+            
+            // 5. 文本/图片审核通过,保存评论(状态为待审核,等待视频审核)
+            int i = this.save(commonRating) ? 0 : 1;
+            if (i != 0) {
+                log.error("保存评价失败");
+                return 1;
+            }
+            
+            // 6. 如果有视频,进行异步视频审核
+            List<String> videoUrls = urlCategoryMap.get("video");
+            if (!videoUrls.isEmpty()) {
                 CompletableFuture.runAsync(() -> {
                     AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
                     try {
                         // 调用审核接口,增加超时控制(避免接口挂死)
                         videoAuditResult = CompletableFuture.supplyAsync(
-                                () -> aiVideoModerationUtil.auditVideos(urlCategoryMap.get("video")),
+                                () -> aiVideoModerationUtil.auditVideos(videoUrls),
                                 commonVideoTaskExecutor
                         ).get();
 
@@ -136,41 +173,60 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
                             // 重新查询最新的rating,避免并发覆盖
                             CommonRating rating = this.getById(commonRating.getId());
                             if (Objects.isNull(rating)) {
-                                log.error("视频审核后更新失败,rating,ID:{}", rating.getId());
+                                log.error("视频审核后更新失败,rating ID:{}", commonRating.getId());
                                 return;
                             }
                             rating.setAuditStatus(2);
                             // 失败原因
                             rating.setAuditReason(videoAuditResult.getFailureReason());
                             this.saveOrUpdate(rating);
-                            log.info("视频审核不通过,已更新状态,requirementID:{},原因:{}",
+                            log.info("视频审核不通过,已更新状态,ratingID:{},原因:{}",
                                     rating.getId(), videoAuditResult.getFailureReason());
                         } else if (Objects.nonNull(videoAuditResult) && videoAuditResult.isPassed()) {
-                            // 审核通过也更新状态(可选,根据业务需求)
+                            // 审核通过也更新状态
                             CommonRating rating = this.getById(commonRating.getId());
                             if (Objects.nonNull(rating)) {
                                 rating.setAuditStatus(1);
-//                                latestRequirement.setAuditReason(null);
                                 this.saveOrUpdate(rating);
                                 // 对不同的businessType进行不同的处理,
                                 doBusinessWithType(commonRating);
-                                log.info("视频审核通过,已更新状态,requirementID:{}", rating.getId());
+                                log.info("视频审核通过,已更新状态,ratingID:{}", rating.getId());
+                            }
+                        } else {
+                            // 视频审核结果为空,可能是没有视频或审核服务异常
+                            // 如果文本/图片已通过,且没有视频,则设置为审核通过
+                            CommonRating rating = this.getById(commonRating.getId());
+                            if (Objects.nonNull(rating)) {
+                                rating.setAuditStatus(1);
+                                this.saveOrUpdate(rating);
+                                doBusinessWithType(commonRating);
+                                log.info("无视频或视频审核结果为空,文本/图片已通过,设置为审核通过,ratingID:{}", rating.getId());
                             }
                         }
                     } catch (Exception e) {
-                        log.error("视频审核接口调用异常,commonRating:{}", commonRating.getId(), e);
+                        log.error("视频审核接口调用异常,commonRating ID:{}", commonRating.getId(), e);
                         CommonRating rating = this.getById(commonRating.getId());
                         if (Objects.nonNull(rating)) {
                             rating.setAuditStatus(2);
-                            rating.setAuditReason("视频审核接口调用异常:" + e.getMessage()); // 实际需捕获具体异常信息
+                            rating.setAuditReason("视频审核接口调用异常:" + e.getMessage());
                             this.saveOrUpdate(rating);
                         }
                     }
                 }, commonVideoTaskExecutor);
+            } else {
+                // 7. 没有视频,文本/图片审核已通过,直接设置为审核通过
+                CommonRating rating = this.getById(commonRating.getId());
+                if (Objects.nonNull(rating)) {
+                    rating.setAuditStatus(1);
+                    this.saveOrUpdate(rating);
+                    doBusinessWithType(commonRating);
+                    log.info("无视频,文本/图片审核已通过,设置为审核通过,ratingID:{}", rating.getId());
+                }
             }
+            
             return i;
         } catch (Exception e) {
-            log.error("CommonRatingService.saveCommonRating ERROR Msg={}", e.getMessage());
+            log.error("CommonRatingService.saveCommonRating ERROR Msg={}", e.getMessage(), e);
             return 1;
         }
     }
@@ -225,35 +281,73 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
                 webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
             }
             // 用户好评且 AI 审核通过后:按运营活动表(评论有礼)配置的 coupon_id/voucher_id 发放,用 participation_limit 限制参与次数,发放成功后扣减库存,按优惠券/代金券发对应通知
+            // 支持多个活动同时生效:如果一个商家有多个正在开启的活动(如一个送优惠券,一个送代金券),都会执行
             if (score != null && score >= 4.5 && commonRating.getUserId() != null && commonRating.getBusinessId() != null) {
                 try {
                     Date now = new Date();
+                    // 查询所有符合条件的活动(不再限制为1个)
                     LambdaQueryWrapper<StoreOperationalActivity> activityWrapper = new LambdaQueryWrapper<>();
                     activityWrapper.eq(StoreOperationalActivity::getStoreId, businessId)
                             .eq(StoreOperationalActivity::getActivityType, "COMMENT")
                             .in(StoreOperationalActivity::getStatus, 5, 8)
                             .le(StoreOperationalActivity::getStartTime, now)
                             .ge(StoreOperationalActivity::getEndTime, now)
-                            .orderByDesc(StoreOperationalActivity::getId)
-                            .last("LIMIT 1");
-                    StoreOperationalActivity activity = storeOperationalActivityMapper.selectOne(activityWrapper);
-                    if (activity == null || (activity.getCouponId() == null && activity.getVoucherId() == null)) {
+                            .orderByDesc(StoreOperationalActivity::getId); // 按ID降序,优先处理最新创建的活动
+                    List<StoreOperationalActivity> activities = storeOperationalActivityMapper.selectList(activityWrapper);
+                    
+                    if (activities == null || activities.isEmpty()) {
+                        log.info("CommonRatingService 好评送券:未找到符合条件的活动,userId={}, storeId={}", 
+                                commonRating.getUserId(), businessId);
                         return;
                     }
-                    Integer limit = activity.getParticipationLimit();
-                    if (limit != null && limit > 0) {
-                        int passedCount = commonRatingMapper.countPassedGoodRatingsByUserAndStore(commonRating.getUserId(), businessId);
-                        if (passedCount > limit) {
-                            log.info("CommonRatingService 好评送券跳过:超过运营活动参与次数 participation_limit={}, count={}, userId={}, storeId={}",
-                                    limit, passedCount, commonRating.getUserId(), businessId);
-                            return;
+                    
+                    // 遍历所有活动,分别处理每个活动
+                    int totalIssuedCount = 0;
+                    for (StoreOperationalActivity activity : activities) {
+                        // 跳过没有配置优惠券或代金券的活动
+                        if (activity.getCouponId() == null && activity.getVoucherId() == null) {
+                            log.info("CommonRatingService 好评送券跳过:活动未配置优惠券或代金券,activityId={}, userId={}, storeId={}", 
+                                    activity.getId(), commonRating.getUserId(), businessId);
+                            continue;
+                        }
+                        
+                        // 检查参与次数限制(每个活动单独计算)
+                        Integer limit = activity.getParticipationLimit();
+                        if (limit != null && limit > 0) {
+                            // 只统计活动开始时间之后的好评,活动开始前的评论不计入参与次数
+                            int passedCount = commonRatingMapper.countPassedGoodRatingsByUserAndStore(
+                                    commonRating.getUserId(), businessId, activity.getStartTime());
+                            if (passedCount > limit) {
+                                log.info("CommonRatingService 好评送券跳过:超过运营活动参与次数 activityId={}, participation_limit={}, count={}, userId={}, storeId={}, activityStartTime={}",
+                                        activity.getId(), limit, passedCount, commonRating.getUserId(), businessId, activity.getStartTime());
+                                continue; // 跳过该活动,继续处理下一个
+                            }
+                        }
+                        
+                        // 发放优惠券/代金券(使用4参数版本,直接传入活动配置的券ID)
+                        int issuedCount = lifeDiscountCouponStoreFriendService.issueCouponForGoodRating(
+                                Integer.valueOf(Math.toIntExact(commonRating.getUserId())), businessId, 
+                                activity.getCouponId(), activity.getVoucherId());
+                        
+                        if (issuedCount > 0) {
+                            totalIssuedCount += issuedCount;
+                            log.info("CommonRatingService 好评送券成功:activityId={}, 发放数量={}, userId={}, storeId={}, couponId={}, voucherId={}",
+                                    activity.getId(), issuedCount, commonRating.getUserId(), businessId, 
+                                    activity.getCouponId(), activity.getVoucherId());
+                        } else {
+                            log.warn("CommonRatingService 好评送券失败:activityId={}, 发放数量=0, userId={}, storeId={}, couponId={}, voucherId={}",
+                                    activity.getId(), commonRating.getUserId(), businessId, 
+                                    activity.getCouponId(), activity.getVoucherId());
                         }
                     }
-//                    lifeDiscountCouponStoreFriendService.issueCouponForGoodRating(
-//                            Math.toIntExact(commonRating.getUserId()), businessId,
-//                            activity.getCouponId(), activity.getVoucherId());
+                    
+                    if (totalIssuedCount > 0) {
+                        log.info("CommonRatingService 好评送券汇总:总发放数量={}, 活动数={}, userId={}, storeId={}",
+                                totalIssuedCount, activities.size(), commonRating.getUserId(), businessId);
+                    }
                 } catch (Exception ex) {
-                    log.warn("CommonRatingService 好评送券异常 userId={}, storeId={}, msg={}", commonRating.getUserId(), businessId, ex.getMessage());
+                    log.warn("CommonRatingService 好评送券异常 userId={}, storeId={}, msg={}", 
+                            commonRating.getUserId(), businessId, ex.getMessage(), ex);
                 }
             }
         }

+ 65 - 9
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponStoreFriendServiceImpl.java

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.stereotype.Service;
@@ -39,6 +40,7 @@ import java.util.stream.Collectors;
  * @author ssk
  * @since 2025-02-19
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDiscountCouponStoreFriendMapper, LifeDiscountCouponStoreFriend> implements LifeDiscountCouponStoreFriendService {
@@ -75,6 +77,8 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
 
     private final StoreOperationalActivityMapper storeOperationalActivityMapper;
 
+    private final CommonRatingMapper commonRatingMapper;
+
     @Override
     public List<LifeDiscountCouponStoreFriendVo> getFriendCouponList(UserLoginInfo userLoginInfo, String friendUserId) {
         List<LifeDiscountCouponStoreFriendVo> result = new ArrayList<>();
@@ -678,6 +682,20 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
         if (activity == null || (activity.getCouponId() == null && activity.getVoucherId() == null)) {
             return 0;
         }
+        
+        // 检查参与次数限制
+        Integer limit = activity.getParticipationLimit();
+        if (limit != null && limit > 0) {
+            // 只统计活动开始时间之后的好评,活动开始前的评论不计入参与次数
+            int passedCount = commonRatingMapper.countPassedGoodRatingsByUserAndStore(
+                    Long.valueOf(userId), storeId, activity.getStartTime());
+            if (passedCount >= limit) {
+                log.info("issueCouponForGoodRating 好评送券跳过:超过运营活动参与次数 participation_limit={}, count={}, userId={}, storeId={}, activityStartTime={}",
+                        limit, passedCount, userId, storeId, activity.getStartTime());
+                return 0;
+            }
+        }
+        
         // 调用4参数方法进行发放
         return issueCouponForGoodRating(userId, storeId, activity.getCouponId(), activity.getVoucherId());
     }
@@ -697,6 +715,8 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
         LifeUser lifeUser = lifeUserMapper.selectById(commenterUserId);
         int grantedCoupon = 0;
         int grantedVoucher = 0;
+        String couponName = null;  // 优惠券名称
+        String voucherName = null;  // 代金券名称
 
         // 按优惠券ID发放:发放1张,扣减库存,发优惠券通知
         if (couponId != null && couponId > 0) {
@@ -715,8 +735,9 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                     coupon.setSingleQty(coupon.getSingleQty() - 1);
                     lifeDiscountCouponMapper.updateById(coupon);
                     grantedCoupon = 1;
+                    couponName = coupon.getName();  // 保存优惠券名称
                 } catch (Exception e) {
-
+                    log.error("发放优惠券失败,userId={}, storeId={}, couponId={}, error={}", userId, storeId, couponId, e.getMessage(), e);
                 }
             }
         }
@@ -740,41 +761,76 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                     lifeCoupon.setSingleQty(lifeCoupon.getSingleQty() - 1);
                     lifeCouponMapper.updateById(lifeCoupon);
                     grantedVoucher = 1;
+                    voucherName = lifeCoupon.getName();  // 保存代金券名称
                 } catch (Exception e) {
-
+                    log.error("发放代金券失败,userId={}, storeId={}, voucherId={}, error={}", userId, storeId, voucherId, e.getMessage(), e);
                 }
             }
         }
 
-        // 根据发放的是优惠券还是代金券发送对应通知
+        // 根据发放的是优惠券还是代金券发送对应通知(明确区分券类型和名称)
         if (lifeUser != null && storeInfo != null) {
-            if (grantedCoupon > 0) {
+            if (grantedCoupon > 0 && couponName != null) {
                 LifeNotice couponNotice = new LifeNotice();
                 couponNotice.setSenderId("system");
                 couponNotice.setReceiverId("user_" + lifeUser.getUserPhone());
-                String couponText = "您对店铺「" + storeInfo.getStoreName() + "」的好评已通过审核,已为您发放优惠券,快去我的券包查看吧~";
+                // 明确标注是优惠券,并包含券的具体名称
+                String couponText = "您对店铺「" + storeInfo.getStoreName() + "」的好评已通过审核,已为您发放优惠券「" + couponName + "」,快去我的券包查看吧~";
                 JSONObject couponJson = new JSONObject();
                 couponJson.put("message", couponText);
+                couponJson.put("couponType", "优惠券");  // 明确标识券类型
+                couponJson.put("couponName", couponName);  // 券名称
                 couponNotice.setContext(couponJson.toJSONString());
                 couponNotice.setNoticeType(1);
-                couponNotice.setTitle("好评送券");
+                couponNotice.setTitle("好评送优惠券");  // 标题明确标注是优惠券
                 couponNotice.setIsRead(0);
                 couponNotice.setDeleteFlag(0);
                 lifeNoticeMapper.insert(couponNotice);
+                
+                // WebSocket 实时推送通知
+                try {
+                    WebSocketVo websocketVo = new WebSocketVo();
+                    websocketVo.setSenderId("system");
+                    websocketVo.setReceiverId("user_" + lifeUser.getUserPhone());
+                    websocketVo.setCategory("notice");
+                    websocketVo.setNoticeType("1");
+                    websocketVo.setIsRead(0);
+                    websocketVo.setText(JSONObject.from(couponNotice).toJSONString());
+                    webSocketProcess.sendMessage("user_" + lifeUser.getUserPhone(), JSONObject.from(websocketVo).toJSONString());
+                } catch (Exception e) {
+                    log.error("好评送优惠券 WebSocket 推送失败,userId={}, storeId={}, couponId={}, error={}", userId, storeId, couponId, e.getMessage(), e);
+                }
             }
-            if (grantedVoucher > 0) {
+            if (grantedVoucher > 0 && voucherName != null) {
                 LifeNotice voucherNotice = new LifeNotice();
                 voucherNotice.setSenderId("system");
                 voucherNotice.setReceiverId("user_" + lifeUser.getUserPhone());
-                String voucherText = "您对店铺「" + storeInfo.getStoreName() + "」的好评已通过审核,已为您发放代金券,快去我的券包查看吧~";
+                // 明确标注是代金券,并包含券的具体名称
+                String voucherText = "您对店铺「" + storeInfo.getStoreName() + "」的好评已通过审核,已为您发放代金券「" + voucherName + "」,快去我的券包查看吧~";
                 JSONObject voucherJson = new JSONObject();
                 voucherJson.put("message", voucherText);
+                voucherJson.put("couponType", "代金券");  // 明确标识券类型
+                voucherJson.put("couponName", voucherName);  // 券名称
                 voucherNotice.setContext(voucherJson.toJSONString());
                 voucherNotice.setNoticeType(1);
-                voucherNotice.setTitle("好评送代金券");
+                voucherNotice.setTitle("好评送代金券");  // 标题明确标注是代金券
                 voucherNotice.setIsRead(0);
                 voucherNotice.setDeleteFlag(0);
                 lifeNoticeMapper.insert(voucherNotice);
+                
+                // WebSocket 实时推送通知
+                try {
+                    WebSocketVo websocketVo = new WebSocketVo();
+                    websocketVo.setSenderId("system");
+                    websocketVo.setReceiverId("user_" + lifeUser.getUserPhone());
+                    websocketVo.setCategory("notice");
+                    websocketVo.setNoticeType("1");
+                    websocketVo.setIsRead(0);
+                    websocketVo.setText(JSONObject.from(voucherNotice).toJSONString());
+                    webSocketProcess.sendMessage("user_" + lifeUser.getUserPhone(), JSONObject.from(websocketVo).toJSONString());
+                } catch (Exception e) {
+                    log.error("好评送代金券 WebSocket 推送失败,userId={}, storeId={}, voucherId={}, error={}", userId, storeId, voucherId, e.getMessage(), e);
+                }
             }
         }