浏览代码

Fix:店铺评价功能优化

panzhilin 3 月之前
父节点
当前提交
59255db621

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

@@ -27,7 +27,7 @@ public class TagsSynonym {
     @TableField("main_tag_id")
     private Integer mainTagId;
 
-    @ApiModelProperty(value = "评论ID")
+    @ApiModelProperty(value = "评论ID(关联common_comment.id)")
     @TableField("comment_id")
     private Integer commentId;
 

+ 10 - 2
alien-store/src/main/java/shop/alien/store/controller/CommonCommentController.java

@@ -1,4 +1,4 @@
-package shop.alien.store.controller;
+  package shop.alien.store.controller;
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -59,7 +59,12 @@ public class CommonCommentController {
      //   "merchantId": 10086, 为2商户评论需要填
      }
      */
-    @ApiOperation(value = "新增评论", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    /**
+     * 新增评论
+     * @param commonComment 评论对象
+     * @return 0:成功, 1:失败, 2:文本内容异常, 4:字数超限(超过300字)
+     */
+    @ApiOperation(value = "新增评论", notes = "0:成功, 1:失败, 2:文本内容异常, 4:字数超限(超过300字)")
     @PostMapping("/addComment")
     public R addComment(@RequestBody CommonComment commonComment) {
         Integer addComment = commonCommentService.addComment(commonComment);
@@ -69,6 +74,9 @@ public class CommonCommentController {
         if (addComment == 2) {
             return R.fail("新增评论失败,评论内容包含敏感字符");
         }
+        if (addComment == 4) {
+            return R.fail("新增评论失败,评论内容超过300字限制");
+        }
         return R.fail("新增评论失败");
     }
 

+ 11 - 5
alien-store/src/main/java/shop/alien/store/controller/CommonRatingController.java

@@ -35,7 +35,10 @@ public class CommonRatingController {
             @ApiImplicitParam(name = "businessId", value = "业务关联ID", dataType = "Long", paramType = "query"),
             @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Long", paramType = "query"),
             @ApiImplicitParam(name = "auditStatus", value = "审核状态:0-待审核 1-通过 2-驳回", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "searchScore", value = "搜索评分:0-全部,1-好评 2-中评 3-差评,4-有图", dataType = "Integer", paramType = "query")
+            @ApiImplicitParam(name = "searchScore", value = "搜索评分:0-全部,1-好评 2-中评 3-差评,4-有图", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "days", value = "查询时间, 多少天前", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "replyStatus", value = "回复状态(0:全部, 1:已回复, 2:未回复)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "tagId", value = "标签id", dataType = "Integer", paramType = "query")
     })
     @GetMapping("/getList")
     public R getList(
@@ -45,10 +48,13 @@ public class CommonRatingController {
             @RequestParam(required = false) Long businessId,
             @RequestParam(required = false) Long userId,
             @RequestParam(required = false) Integer auditStatus,
-            @RequestParam(defaultValue = "0") Integer searchScore) {
-        log.info("CommonRatingController.getList?pageNum={}&pageSize={}&businessType={}&businessId={}&userId={}&auditStatus={}&searchScore={}",
-                pageNum, pageSize, businessType, businessId, userId, auditStatus, searchScore);
-        return commonRatingService.getRatingList(pageNum, pageSize, businessType, businessId, userId, auditStatus, searchScore);
+            @RequestParam(defaultValue = "0") Integer searchScore,
+            @RequestParam(required = false) Integer days,
+            @RequestParam(required = false) Integer replyStatus,
+            @RequestParam(required = false) Integer tagId) {
+        log.info("CommonRatingController.getList?pageNum={}&pageSize={}&businessType={}&businessId={}&userId={}&auditStatus={}&searchScore={}&days={}&replyStatus={}&tagId={}",
+                pageNum, pageSize, businessType, businessId, userId, auditStatus, searchScore, days, replyStatus, tagId);
+        return commonRatingService.getRatingList(pageNum, pageSize, businessType, businessId, userId, auditStatus, searchScore, days, replyStatus, tagId);
     }
 
     @ApiOperation("获取全部评价数量(好评,中评,差评)")

+ 5 - 2
alien-store/src/main/java/shop/alien/store/service/CommonRatingService.java

@@ -20,7 +20,7 @@ public interface CommonRatingService extends IService<CommonRating> {
      */
     Integer saveCommonRating(CommonRating commonRating);
 
-/*        *
+    /**
      * 分页查询评价列表
      *
      * @param pageNum      页数
@@ -30,9 +30,12 @@ public interface CommonRatingService extends IService<CommonRating> {
      * @param userId       用户ID
      * @param auditStatus  审核状态:0-待审核 1-通过 2-驳回
      * @param searchScore  搜索评分:0-全部,1-好评 2-中评 3-差评,4-有图
+     * @param days         查询时间, 多少天前
+     * @param replyStatus  回复状态(0:全部, 1:已回复, 2:未回复)
+     * @param tagId        标签id
      * @return IPage<CommonRating>
      */
-    R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore);
+    R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore, Integer days, Integer replyStatus, Integer tagId);
 
      /**
      * 获取店铺评价数量(好评,中评,差评)

+ 15 - 1
alien-store/src/main/java/shop/alien/store/service/impl/CommonCommentServiceImpl.java

@@ -27,16 +27,30 @@ public class CommonCommentServiceImpl extends ServiceImpl<CommonCommentMapper, C
     @Autowired
     private TextModerationUtil textModerationUtil;
 
+    /**
+     * 新增评论
+     * 
+     * @param commonComment 评论对象
+     * @return 0:成功, 1:失败, 2:文本内容异常, 4:字数超限(超过300字)
+     */
     @Override
     public Integer addComment(CommonComment commonComment) {
+        // 校验评论内容字数限制(300字)
+        if (commonComment.getContent() != null && commonComment.getContent().length() > 300) {
+            log.warn("评论内容超过300字限制,content length={}", commonComment.getContent().length());
+            return 4; // 字数超限
+        }
+        
+        // 文本内容审核
         TextModerationResultVO textCheckResult = null;
         try {
             textCheckResult = textModerationUtil.invokeFunction(commonComment.getContent(), CommonRatingServiceImpl.SERVICES_LIST);
             if ("high".equals(textCheckResult.getRiskLevel())) {
-                return 2;
+                return 2; // 文本内容异常(包含敏感词)
             }
             return this.save(commonComment) ? 0 : 1;
         } catch (Exception e) {
+            log.error("新增评论失败", e);
             return 1;
         }
     }

+ 176 - 28
alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java

@@ -24,6 +24,7 @@ import shop.alien.entity.store.vo.CommonRatingVo;
 import shop.alien.entity.store.vo.StoreInfoScoreVo;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.mapper.*;
+import shop.alien.entity.store.TagsSynonym;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.CommonRatingService;
 import shop.alien.store.util.CommonConstant;
@@ -32,6 +33,7 @@ import shop.alien.util.common.constant.RatingBusinessTypeEnum;
 import shop.alien.util.common.safe.TextModerationResultVO;
 import shop.alien.util.common.safe.TextModerationUtil;
 import shop.alien.util.common.safe.TextReviewServiceEnum;
+import shop.alien.util.common.DateUtils;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -66,6 +68,7 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
     private final LifeCollectMapper lifeCollectMapper;
     private final LifeFansMapper lifeFansMapper;
+    private final TagsSynonymMapper tagsSynonymMapper;
 
 
     public static final List<String> SERVICES_LIST = ImmutableList.of(
@@ -153,20 +156,39 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
 
     }
 
+    /**
+     * 分页查询评价列表
+     *
+     * @param pageNum      页数
+     * @param pageSize     页容
+     * @param businessType 业务类型:1-店铺评价
+     * @param businessId   业务关联ID
+     * @param userId       用户ID
+     * @param auditStatus  审核状态:0-待审核 1-通过 2-驳回
+     * @param searchScore  搜索评分:0-全部,1-好评 2-中评 3-差评,4-有图
+     * @param days         查询时间, 多少天前
+     * @param replyStatus  回复状态(0:全部, 1:已回复, 2:未回复)
+     * @param tagId        标签id
+     * @return R
+     */
     @Override
-    public R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore) {
+    public R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore, Integer days, Integer replyStatus, Integer tagId) {
         Page<CommonRating> page = new Page<>(pageNum, pageSize);
         LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
         
+        // 业务类型筛选
         if (businessType != null) {
             wrapper.eq(CommonRating::getBusinessType, businessType);
         }
+        // 业务ID筛选
         if (businessId != null) {
             wrapper.eq(CommonRating::getBusinessId, businessId);
         }
+        // 审核状态筛选
         if (auditStatus != null) {
             wrapper.eq(CommonRating::getAuditStatus, auditStatus);
         }
+        // 评分等级筛选
         if (searchScore != null) {
             if(searchScore == 1){
                 // 1-好评
@@ -182,17 +204,89 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
             }else if(searchScore == 4){
                 // 4-有图
                 wrapper.isNotNull(CommonRating::getImageUrls);
-                // 2. 排除 空字符串 ""
                 wrapper.ne(CommonRating::getImageUrls, "");
-                // 3. 排除 纯空格字符串(如"   ")—— 用 TRIM 函数去掉首尾空格后判断非空
                 wrapper.apply("TRIM({0}) <> ''", "image_urls");
             }
         }
+        // 时间范围筛选
+        if (days != null && days > 0) {
+            Date date = DateUtils.calcDays(new Date(), -days);
+            wrapper.ge(CommonRating::getCreatedTime, date);
+        }
+        // 回复状态筛选(使用EXISTS子查询在数据库层面筛选)
+        if (replyStatus != null && (replyStatus == 1 || replyStatus == 2)) {
+            if (replyStatus == 1) {
+                // 已回复:存在商户评论
+                wrapper.apply("EXISTS (SELECT 1 FROM common_comment cc WHERE cc.source_type = 1 " +
+                             "AND cc.source_id = common_rating.id AND cc.comment_type = 2 " +
+                             "AND cc.is_show = 1 AND cc.audit_status = 1 AND cc.delete_flag = 0)");
+            } else if (replyStatus == 2) {
+                // 未回复:不存在商户评论
+                wrapper.apply("NOT EXISTS (SELECT 1 FROM common_comment cc WHERE cc.source_type = 1 " +
+                             "AND cc.source_id = common_rating.id AND cc.comment_type = 2 " +
+                             "AND cc.is_show = 1 AND cc.audit_status = 1 AND cc.delete_flag = 0)");
+            }
+        }
+        // 标签筛选
+        if (tagId != null) {
+            // 1. 查询标签关联的评论ID列表(tags_synonym.comment_id 关联 common_comment.id)
+            LambdaQueryWrapper<TagsSynonym> tagWrapper = new LambdaQueryWrapper<>();
+            tagWrapper.eq(TagsSynonym::getMainTagId, tagId)
+                     .eq(TagsSynonym::getDeleteFlag, 0)
+                     .isNotNull(TagsSynonym::getCommentId);  // 确保comment_id不为空
+            List<TagsSynonym> tagsSynonymList = tagsSynonymMapper.selectList(tagWrapper);
+            
+            if (CollectionUtils.isNotEmpty(tagsSynonymList)) {
+                // 2. 提取评论ID列表(common_comment.id)
+                List<Long> commentIdList = tagsSynonymList.stream()
+                        .filter(synonym -> synonym != null && synonym.getCommentId() != null)
+                        .map(synonym -> synonym.getCommentId().longValue())
+                        .distinct()
+                        .collect(Collectors.toList());
+                
+                if (CollectionUtils.isNotEmpty(commentIdList)) {
+                    // 3. 通过评论ID查询common_comment表,获取source_id(评价ID)
+                    LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<>();
+                    commentWrapper.in(CommonComment::getId, commentIdList)
+                                 .eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())  // source_type=1表示评价的评论
+                                 .eq(CommonComment::getIsShow, 1)
+                                 .eq(CommonComment::getAuditStatus, 1);
+                    List<CommonComment> comments = commonCommentMapper.selectList(commentWrapper);
+                    
+                    if (CollectionUtils.isNotEmpty(comments)) {
+                        // 4. 提取评价ID列表(common_comment.source_id = common_rating.id)
+                        List<Long> ratingIdList = comments.stream()
+                                .filter(comment -> comment.getSourceId() != null)
+                                .map(CommonComment::getSourceId)
+                                .distinct()
+                                .collect(Collectors.toList());
+                        
+                        if (CollectionUtils.isNotEmpty(ratingIdList)) {
+                            wrapper.in(CommonRating::getId, ratingIdList);
+                        } else {
+                            // 如果没有匹配的评价ID,返回空结果
+                            wrapper.eq(CommonRating::getId, -1);
+                        }
+                    } else {
+                        // 如果没有匹配的评论,返回空结果
+                        wrapper.eq(CommonRating::getId, -1);
+                    }
+                } else {
+                    // 如果没有评论ID,返回空结果
+                    wrapper.eq(CommonRating::getId, -1);
+                }
+            } else {
+                // 如果没有标签关联,返回空结果
+                wrapper.eq(CommonRating::getId, -1);
+            }
+        }
         
         wrapper.eq(CommonRating::getIsShow, 1);
         wrapper.orderByDesc(CommonRating::getId);
         IPage<CommonRating> page1 = this.page(page, wrapper);
-        return doListBusinessWithType(page1, businessType,userId);
+        
+        // 处理回复状态筛选
+        return doListBusinessWithType(page1, businessType, userId, replyStatus);
     }
 
     @Override
@@ -211,6 +305,33 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
         }
         // 获取评价统计信息(总评论数、有图评论数、好评数、中评数、差评数)
         ratingCount = commonRatingMapper.getRatingCount(new QueryWrapper<CommonRating>().in("id", collect));
+        
+        // 计算好评、中评、差评占比
+        // 注意:数据库返回的 count 可能是 Long 类型,需要安全转换
+        Long goodCountLong = (Long) ratingCount.getOrDefault("goodCount", 0L);
+        Long midCountLong = (Long) ratingCount.getOrDefault("midCount", 0L);
+        Long badCountLong = (Long) ratingCount.getOrDefault("badCount", 0L);
+        
+        int goodCount = goodCountLong != null ? goodCountLong.intValue() : 0;
+        int midCount = midCountLong != null ? midCountLong.intValue() : 0;
+        int badCount = badCountLong != null ? badCountLong.intValue() : 0;
+        int totalCount = goodCount + midCount + badCount;
+        
+        if (totalCount > 0) {
+            // 计算占比(保留2位小数)
+            Double goodPercent = Math.round((goodCount * 100.0 / totalCount) * 100.0) / 100.0;
+            Double midPercent = Math.round((midCount * 100.0 / totalCount) * 100.0) / 100.0;
+            Double badPercent = Math.round((badCount * 100.0 / totalCount) * 100.0) / 100.0;
+            
+            ratingCount.put("goodPercent", goodPercent);
+            ratingCount.put("midPercent", midPercent);
+            ratingCount.put("badPercent", badPercent);
+        } else {
+            ratingCount.put("goodPercent", 0.0);
+            ratingCount.put("midPercent", 0.0);
+            ratingCount.put("badPercent", 0.0);
+        }
+        
         if(RatingBusinessTypeEnum.STORE_RATING.getBusinessType() == businessType){
             // 1店铺评分
             StoreInfo storeInfo = storeInfoMapper.selectById(businessId);
@@ -403,19 +524,29 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
         }
     }
 
-    private R doListBusinessWithType(IPage<CommonRating> page1, Integer businessType, Long userId) {
+    /**
+     * 根据业务类型处理评价列表
+     *
+     * @param page1       评价分页数据
+     * @param businessType 业务类型
+     * @param userId      用户ID
+     * @param replyStatus 回复状态(0:全部, 1:已回复, 2:未回复)
+     * @return R<IPage<CommonRatingVo>>
+     */
+    private R<IPage<CommonRatingVo>> doListBusinessWithType(IPage<CommonRating> page1, Integer businessType, Long userId, Integer replyStatus) {
         if(businessType == RatingBusinessTypeEnum.STORE_RATING.getBusinessType()){
-            IPage<CommonRatingVo> result = new Page<>(page1.getPages(), page1.getSize(), page1.getTotal());
-            List resultList = new ArrayList();
+            IPage<CommonRatingVo> result = new Page<>(page1.getCurrent(), page1.getSize(), page1.getTotal());
+            List<CommonRatingVo> resultList = new ArrayList<>();
             if(page1.getRecords().isEmpty()){
                 result.setRecords(resultList);
                 return R.data(result);
             }
-            // 1查询评价用户信息
+            
+            // 1. 查询评价用户信息
             Set<Long> userIdSet = page1.getRecords().stream()
                     .map(CommonRating::getUserId)
                     .collect(Collectors.toSet());
-            List<LifeUser> lifeUsers = new  ArrayList<>();
+            List<LifeUser> lifeUsers = new ArrayList<>();
             Map<Integer, LifeUser> lifeUserMap = new HashMap<>();
             if (!userIdSet.isEmpty()){
                 lifeUsers = lifeUserMapper.selectList(
@@ -424,49 +555,66 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
                 lifeUserMap = lifeUsers.stream()
                         .collect(Collectors.toMap(LifeUser::getId, Function.identity()));
             }
-            // 2查询当前用户点赞列表(仅评价)
-            List<LifeLikeRecord> lifeLikeRecords = lifeLikeRecordMapper.selectList(
-                    new QueryWrapper<LifeLikeRecord>().lambda()
-                    .eq(LifeLikeRecord::getDianzanId, userId)
-                    .eq(LifeLikeRecord::getType, CommonConstant.RATING_LIKE));
-            Map<String, LifeLikeRecord> likeRecordMap = lifeLikeRecords.stream()
-                    .collect(Collectors.toMap(LifeLikeRecord::getHuifuId, Function.identity()));
-
+            
+            // 2. 查询当前用户点赞列表(仅评价)
+            List<LifeLikeRecord> lifeLikeRecords = new ArrayList<>();
+            Map<String, LifeLikeRecord> likeRecordMap = new HashMap<>();
+            if (userId != null) {
+                lifeLikeRecords = lifeLikeRecordMapper.selectList(
+                        new QueryWrapper<LifeLikeRecord>().lambda()
+                        .eq(LifeLikeRecord::getDianzanId, userId)
+                        .eq(LifeLikeRecord::getType, CommonConstant.RATING_LIKE));
+                likeRecordMap = lifeLikeRecords.stream()
+                        .collect(Collectors.toMap(LifeLikeRecord::getHuifuId, Function.identity()));
+            }
 
-            // 1.查询评价的评论的记录个数
+            // 3. 查询评价的评论记录个数和商户回复状态
             Set<Long> ratingIdSet = page1.getRecords().stream()
                     .map(CommonRating::getId)
                     .collect(Collectors.toSet());
-            LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<CommonComment>()
-                    .eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
-                    .in(CommonComment::getSourceId, ratingIdSet);
-            // 评价id对应的所有评论 定义Map存储「评价ID -> 该评价下的总评论数」
-            Map<Long, Long> ratingCommentCountMap = commonCommentMapper.selectList(commentWrapper).stream()
+            
+            // 查询所有评论(用于统计评论数)
+            LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<>();
+            commentWrapper.eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
+                         .in(CommonComment::getSourceId, ratingIdSet)
+                         .eq(CommonComment::getIsShow, 1)
+                         .eq(CommonComment::getAuditStatus, 1);
+            List<CommonComment> allComments = commonCommentMapper.selectList(commentWrapper);
+            
+            // 评价ID -> 该评价下的总评论数
+            Map<Long, Long> ratingCommentCountMap = allComments.stream()
                     .collect(Collectors.groupingBy(CommonComment::getSourceId, Collectors.counting()));
 
+            // 4. 组装评价列表数据
             for (CommonRating record : page1.getRecords()) {
                 CommonRatingVo commonRatingVo = new CommonRatingVo();
                 BeanUtil.copyProperties(record, commonRatingVo);
-                // 判断用户信息
+                
+                // 设置用户信息
                 if(lifeUserMap.containsKey(Integer.parseInt(record.getUserId().toString()))){
                     LifeUser lifeUser = lifeUserMap.get(Integer.parseInt(record.getUserId().toString()));
-                    // 设置评论用户信息
                     commonRatingVo.setUserImage(lifeUser.getUserImage());
                     commonRatingVo.setUserName(lifeUser.getUserName());
                 }
-                // 判断当前登录人是否点赞过
+                
+                // 设置点赞状态
                 commonRatingVo.setIsLike(0);
                 if(likeRecordMap.containsKey(record.getId().toString())){
                     commonRatingVo.setIsLike(1);
                 }
-                // 3.1 从映射中获取该评价的总评论数(默认0)
+                
+                // 设置评论数
                 commonRatingVo.setCommentCount(ratingCommentCountMap.getOrDefault(record.getId(), 0L));
+                
                 resultList.add(commonRatingVo);
             }
+            
             result.setRecords(resultList);
             return R.data(result);
         }
-        return null;
+        // 如果不是店铺评价类型,返回空结果
+        IPage<CommonRatingVo> emptyResult = new Page<>(page1.getCurrent(), page1.getSize(), 0);
+        return R.data(emptyResult);
     }
 /*
     @Override