2 Commits 747285a9eb ... cb17e630dd

Autor SHA1 Mensaje Fecha
  lutong cb17e630dd Merge branch 'release_lutong_bug' into sit hace 1 semana
  lutong 5c8f40a8e3 (预生产环境)店铺详情(提测0328):更多推荐列表中的店铺未显示评分和评价条数 hace 1 semana

+ 2 - 0
alien-entity/src/main/resources/mapper/StoreStaffReviewMapper.xml

@@ -115,6 +115,7 @@
             AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
             AND llr.delete_flag = 0
         WHERE ssr.delete_flag = 0
+          AND ssr.audit_status = 1
         AND ssr.id = #{reviewId}
         LIMIT 1
     </select>
@@ -126,6 +127,7 @@
         FROM store_staff_review
         WHERE staff_user_id = #{staffUserId}
           AND delete_flag = 0
+          AND audit_status = 1
           AND overall_rating IS NOT NULL
     </select>
 

+ 16 - 20
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -5221,20 +5221,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         Map<String, String> typeMap = storeDictionaries.stream().collect(Collectors.toMap(StoreDictionary::getDictId, StoreDictionary::getDictDetail));
 
 
-        // 计算平均分和评价
-        Map<String, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
-        Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
-
-        // 注意:需要将store_id转换为String类型,与后续containsKey判断保持一致
-        avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
-        List<StoreComment> storeComments = storeCommentMapper.selectList(
-                new QueryWrapper<StoreComment>()
-                        .eq("business_type", "5")
-                        .eq("delete_flag", 0));
-        commentMap = storeComments.stream()
-                .filter(comment -> comment.getStoreId() != null) // 过滤无店铺ID的评论,避免 groupingBy NPE
-                .collect(Collectors.groupingBy(StoreComment::getStoreId));
-
+        // 平均分仍取自历史聚合(store_comment);评价条数 totalNum 在循环内按 common_rating 统计,与 getStoreMainInfo 口径一致
+        Map<String, List<Map<String, Object>>> avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream()
+                .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
 
         // 查询入口头图
         List<Integer> storeIds = storeInfoVoList.stream()
@@ -5305,18 +5294,25 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 record.setDaysToExpire(daysToExpire);
             }
 
-            // 设置店铺得分,设置店铺人均消费,设置总评论数
+            // 设置店铺得分;评价条数取自 common_rating(商铺评价 business_type=1),不含已废弃的 store_comment
             if (avgScoreMap.containsKey(String.valueOf(record.getId()))) {
                 record.setAvgScore(String.valueOf(avgScoreMap.get(String.valueOf(record.getId())).get(0).get("avg_score")));
             } else {
                 record.setAvgScore("0");
             }
-            // 设置店铺得分,设置店铺人均消费,设置总评论数
-            if (commentMap.containsKey(record.getId())) {
-                record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
-            } else {
-                record.setTotalNum("0");
+            String ratingCountStr = "0";
+            try {
+                long n = commonRatingMapper.selectCount(new QueryWrapper<CommonRating>()
+                        .eq("business_type", 1)
+                        .eq("business_id", record.getId())
+                        .eq("delete_flag", 0)
+                        .and(w -> w.in("audit_status", 0, 1).or().isNull("audit_status")));
+                ratingCountStr = String.valueOf(n);
+            } catch (Exception e) {
+                log.warn("更多推荐-店铺评价条数(common_rating)计算失败, storeId={}", record.getId(), e);
             }
+            record.setTotalNum(ratingCountStr);
+            record.setCommitCount(ratingCountStr);
 
         }
 

+ 20 - 1
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffCommentServiceImpl.java

@@ -65,11 +65,14 @@ public class StoreStaffCommentServiceImpl extends ServiceImpl<StoreStaffCommentM
             return R.fail(validateResult.getMsg());
         }
 
-        // 验证评价是否存在
+        // 验证评价是否存在且已通过审核(未通过 AI 的评价不允许首评,避免 comment_count 等指标被撬开)
         StoreStaffReview review = storeStaffReviewService.getById(comment.getReviewId());
         if (review == null || review.getDeleteFlag() == 1) {
             return R.fail("评价不存在或已删除");
         }
+        if (review.getAuditStatus() == null || review.getAuditStatus() != 1) {
+            return R.fail("评价未通过展示审核,暂不能评论");
+        }
 
         // 设置评论属性
         setCommentDefaults(comment, review);
@@ -97,6 +100,14 @@ public class StoreStaffCommentServiceImpl extends ServiceImpl<StoreStaffCommentM
             return R.fail("评价ID不能为空");
         }
 
+        StoreStaffReview review = storeStaffReviewService.getById(reviewId);
+        if (review == null || review.getDeleteFlag() == 1) {
+            return R.fail("评价不存在或已删除");
+        }
+        if (review.getAuditStatus() == null || review.getAuditStatus() != 1) {
+            return R.fail("评价不可见");
+        }
+
         List<StoreStaffCommentVo> comments = storeStaffCommentMapper.getCommentListByReviewId(reviewId, currentUserId);
 
         // 为每个首评加载回复列表
@@ -176,6 +187,14 @@ public class StoreStaffCommentServiceImpl extends ServiceImpl<StoreStaffCommentM
             return R.fail("只能回复首评");
         }
 
+        StoreStaffReview parentReview = storeStaffReviewService.getById(headComment.getReviewId());
+        if (parentReview == null || parentReview.getDeleteFlag() == 1) {
+            return R.fail("评价不存在或已删除");
+        }
+        if (parentReview.getAuditStatus() == null || parentReview.getAuditStatus() != 1) {
+            return R.fail("评价未通过展示审核,暂不能回复");
+        }
+
         // 构建回复对象
         StoreStaffComment reply = buildReplyFromDto(replyDto, headComment);
 

+ 6 - 3
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java

@@ -1555,9 +1555,11 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             List<StoreStaffConfig> allStaffList = storeStaffConfigMapper.selectBatchIds(allStaffIds);
 
             // 4. 构建员工ID到员工对象的映射
-            // 过滤条件:未删除、status为1(审核通过)、online_status为0(上线)
+            // 过滤条件:未删除、属于当前店铺、status为1(审核通过)、online_status为0(上线)
+            // 必须与 queryStaffListByTitleId 一致校验 storeId,避免 title.staff_ids 误配时串店展示
             Map<Integer, StoreStaffConfig> staffMap = allStaffList.stream()
-                    .filter(staff -> staff != null 
+                    .filter(staff -> staff != null
+                            && storeId.equals(staff.getStoreId())
                             && CommonConstant.DELETE_FLAG_UNDELETE.equals(staff.getDeleteFlag())
                             && "1".equals(staff.getStatus())
                             && staff.getOnlineStatus() != null && staff.getOnlineStatus() == 0)
@@ -1643,9 +1645,10 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
         }
 
         try {
-            // 查询好评(overall_rating>4.5, staff_user_id in staffIds)
+            // 查询好评(overall_rating>4.5, staff_user_id in staffIds);与评价列表 SQL 一致仅统计审核通过
             LambdaQueryWrapper<StoreStaffReview> queryWrapper = new LambdaQueryWrapper<>();
             queryWrapper.eq(StoreStaffReview::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
+                    .eq(StoreStaffReview::getAuditStatus, 1)
                     .ge(StoreStaffReview::getOverallRating, new java.math.BigDecimal("4.5"))
                     .in(StoreStaffReview::getStaffUserId, staffIds);
 

+ 43 - 77
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffReviewServiceImpl.java

@@ -10,7 +10,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
@@ -22,14 +21,11 @@ import shop.alien.mapper.*;
 import shop.alien.store.service.StoreStaffCommentService;
 import shop.alien.store.service.StoreStaffReviewService;
 import shop.alien.store.util.ai.AiContentModerationUtil;
-import shop.alien.store.util.ai.AiVideoModerationUtil;
 import shop.alien.util.common.DistanceUtil;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
 
 /**
  * 员工评价 服务实现类
@@ -53,9 +49,6 @@ public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMap
     private final StoreInfoMapper storeInfoMapper;
     private final StoreUserMapper storeUserMapper;
     private final AiContentModerationUtil aiContentModerationUtil;
-    @Qualifier("commonVideoTaskExecutor")
-    private final ExecutorService commonVideoTaskExecutor;
-    private final AiVideoModerationUtil aiVideoModerationUtil;
 
     @Override
     public R<StoreStaffReview> createReview(StoreStaffReviewDto reviewDto) {
@@ -67,80 +60,36 @@ public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMap
             return R.fail(validateResult.getMsg());
         }
 
-        // 创建评价
+        // 先审文本再入库:AI 不通过不落库,不计入评价/后续评论数等
+        if (StringUtils.isNotEmpty(reviewDto.getReviewContent())) {
+            AiContentModerationUtil.AuditResult auditResult =
+                    aiContentModerationUtil.auditContent(reviewDto.getReviewContent(), null);
+            if (!auditResult.isPassed()) {
+                String displayReason = buildAiTextAuditFailMessage(auditResult.getFailureReason());
+                log.warn("员工评价AI文本审核未通过, 未写入评价表, rawReason={}, displayReason={}",
+                        auditResult.getFailureReason(), displayReason);
+                return R.fail(displayReason);
+            }
+        }
+
         StoreStaffReview review = buildReviewFromDto(reviewDto);
         boolean success = this.save(review);
-        // AI:评价图/视频前端直连审;服务端仅审 reviewContent 文本(图列表在 util 内忽略)
-        Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(reviewDto.getReviewImages());
-
-        AiContentModerationUtil.AuditResult auditResult = new AiContentModerationUtil.AuditResult(true, "");
-        if( StringUtils.isNotEmpty(reviewDto.getReviewContent()) || urlCategoryMap.get("image").size() > 0){
-            auditResult = aiContentModerationUtil.auditContent(reviewDto.getReviewContent(), null);
-        }
-        if (!auditResult.isPassed()) {
-            // 审核不通过
-            StoreStaffReview staffReview = this.getById(review.getId());
-            staffReview.setAuditStatus(2);
-            staffReview.setAuditReason(auditResult.getFailureReason());
-            this.saveOrUpdate(staffReview);
-        } else{
-            // 视频审核由前端完成;此处 auditVideos 恒通过,仅保留异步更新员工分等原流程
-            CompletableFuture.runAsync(() -> {
-                AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
-                try {
-                    videoAuditResult = CompletableFuture.supplyAsync(
-                            () -> aiVideoModerationUtil.auditVideos(urlCategoryMap.get("video")),
-                            commonVideoTaskExecutor
-                    ).get();
-
-                    // 审核不通过则更新状态和原因
-                    if (Objects.nonNull(videoAuditResult) && !videoAuditResult.isPassed()) {
-                        // 重新查询最新的rating,避免并发覆盖
-                        StoreStaffReview staffReview  = this.getById(review.getId());
-                        if (Objects.isNull(review)) {
-                            log.error("视频审核后更新失败,rating,ID:{}", review.getId());
-                            return;
-                        }
-                        staffReview.setAuditStatus(2);
-                        // 失败原因
-                        staffReview.setAuditReason(videoAuditResult.getFailureReason());
-                        this.saveOrUpdate(staffReview);
-                        log.info("视频审核不通过,已更新状态,reviewID:{},原因:{}",
-                                staffReview.getId(), videoAuditResult.getFailureReason());
-                    } else if (Objects.nonNull(videoAuditResult) && videoAuditResult.isPassed()) {
-                        // 审核通过也更新状态(可选,根据业务需求)
-                        StoreStaffReview staffReview = this.getById(review.getId());
-                        if (Objects.nonNull(staffReview)) {
-                            staffReview.setAuditStatus(1);
-                        // latestRequirement.setAuditReason(null);
-                            this.saveOrUpdate(staffReview);
-                            // 更新员工评分
-                            updateStaffServiceScore(review.getStaffUserId());
-                            log.info("视频审核通过,已更新状态,reviewID:{}", staffReview.getId());
-                        }
-                    }
-                } catch (Exception e) {
-                    log.error("视频审核接口调用异常,reviewID:{}", review.getId(), e);
-                    StoreStaffReview staffReview = this.getById(review.getId());
-                    if (Objects.nonNull(staffReview)) {
-                        staffReview.setAuditStatus(2);
-                        staffReview.setAuditReason("视频审核接口调用异常:" + e.getMessage()); // 实际需捕获具体异常信息
-                        this.saveOrUpdate(staffReview);
-                    }
-                }
-            }, commonVideoTaskExecutor);
+        if (!success) {
+            log.error("创建评价失败,入库失败");
+            return R.fail("创建评价失败");
         }
 
-
-
-
-        if (success) {
-            log.info("创建评价成功, 评价ID={}", review.getId());
-            return R.data(review, "提交成功");
-        } else {
-            log.error("创建评价失败");
-            return R.fail("创建评价失败");
+        StoreStaffReview approved = this.getById(review.getId());
+        if (approved != null) {
+            approved.setAuditStatus(1);
+            approved.setAuditReason(null);
+            this.updateById(approved);
+            updateStaffServiceScore(review.getStaffUserId());
         }
+
+        log.info("创建评价成功, 评价ID={}", review.getId());
+        StoreStaffReview latest = this.getById(review.getId());
+        return R.data(latest != null ? latest : review, "提交成功");
     }
 
     @Override
@@ -274,11 +223,14 @@ public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMap
             return R.fail(validateResult.getMsg());
         }
 
-        // 验证评价是否存在
+        // 验证评价是否存在且已展示通过(与列表、详情 SQL 一致,防止未通过审核的评价仍被点赞刷数)
         StoreStaffReview review = this.getById(reviewId);
         if (review == null || review.getDeleteFlag() == 1) {
             return R.fail("评价不存在或已删除");
         }
+        if (review.getAuditStatus() == null || review.getAuditStatus() != 1) {
+            return R.fail("评价不存在");
+        }
 
         // 检查是否已点赞
         if (isLiked(reviewId, userId, LIKE_TYPE_REVIEW)) {
@@ -362,6 +314,20 @@ public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMap
     }
 
     /**
+     * 人员评价走服务端 moderate 文本审核,失败时对用户与库内 audit_reason 统一为「AI审核失败」为主文案。
+     */
+    private static String buildAiTextAuditFailMessage(String rawReason) {
+        if (!org.springframework.util.StringUtils.hasText(rawReason)) {
+            return "AI审核失败";
+        }
+        String t = rawReason.trim();
+        if ("审核异常".equals(t)) {
+            return "AI审核失败(服务调用异常)";
+        }
+        return "AI审核失败:" + t;
+    }
+
+    /**
      * 构建评价对象
      */
     private StoreStaffReview buildReviewFromDto(StoreStaffReviewDto reviewDto) {