Sfoglia il codice sorgente

Merge branch 'refs/heads/sit' into sit-new-demands

# Conflicts:
#	alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
lutong 3 mesi fa
parent
commit
829fbae8ba
61 ha cambiato i file con 2659 aggiunte e 381 eliminazioni
  1. 70 0
      alien-config/src/main/java/shop/alien/config/advice/DecryptRequestBodyAdvice.java
  2. 49 0
      alien-config/src/main/java/shop/alien/config/advice/EncryptResponseBodyAdvice.java
  3. 31 0
      alien-config/src/main/java/shop/alien/config/properties/EncryptProperties.java
  4. 97 0
      alien-entity/src/main/java/shop/alien/entity/store/CommonComment.java
  5. 110 0
      alien-entity/src/main/java/shop/alien/entity/store/CommonRating.java
  6. 12 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreComment.java
  7. 10 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
  8. 8 4
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java
  9. 21 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/CommonCommentVo.java
  10. 37 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/CommonRatingVo.java
  11. 6 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/PerformanceDetailVo.java
  12. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreInfoScoreVo.java
  13. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreInfoVo.java
  14. 12 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffDetailWithPerformanceVo.java
  15. 40 0
      alien-entity/src/main/java/shop/alien/mapper/CommonCommentMapper.java
  16. 61 0
      alien-entity/src/main/java/shop/alien/mapper/CommonRatingMapper.java
  17. 1 0
      alien-entity/src/main/java/shop/alien/mapper/LawyerConsultationOrderMapper.java
  18. 5 5
      alien-entity/src/main/java/shop/alien/mapper/StoreCommentMapper.java
  19. 1 1
      alien-entity/src/main/java/shop/alien/mapper/StoreOfficialAlbumMapper.java
  20. 35 0
      alien-entity/src/main/resources/mapper/CommonCommentMapper.xml
  21. 36 0
      alien-entity/src/main/resources/mapper/CommonRatingMapper.xml
  22. 1 1
      alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java
  23. 2 0
      alien-lawyer/src/main/java/shop/alien/lawyer/payment/impl/AlipayPaymentStrategyImpl.java
  24. 2 0
      alien-lawyer/src/main/java/shop/alien/lawyer/payment/impl/WeChatPaymentStrategyImpl.java
  25. 46 0
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java
  26. 2 2
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java
  27. 1 1
      alien-store/src/main/java/shop/alien/store/AlienStoreApplication.java
  28. 122 3
      alien-store/src/main/java/shop/alien/store/controller/AiSearchController.java
  29. 19 7
      alien-store/src/main/java/shop/alien/store/controller/BarPerformanceController.java
  30. 90 0
      alien-store/src/main/java/shop/alien/store/controller/CommonCommentController.java
  31. 140 0
      alien-store/src/main/java/shop/alien/store/controller/CommonRatingController.java
  32. 31 20
      alien-store/src/main/java/shop/alien/store/controller/StoreCommentController.java
  33. 20 3
      alien-store/src/main/java/shop/alien/store/controller/StoreImgController.java
  34. 3 3
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  35. 13 0
      alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java
  36. 2 2
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffTitleController.java
  37. 6 4
      alien-store/src/main/java/shop/alien/store/service/BarPerformanceService.java
  38. 16 0
      alien-store/src/main/java/shop/alien/store/service/CommonCommentService.java
  39. 73 0
      alien-store/src/main/java/shop/alien/store/service/CommonRatingService.java
  40. 40 1
      alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java
  41. 34 0
      alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java
  42. 15 19
      alien-store/src/main/java/shop/alien/store/service/StoreCommentService.java
  43. 159 41
      alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java
  44. 103 0
      alien-store/src/main/java/shop/alien/store/service/impl/CommonCommentServiceImpl.java
  45. 449 0
      alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java
  46. 3 0
      alien-store/src/main/java/shop/alien/store/service/impl/PerformanceListServiceImpl.java
  47. 138 154
      alien-store/src/main/java/shop/alien/store/service/impl/StoreCommentServiceImpl.java
  48. 9 1
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  49. 2 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreOfficialAlbumServiceImpl.java
  50. 171 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffAuditAsyncService.java
  51. 113 91
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java
  52. 4 1
      alien-store/src/main/java/shop/alien/store/util/CommonConstant.java
  53. 20 7
      alien-store/src/main/java/shop/alien/store/util/FileUploadUtil.java
  54. 1 2
      alien-store/src/main/java/shop/alien/store/util/ai/AiFeedbackAssignUtils.java
  55. 21 0
      alien-util/src/main/java/shop/alien/util/common/constant/CommentSourceTypeEnum.java
  56. 21 0
      alien-util/src/main/java/shop/alien/util/common/constant/RatingBusinessTypeEnum.java
  57. 12 0
      alien-util/src/main/java/shop/alien/util/encryption/Decrypt.java
  58. 12 0
      alien-util/src/main/java/shop/alien/util/encryption/Encrypt.java
  59. 11 0
      alien-util/src/main/java/shop/alien/util/encryption/EncryptTest.java
  60. 52 0
      alien-util/src/main/java/shop/alien/util/encryption/StandardAesUtil.java
  61. 32 8
      alien-util/src/main/java/shop/alien/util/file/FileUtil.java

+ 70 - 0
alien-config/src/main/java/shop/alien/config/advice/DecryptRequestBodyAdvice.java

@@ -0,0 +1,70 @@
+package shop.alien.config.advice;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
+import shop.alien.config.properties.EncryptProperties;
+import shop.alien.util.encryption.Decrypt;
+import shop.alien.util.encryption.StandardAesUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 请求体解密 Advice
+ * 拦截带有 @Decrypt 注解的控制器方法,自动解密 AES 加密的请求体
+ */
+@ControllerAdvice
+public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
+
+    @Autowired
+    private EncryptProperties encryptProperties;
+
+    @Override
+    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
+        // 只有开启了配置,且方法或类上有 @Decrypt 注解时才拦截
+        return encryptProperties.isEnabled() && 
+               (methodParameter.hasMethodAnnotation(Decrypt.class) || 
+                methodParameter.getContainingClass().isAnnotationPresent(Decrypt.class));
+    }
+
+    @Override
+    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
+        // 读取加密的请求体
+        byte[] bodyBytes = StreamUtils.copyToByteArray(inputMessage.getBody());
+        String encryptedData = new String(bodyBytes, StandardCharsets.UTF_8);
+        
+        // 如果是 JSON 字符串格式(带有双引号),先去掉前后的双引号
+        if (encryptedData.startsWith("\"") && encryptedData.endsWith("\"")) {
+            encryptedData = encryptedData.substring(1, encryptedData.length() - 1);
+        }
+        
+        // 执行 AES 解密
+        String decryptedData = StandardAesUtil.decrypt(encryptedData, encryptProperties.getKey(), encryptProperties.getIv());
+        
+        if (decryptedData == null) {
+            throw new RuntimeException("请求数据解密失败");
+        }
+
+        // 返回解密后的输入流供后续 Jackson/Fastjson 解析
+        return new HttpInputMessage() {
+            @Override
+            public InputStream getBody() throws IOException {
+                return new ByteArrayInputStream(decryptedData.getBytes(StandardCharsets.UTF_8));
+            }
+
+            @Override
+            public HttpHeaders getHeaders() {
+                return inputMessage.getHeaders();
+            }
+        };
+    }
+}

+ 49 - 0
alien-config/src/main/java/shop/alien/config/advice/EncryptResponseBodyAdvice.java

@@ -0,0 +1,49 @@
+package shop.alien.config.advice;
+
+import com.alibaba.fastjson.JSON;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+import shop.alien.config.properties.EncryptProperties;
+import shop.alien.util.encryption.Encrypt;
+import shop.alien.util.encryption.StandardAesUtil;
+
+/**
+ * 响应体加密 Advice
+ * 拦截带有 @Encrypt 注解的控制器方法,将返回结果 AES 加密后返回
+ */
+@ControllerAdvice
+public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
+
+    @Autowired
+    private EncryptProperties encryptProperties;
+
+    @Override
+    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+        // 只有开启了配置,且方法或类上有 @Encrypt 注解时才拦截
+        return encryptProperties.isEnabled() && 
+               (returnType.hasMethodAnnotation(Encrypt.class) || 
+                returnType.getContainingClass().isAnnotationPresent(Encrypt.class));
+    }
+
+    @Override
+    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
+        if (body == null) {
+            return null;
+        }
+        
+        // 1. 将返回对象序列化为 JSON 字符串
+        String data = JSON.toJSONString(body);
+        
+        // 2. 执行 AES 加密
+        String encryptedData = StandardAesUtil.encrypt(data, encryptProperties.getKey(), encryptProperties.getIv());
+        
+        // 3. 返回加密后的字符串(直接写回响应流)
+        return encryptedData;
+    }
+}

+ 31 - 0
alien-config/src/main/java/shop/alien/config/properties/EncryptProperties.java

@@ -0,0 +1,31 @@
+package shop.alien.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+/**
+ * 加解密配置属性
+ */
+@Data
+@Component
+@RefreshScope
+@ConfigurationProperties(prefix = "alien.encrypt")
+public class EncryptProperties {
+
+    /**
+     * 是否开启加解密
+     */
+    private boolean enabled = true;
+
+    /**
+     * AES 密钥 (16位)
+     */
+    private String key;
+
+    /**
+     * AES IV (16位)
+     */
+    private String iv;
+}

+ 97 - 0
alien-entity/src/main/java/shop/alien/entity/store/CommonComment.java

@@ -0,0 +1,97 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 评论表(商户仅可回复)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("common_comment")
+@ApiModel(value = "CommonComment对象", description = "评论表(商户仅可回复)")
+public class CommonComment implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "评论来源类型:1-评价的评论 2-社区动态 3-活动留言")
+    @TableField("source_type")
+    private Integer sourceType;
+
+    @ApiModelProperty(value = "来源关联ID:source_type=1时=rating.id,=2时=动态ID")
+    @TableField("source_id")
+    private Long sourceId;
+
+    @ApiModelProperty(value = "评论发布者ID:用户=用户ID,商户=商户运营账号ID")
+    @TableField("user_id")
+    private Long userId;
+
+    @ApiModelProperty(value = "父评论ID:0=根评论,>0=回复某条评论")
+    @TableField("parent_id")
+    private Long parentId;
+
+    @ApiModelProperty(value = "评论纯文本内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty(value = "评论图片URL(可选)")
+    @TableField("image_urls")
+    private String imageUrls;
+
+    @ApiModelProperty(value = "是否匿名:0-否 1-是(仅用户评论生效)")
+    @TableField("is_anonymous")
+    private Integer isAnonymous;
+
+    @ApiModelProperty(value = "评论主体:1-用户评论 2-商户评论(仅回复)")
+    @TableField("comment_type")
+    private Integer commentType;
+
+    @ApiModelProperty(value = "商户ID:comment_type=2时必填")
+    @TableField("merchant_id")
+    private Long merchantId;
+
+    @ApiModelProperty(value = "是否展示:0-隐藏 1-展示")
+    @TableField("is_show")
+    private Integer isShow;
+
+    @ApiModelProperty(value = "审核状态:0-待审核 1-通过 2-驳回")
+    @TableField("audit_status")
+    private Integer auditStatus;
+
+    @ApiModelProperty(value = "点赞数")
+    @TableField("like_count")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "回复数")
+    @TableField("reply_count")
+    private Integer replyCount;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "扩展字段:如评论楼层")
+    @TableField(value = "ext_info", typeHandler = com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
+    private String extInfo;
+}
+

+ 110 - 0
alien-entity/src/main/java/shop/alien/entity/store/CommonRating.java

@@ -0,0 +1,110 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 评价表(仅用户可发布)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("common_rating")
+@ApiModel(value = "CommonRating对象", description = "评价表(仅用户可发布)")
+public class CommonRating implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "业务类型:1-商铺评价")
+    @TableField("business_type")
+    private Integer businessType;
+
+    @ApiModelProperty(value = "业务关联ID:1-商铺ID")
+    @TableField("business_id")
+    private Integer businessId;
+
+    @ApiModelProperty(value = "评价用户ID(仅普通用户)")
+    @TableField("user_id")
+    private Long userId;
+
+    @ApiModelProperty(value = "评价文字内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty(value = "核心评分(0.0-5.0)")
+    @TableField("score")
+    private Double score;
+
+    @ApiModelProperty(value = "多维度评分:如{\"口味\":5.0,\"服务\":4.0}")
+    @TableField(value = "other_score")
+    private String otherScore;
+
+    @ApiModelProperty(value = "评价图片URL,多个逗号分隔")
+    @TableField("image_urls")
+    private String imageUrls;
+
+    @ApiModelProperty(value = "是否匿名:0-否 1-是")
+    @TableField("is_anonymous")
+    private Integer isAnonymous;
+
+    @ApiModelProperty(value = "是否展示:0-隐藏 1-展示")
+    @TableField("is_show")
+    private Integer isShow;
+
+    @ApiModelProperty(value = "审核状态:0-待审核 1-通过 2-驳回")
+    @TableField("audit_status")
+    private Integer auditStatus;
+
+    @ApiModelProperty(value = "点赞数")
+    @TableField("like_count")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "评分1")
+    @TableField("score_one")
+    private Double scoreOne;
+
+    @ApiModelProperty(value = "评分2")
+    @TableField("score_two")
+    private Double scoreTwo;
+
+    @ApiModelProperty(value = "评分3")
+    @TableField("score_three")
+    private Double scoreThree;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

+ 12 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreComment.java

@@ -131,4 +131,16 @@ public class StoreComment extends Model<StoreComment> {
     @TableField("lawyer_id")
     private Integer lawyerId;
 
+    @ApiModelProperty(value = "口味评分")
+    @TableField("score_one")
+    private Double scoreOne;
+
+    @ApiModelProperty(value = "环境评分")
+    @TableField("score_two")
+    private Double scoreTwo;
+
+    @ApiModelProperty(value = "服务评分")
+    @TableField("score_three")
+    private Double scoreThree;
+
 }

+ 10 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java

@@ -341,6 +341,16 @@ public class StoreInfo {
     @TableField("business_category_name")
     private String businessCategoryName;
 
+    @ApiModelProperty(value = "评价1")
+    @TableField("score_one")
+    private Double scoreOne;
+     @ApiModelProperty(value = "评价2")
+    @TableField("score_two")
+    private Double scoreTwo;
+     @ApiModelProperty(value = "评价3")
+    @TableField("score_three")
+    private Double scoreThree;
+
     @ApiModelProperty(value = "门店类型(0-其他,1-装修公司)")
     @TableField("store_tickets")
     private Integer storeTickets;

+ 8 - 4
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java

@@ -37,14 +37,18 @@ public class StoreStaffConfig extends Model<StoreStaffConfig> {
     @TableField("name")
     private String name;
 
-    @ApiModelProperty(value = "背景图片/视频(存储URL逗号分隔)")
-    @TableField("background_url")
-    private String backgroundUrl;
-
     @ApiModelProperty(value = "头像")
     @TableField("staff_image")
     private String staffImage;
 
+    @ApiModelProperty(value = "视频封面图片(存储URL逗号分隔)")
+    @TableField("video_head_url")
+    private String videoHeadUrl;
+
+    @ApiModelProperty(value = "背景图片/视频(存储URL逗号分隔)")
+    @TableField("background_url")
+    private String backgroundUrl;
+
     // @ApiModelProperty(value = "擅长标签id(字典表proficient_tag的dict_id逗号分隔)----废弃!")
     // @TableField("proficient_id")
     // private String proficientId;

+ 21 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/CommonCommentVo.java

@@ -0,0 +1,21 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.CommonComment;
+
+import java.util.List;
+
+@Data
+@ApiModel(value = "CommonCommentVo对象")
+public class CommonCommentVo extends CommonComment {
+    @ApiModelProperty(value = "子评论列表")
+    private List<CommonCommentVo> childCommonComments;
+    @ApiModelProperty(value = "评论用户头像")
+    private String headImg;
+    @ApiModelProperty(value = "评论用户昵称")
+    private String headName;
+    @ApiModelProperty(value = "是否点赞")
+    private String isLike;
+}

+ 37 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/CommonRatingVo.java

@@ -0,0 +1,37 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.CommonRating;
+
+import java.util.List;
+
+@Data
+@ApiModel(value = "CommonRatingVo对象")
+public class CommonRatingVo extends CommonRating {
+    @ApiModelProperty(name = "userImage", value = "用户头像")
+    private String userImage;
+    @ApiModelProperty(name = "userName", value = "用户名")
+    private String userName;
+    @ApiModelProperty(name = "isLike", value = "是否点赞")
+    private Integer isLike;
+    @ApiModelProperty(name = "storeName", value = "门店名称")
+    private String storeName;
+    @ApiModelProperty(name = "storeEvaluate", value = "门店评价")
+    private String storeEvaluate;
+    @ApiModelProperty(name = "storeScore", value = "门店评分")
+    private Double storeScore;
+    @ApiModelProperty(name = "scoreOne", value = "评分1")
+    private Double scoreOne;
+    @ApiModelProperty(name = "scoreTwo", value = "评分2")
+    private Double scoreTwo;
+    @ApiModelProperty(name = "scoreThree", value = "评分3")
+    private Double scoreThree;
+    @ApiModelProperty(name = "childCommonRatings", value = "评论")
+    private List<CommonCommentVo> childCommonComments;
+    @ApiModelProperty(name = "isCollect", value = "是否收藏")
+    private Integer isCollect;
+    @ApiModelProperty(name = "commentCount", value = "评论数量")
+    private Long commentCount;
+}

+ 6 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/PerformanceDetailVo.java

@@ -75,5 +75,11 @@ public class PerformanceDetailVo implements Serializable {
      */
     @ApiModelProperty(value = "演出类型(0-特邀演出,1-常规演出)")
     private Integer performanceType;
+
+    /**
+     * 演出图片
+     */
+    @ApiModelProperty(value = "演出图片")
+    private String performanceDetail;
 }
 

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

@@ -9,4 +9,7 @@ public class StoreInfoScoreVo {
     private Double tasteScore;
     private Double enScore;
     private Double serviceScore;
+    private Double scoreOne;
+    private Double scoreTwo;
+    private Double scoreThree;
 }

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

@@ -269,4 +269,7 @@ public class StoreInfoVo extends StoreInfo {
     @ApiModelProperty(value = "续签合同拒绝原因")
     private String contractReason;
 
+    @ApiModelProperty(value = "经营板块名称")
+    private String businessSectionName;
+
 }

+ 12 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffDetailWithPerformanceVo.java

@@ -47,5 +47,17 @@ public class StoreStaffDetailWithPerformanceVo implements Serializable {
      */
     @ApiModelProperty(value = "是否喜欢(true-已喜欢,false-未喜欢)")
     private Boolean isLiked;
+
+    /**
+     * 店铺名称
+     */
+    @ApiModelProperty(value = "店铺名称")
+    private String storeName;
+
+    /**
+     * 店铺电话
+     */
+    @ApiModelProperty(value = "店铺电话")
+    private String storePhone;
 }
 

+ 40 - 0
alien-entity/src/main/java/shop/alien/mapper/CommonCommentMapper.java

@@ -0,0 +1,40 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.store.CommonComment;
+import shop.alien.entity.store.vo.CommonCommentVo;
+
+import java.util.List;
+
+/**
+ * 评论表 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface CommonCommentMapper extends BaseMapper<CommonComment> {
+
+
+    @Select("select *,if(comment_type = 1,lu.user_image,su.head_img) headImg,\n" +
+            "if(comment_type = 1,lu.user_name ,su.nick_name ) headName," +
+            "if(llr.dianzan_id is null,'0','1') isLike\n" +
+            "from common_comment cc\n" +
+            "left join life_user lu on cc.user_id = lu.id and lu.delete_flag = 0\n" +
+            "left join store_user su on cc.merchant_id = su.store_id and su.delete_flag = 0\n" +
+            "left join life_like_record llr on\n" +
+            "llr.huifu_id = cc.id\n" +
+            "and llr.`type` = #{type}\n" +
+            "and llr.dianzan_id = #{dianzanId}\n" +
+            "and llr.delete_flag = 0\n" +
+            "${ew.customSqlSegment}")
+    List<CommonCommentVo> selectALlComment(@Param(Constants.WRAPPER)QueryWrapper<CommonCommentVo> wrapper,
+                                           @Param("type") String type,
+                                           @Param("dianzanId") Long dianzanId);
+}
+

+ 61 - 0
alien-entity/src/main/java/shop/alien/mapper/CommonRatingMapper.java

@@ -0,0 +1,61 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.store.CommonRating;
+import shop.alien.entity.store.vo.StoreInfoScoreVo;
+
+import java.util.Map;
+
+/**
+ * 评价表 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface CommonRatingMapper extends BaseMapper<CommonRating> {
+
+    /**
+     * 获取评价数量和评价分数信息
+     *
+     * @param businessId 门店ID
+     * @param businessType 业务类型
+     * @return 评价数量和评价分数信息
+     */
+    @Select("SELECT " +
+            "IFNULL(SUM(score), 0) AS score, " +
+            "IFNULL(SUM(score_one), 0) AS scoreOne, " +
+            "IFNULL(SUM(score_two), 0) AS scoreTwo, " +
+            "IFNULL(SUM(score_three), 0) AS scoreThree, " +
+            "COUNT(0) AS total " +
+            "FROM `common_rating` " +  // FROM 后加空格,避免和表名拼接成 FROM`common_rating`
+            "WHERE business_type = #{businessType} " +
+            "AND delete_flag = 0 " +
+            "AND business_id = #{businessId}")
+    StoreInfoScoreVo getCommentCountAndScoreInfo(@Param("businessType")Integer businessType,@Param("businessId")Integer businessId);
+
+     /**
+     * 获取评价数量
+     */
+    @Select("SELECT\n" +
+//            "    -- 1. 总评论数(当前门店、根评论、未删除)\n" +
+//            "    COUNT(1) AS totalCount,\n" +
+            "    -- 2. 有图评论数(image_urls 不为 null 且不为空字符串)\n" +
+            "    SUM(CASE WHEN image_urls IS NOT NULL AND image_urls != '' THEN 1 ELSE 0 END) AS imageCount,\n" +
+            "    -- 3. 好评数(score >= 4.5)\n" +
+            "    SUM(CASE WHEN score >= 4.5 THEN 1 ELSE 0 END) AS goodCount,\n" +
+            "    -- 4. 中评数(score >= 3.0 AND score <= 4.0)\n" +
+            "    SUM(CASE WHEN score >= 3.0 AND score <= 4.0 THEN 1 ELSE 0 END) AS midCount,\n" +
+            "    -- 5. 差评数(score >= 0.5 AND score <= 2.5)\n" +
+            "    SUM(CASE WHEN score >= 0.5 AND score <= 2.5 THEN 1 ELSE 0 END) AS badCount\n" +
+            "FROM\n" +
+            "    common_rating\n" +
+            "   ${ew.customSqlSegment}\n")
+    Map<String, Object> getRatingCount(@Param(Constants.WRAPPER) QueryWrapper<CommonRating> wrapper);
+}
+

+ 1 - 0
alien-entity/src/main/java/shop/alien/mapper/LawyerConsultationOrderMapper.java

@@ -244,6 +244,7 @@ public interface LawyerConsultationOrderMapper extends BaseMapper<LawyerConsulta
             "<if test='placeId != null'>`place_id` = #{placeId},</if>" +
             "<if test='lawyerEarnings != null'>`lawyer_earnings` = #{lawyerEarnings},</if>" +
             "<if test='acceptOrdersTime != null'>`accept_orders_time` = #{acceptOrdersTime},</if>" +
+            "<if test='payType != null'>`pay_type` = #{payType},</if>" +
             "<if test='reasonOrderRefusal != null'>`reason_order_refusal` = #{reasonOrderRefusal},</if>" +
             "</set>" +
             "WHERE `order_number` = #{orderNumber}" +

+ 5 - 5
alien-entity/src/main/java/shop/alien/mapper/StoreCommentMapper.java

@@ -116,14 +116,14 @@ public interface StoreCommentMapper extends BaseMapper<StoreComment> {
 
     @Select("SELECT " +
             "ifnull(SUM( score ),0) score," +
-            "ifnull(SUM( taste_score ),0) tasteScore," +
-            "ifnull(SUM( en_score ),0) enScore," +
-            "ifnull(SUM( service_score ),0) serviceScore," +
+            "ifnull(SUM( score_one ),0) scoreOne," +
+            "ifnull(SUM( score_two ),0) scoreTwo," +
+            "ifnull(SUM( score_three ),0) scoreThree," +
             "COUNT(0) total " +
             "FROM" +
             "`store_comment` " +
             "WHERE " +
-            "business_type = 5 " +
+            "business_type = 6 " +
             "AND delete_flag = 0 " +
             "AND reply_id IS NULL " +
             "AND store_id = #{storeId}")
@@ -194,7 +194,7 @@ WHERE
             "\tstore_comment s\n" +
             "\tLEFT JOIN life_user lu ON s.user_id = lu.id \n" +
             "WHERE\n" +
-            "\ts.business_type = 5 \n" +
+            "\ts.business_type = 6 \n" +
             "\tAND s.store_id = #{storeId}\n" +
             "\tLIMIT 1")
     StoreCommentVo getCommentOneInfo(@Param("storeId") int storeId);

+ 1 - 1
alien-entity/src/main/java/shop/alien/mapper/StoreOfficialAlbumMapper.java

@@ -23,7 +23,7 @@ public interface StoreOfficialAlbumMapper extends BaseMapper<StoreOfficialAlbum>
      */
     //"图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉, 10:商家头像, 11:店铺轮播图"
     @Select("SELECT a.*, b.img_url FROM store_official_album a LEFT JOIN store_img b ON b.id = " +
-            "(SELECT b2.id FROM store_img b2 WHERE b2.business_id = a.id and b2.img_type = '2' " +
+            "(SELECT b2.id FROM store_img b2 WHERE b2.business_id = a.id and b2.img_type in ( '2','4') " +
             "order by b2.img_sort LIMIT 1) where a.delete_flag = '0' and  a.store_id = #{storeId}")
     List<StoreOfficialAlbumVo> getStoreOfficialAlbumList(@Param("storeId") Integer storeId);
 

+ 35 - 0
alien-entity/src/main/resources/mapper/CommonCommentMapper.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.CommonCommentMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.CommonComment">
+        <id column="id" property="id" />
+        <result column="source_type" property="sourceType" />
+        <result column="source_id" property="sourceId" />
+        <result column="user_id" property="userId" />
+        <result column="parent_id" property="parentId" />
+        <result column="content" property="content" />
+        <result column="image_urls" property="imageUrls" />
+        <result column="is_anonymous" property="isAnonymous" />
+        <result column="comment_type" property="commentType" />
+        <result column="merchant_id" property="merchantId" />
+        <result column="is_show" property="isShow" />
+        <result column="audit_status" property="auditStatus" />
+        <result column="like_count" property="likeCount" />
+        <result column="reply_count" property="replyCount" />
+        <result column="create_time" property="createdTime" />
+        <result column="update_time" property="updatedTime" />
+        <result column="ext_info" property="extInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, source_type, source_id, user_id, parent_id, content, image_urls, is_anonymous,
+        comment_type, merchant_id, is_show, audit_status, like_count, reply_count,
+        create_time, update_time, ext_info
+    </sql>
+
+
+</mapper>
+

+ 36 - 0
alien-entity/src/main/resources/mapper/CommonRatingMapper.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.CommonRatingMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.CommonRating">
+        <id column="id" property="id" />
+        <result column="business_type" property="businessType" />
+        <result column="business_id" property="businessId" />
+        <result column="user_id" property="userId" />
+        <result column="content" property="content" />
+        <result column="score" property="score" />
+        <result column="other_score" property="otherScore" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
+        <result column="image_urls" property="imageUrls" />
+        <result column="is_anonymous" property="isAnonymous" />
+        <result column="is_show" property="isShow" />
+        <result column="audit_status" property="auditStatus" />
+        <result column="like_count" property="likeCount" />
+        <result column="score_one" property="scoreOne" />
+        <result column="score_two" property="scoreTwo" />
+        <result column="score_three" property="scoreThree" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_time" property="updatedTime" />
+        <result column="updated_user_id" property="updatedUserId" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, business_type, business_id, user_id, content, score, other_score, image_urls,
+        is_anonymous, is_show, audit_status, like_count, score_one, score_two, score_three,
+        delete_flag, created_user_id, updated_time, updated_user_id
+    </sql>
+
+</mapper>
+

+ 1 - 1
alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java

@@ -48,7 +48,7 @@ public class LifeUserController {
         }
 
         if (1 == userVo.getIsBanned()) {
-            return R.fail("账号已被封禁, 无法登录");
+            return R.fail("您的账户因严重违规导致被封禁");
         }
 
         // 2025-11-04 验证码-用户端登录

+ 2 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/payment/impl/AlipayPaymentStrategyImpl.java

@@ -204,6 +204,8 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
         try {
 
             //根据id把transactionId和orderStr更新到lawyer_consultation_order表中
+            log.info("AlipayPaymentStrategyImpl.searchOrderByOutTradeNoPath?transactionId={},id={},orderStr={}",
+                    transactionId, id, orderStr);
 
             LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
             updateWrapper.eq(LawyerConsultationOrder::getId, id);

+ 2 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/payment/impl/WeChatPaymentStrategyImpl.java

@@ -249,6 +249,8 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
     @Override
     public R searchOrderByOutTradeNoPath(String transactionId ,Integer id,String orderStr) throws Exception {
         log.info("查询微信支付订单状态,交易订单号:{}", transactionId);
+        log.info("WeChatPaymentStrategyImpl.searchOrderByOutTradeNoPath?transactionId={},id={},orderStr={}",
+                transactionId, id, orderStr);
 
         LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
         updateWrapper.eq(LawyerConsultationOrder::getId, id);

+ 46 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java

@@ -813,6 +813,9 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
                 // 发送拒绝接单通知
                 sendRejectOrderNotice(order);
                 
+                // 发送退款到账通知
+                sendRefundArrivalNotice(order);
+                
                 return R.data(true, "取消订单成功");
             } else {
                 log.error("律师取消订单失败:数据库操作失败,订单ID={}, 订单编号={}", orderId, order.getOrderNumber());
@@ -1592,6 +1595,49 @@ public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<Lawyer
     }
 
     /**
+     * 发送退款到账通知给客户端用户
+     *
+     * @param order 订单对象
+     */
+    private void sendRefundArrivalNotice(LawyerConsultationOrder order) {
+        try {
+            Integer clientUserId = order.getClientUserId();
+            if (clientUserId == null) {
+                log.warn("发送退款到账通知失败:客户端用户ID为空,订单ID={}", order.getId());
+                return;
+            }
+
+            // 获取客户端用户接收ID
+            String receiverId = getClientReceiverId(clientUserId);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("发送退款到账通知失败:获取客户端用户接收ID失败,订单ID={}, 用户ID={}", 
+                        order.getId(), clientUserId);
+                return;
+            }
+
+            // 构建通知消息
+            String orderNumber = order.getOrderNumber() != null ? order.getOrderNumber() : "";
+            String message = String.format("您的编号为%s的订单,订单金额已原路返还至您的支付渠道,请查收。", 
+                    orderNumber);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createOrderNotice(order.getId(), receiverId, "退款到账通知", message);
+            int noticeResult = lifeNoticeMapper.insert(lifeNotice);
+            if (noticeResult <= 0) {
+                log.warn("发送退款到账通知失败:保存通知失败,订单ID={}", order.getId());
+                return;
+            }
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("发送退款到账通知成功,接收人ID={}, 订单编号={}", receiverId, orderNumber);
+        } catch (Exception e) {
+            log.error("发送退款到账通知异常,订单ID={}, 异常信息={}", order.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
      * 创建订单通知对象
      *
      * @param businessId 业务ID(订单ID)

+ 2 - 2
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -649,8 +649,8 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
      */
     @Override
     public R<LawyerConsultationOrderDto> payStatus(PayStatusRequest request) {
-        log.info("LawyerConsultationOrderServiceImpl.payStatus?orderNumber={},paymentStatus={},orderStatus={}",
-                request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus());
+        log.info("LawyerConsultationOrderServiceImpl.payStatus?orderNumber={},paymentStatus={},orderStatus={},payType={}",
+                request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus(), request.getPayType());
 
         LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
         if (null != request.getOrderStr()){

+ 1 - 1
alien-store/src/main/java/shop/alien/store/AlienStoreApplication.java

@@ -8,7 +8,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
-@ComponentScan({"shop.alien.store.*","shop.alien.util.*","shop.alien.config.http","shop.alien.config.properties"})
+@ComponentScan({"shop.alien.store.*","shop.alien.util.*","shop.alien.config.http","shop.alien.config.properties","shop.alien.config.advice"})
 @EnableSwaggerBootstrapUI
 @MapperScan({"shop.alien.mapper"})
 @SpringBootApplication

+ 122 - 3
alien-store/src/main/java/shop/alien/store/controller/AiSearchController.java

@@ -1,6 +1,9 @@
 package shop.alien.store.controller;
 
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import io.swagger.annotations.Api;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -16,9 +19,18 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.StoreInfoVo;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.store.service.StoreImgService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.Comparator;
 
 
 @Slf4j
@@ -30,10 +42,12 @@ import java.util.Map;
 @RefreshScope
 public class AiSearchController {
 
+    private final StoreUserMapper storeUserMapper;
     @Value("${third-party-ai-search.base-url:http://124.93.18.180:7870/api/v1/search}")
     private String aiSearchUrl;
 
     private final RestTemplate restTemplate;
+    private final StoreImgService storeImgService;
 
     @RequestMapping("/search")
     public R search(@RequestBody Map<String,String> map) {
@@ -46,6 +60,8 @@ public class AiSearchController {
         requestBody.put("user_lat", map.get("lat"));
         requestBody.put("user_lng", map.get("lon"));
         requestBody.put("category", map.get("category"));
+        requestBody.put("page", map.get("pageNum"));
+        requestBody.put("sort_by", map.get("sortBy"));
         HttpHeaders aiHeaders = new HttpHeaders();
         aiHeaders.setContentType(MediaType.APPLICATION_JSON);
 //        aiHeaders.set("Authorization", "Bearer " + accessToken);
@@ -57,9 +73,56 @@ public class AiSearchController {
             String body = stringResponseEntity.getBody();
             JSONObject jsonObject = JSONObject.parseObject(body);
             JSONObject jsonObject1 = new JSONObject();
-            jsonObject1.put("total",jsonObject.get("total"));
-            jsonObject1.put("records",jsonObject.get("results"));
-            jsonObject1.put("size",map.get("pageSize"));
+//            if ("生活服务".equals(requestBody.get("category")) || "休闲娱乐".equals(requestBody.get("category"))) {
+                // 生活服务类别:转换为StoreInfoVo,确保返回的字段名按照StoreInfoVo定义
+                List<StoreInfoVo> result = convertToStoreInfoList(jsonObject.getJSONArray("results"));
+                
+                // 从结果中提取所有的id,放到集合中
+                List<Integer> storeIdList = result.stream()
+                        .map(StoreInfoVo::getId)
+                        .filter(id -> id != null)
+                        .collect(Collectors.toList());
+                
+                // 使用storeImgService批量查询,imgType为10
+                List<StoreUser> storeImgList = new ArrayList<>();
+                if (!storeIdList.isEmpty()) {
+                    QueryWrapper<StoreUser> queryWrapper = new QueryWrapper<>();
+                    queryWrapper.in("store_id", storeIdList);
+                    storeImgList = storeUserMapper.selectList(queryWrapper);
+                }
+
+                // 将storeImgList按照storeId分组,并关联到对应的result中
+                Map<Integer, List<StoreUser>> storeImgMap = storeImgList.stream()
+                        .collect(Collectors.groupingBy(StoreUser::getStoreId));
+                
+                // 将图片列表设置到对应的StoreInfoVo中
+                for (StoreInfoVo storeInfo : result) {
+                    if (storeInfo.getId() != null) {
+                        List<StoreUser> imgs = storeImgMap.get(storeInfo.getId());
+                        if (imgs != null && !imgs.isEmpty()) {
+                            // 将图片列表转换为URL列表,按照imgSort排序
+                            List<String> imgUrlList = imgs.stream()
+                                    .sorted(Comparator.comparing(StoreUser::getStoreId))
+                                    .map(StoreUser::getHeadImg)
+                                    .filter(url -> url != null)
+                                    .collect(Collectors.toList());
+                            storeInfo.setStoreAlbumUrlList(imgUrlList);
+                            // 如果第一个图片存在,也可以设置到imgUrl字段作为头像
+                            if (!imgUrlList.isEmpty()) {
+                                storeInfo.setImgUrl(imgUrlList.get(0));
+                            }
+                        }
+                    }
+                }
+                
+                jsonObject1.put("records", result);
+//            } else {
+//                // 其他类别:直接返回原始结果
+//                jsonObject1.put("records", jsonObject.get("results"));
+//            }
+
+            jsonObject1.put("total", jsonObject.get("total"));
+            jsonObject1.put("size", map.get("pageSize"));
             log.info("调用AI搜索接口 接口返回------{}", body);
             return R.data(jsonObject1);
         } catch (Exception e) {
@@ -68,6 +131,62 @@ public class AiSearchController {
         return  R.fail("请求失败");
     }
 
+    private List<StoreInfoVo> convertToStoreInfoList(JSONArray results) {
+        List<StoreInfoVo> storeInfoList = new ArrayList<>();
+        if (results != null) {
+            for (int i = 0; i < results.size(); i++) {
+                JSONObject item = results.getJSONObject(i);
+                // 记录第一条数据的字段名和内容,用于调试
+                if (i == 0) {
+                    log.info("AI返回的JSON字段示例: {}", item.keySet());
+                }
+                
+                // 创建新的JSONObject,将下划线命名转换为驼峰命名
+                JSONObject camelCaseItem = new JSONObject();
+                for (String key : item.keySet()) {
+                    String camelKey = underscoreToCamelCase(key);
+                    camelCaseItem.put(camelKey, item.get(key));
+                }
+                
+                // 将"name"字段映射到"storeName"字段
+                if (camelCaseItem.containsKey("name") && !camelCaseItem.containsKey("storeName")) {
+                    camelCaseItem.put("storeName", camelCaseItem.get("name"));
+                }
+
+                // 使用JSON.parseObject方法进行转换
+                StoreInfoVo storeInfo = JSON.parseObject(camelCaseItem.toJSONString(), StoreInfoVo.class);
+                storeInfoList.add(storeInfo);
+            }
+        }
+        return storeInfoList;
+    }
+
+    /**
+     * 将下划线命名转换为驼峰命名
+     * 例如: store_tel -> storeTel, created_time -> createdTime
+     */
+    private String underscoreToCamelCase(String underscore) {
+        if (underscore == null || !underscore.contains("_")) {
+            return underscore;
+        }
+        StringBuilder camelCase = new StringBuilder();
+        boolean nextUpperCase = false;
+        for (char c : underscore.toCharArray()) {
+            if (c == '_') {
+                nextUpperCase = true;
+            } else {
+                if (nextUpperCase) {
+                    camelCase.append(Character.toUpperCase(c));
+                    nextUpperCase = false;
+                } else {
+                    camelCase.append(c);
+                }
+            }
+        }
+        return camelCase.toString();
+    }
+
+
 
 
 

+ 19 - 7
alien-store/src/main/java/shop/alien/store/controller/BarPerformanceController.java

@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.BarPerformance;
 import shop.alien.entity.store.dto.BarPerformanceOnlineStatusDto;
+import shop.alien.entity.store.vo.BarPerformanceDetailVo;
 import shop.alien.store.service.BarPerformanceService;
 
 /**
@@ -65,12 +66,23 @@ public class BarPerformanceController {
      */
     @ApiOperation("新增或更新酒吧演出")
     @PostMapping("/saveOrUpdate")
-    public R<Integer> addOrUpdateBarPerformance(@RequestBody BarPerformance barPerformance) {
+    public R<BarPerformance> addOrUpdateBarPerformance(@RequestBody BarPerformance barPerformance) {
         log.info("BarPerformanceController.addOrUpdateBarPerformance?barPerformance={}", barPerformance);
         try {
-            int result = barPerformanceService.addOrUpdateBarPerformance(barPerformance);
-            if (result > 0) {
-                return R.success("操作成功");
+            BarPerformance result = barPerformanceService.addOrUpdateBarPerformance(barPerformance);
+            if (result != null) {
+                // 根据审核状态返回不同的提示信息
+                if (result.getReviewStatus() != null && result.getReviewStatus() == 2) {
+                    // 审核拒绝:数据已保存,但审核未通过
+                    return R.success("数据保存成功,但内容审核未通过:" + 
+                            (result.getRejectReason() != null ? result.getRejectReason() : "内容包含违规信息"));
+                } else if (result.getReviewStatus() != null && result.getReviewStatus() == 1) {
+                    // 审核通过
+                    return R.success("操作成功");
+                } else {
+                    // 其他状态
+                    return R.success("操作成功");
+                }
             } else {
                 return R.fail("操作失败");
             }
@@ -84,15 +96,15 @@ public class BarPerformanceController {
      * 获取酒吧演出详情
      *
      * @param id 演出ID
-     * @return 演出详情
+     * @return 演出详情(包含表演嘉宾信息)
      */
     @ApiOperation("获取酒吧演出详情")
     @ApiImplicitParam(name = "id", value = "演出ID", dataType = "Integer", paramType = "query", required = true)
     @GetMapping("/detail")
-    public R<BarPerformance> getBarPerformanceDetail(@RequestParam Integer id) {
+    public R<BarPerformanceDetailVo> getBarPerformanceDetail(@RequestParam Integer id) {
         log.info("BarPerformanceController.getBarPerformanceDetail?id={}", id);
         try {
-            BarPerformance performance = barPerformanceService.getBarPerformanceDetail(id);
+            BarPerformanceDetailVo performance = barPerformanceService.getBarPerformanceDetail(id);
             if (performance != null) {
                 return R.data(performance);
             } else {

+ 90 - 0
alien-store/src/main/java/shop/alien/store/controller/CommonCommentController.java

@@ -0,0 +1,90 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.CommonComment;
+import shop.alien.store.service.CommonCommentService;
+
+/**
+ * 评论表 前端控制器
+ *
+ * @author lyx
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"评论管理"})
+@ApiSort(13)
+@CrossOrigin
+@RestController
+@RequestMapping("/commonComment")
+@RequiredArgsConstructor
+public class CommonCommentController {
+
+    private final CommonCommentService commonCommentService;
+
+    /**
+     商户回复评论 实例:
+     {
+       "id": 10002,
+       "sourceType": 1,
+       "sourceId": 50008,
+       "userId": 999000,
+       "parentId": 10001,
+       "content": "感谢您的认可,我们会继续努力为您提供更好的服务!",
+       "imageUrls": "",
+       "isAnonymous": 0,
+       "commentType": 2,
+       "merchantId": 10086,
+       "isShow": 1,
+       "auditStatus": 1,
+       "likeCount": 8,
+       "replyCount": 0,
+       "createdTime": "2025-01-08 15:00:30",
+       "updatedTime": "2025-01-08 15:00:30",
+       "extInfo": "{\"floor\": 2, \"replyToUserId\": 888999}"
+     }
+     正常回复实例:
+     {
+     "sourceType": 1,
+     "sourceId": 50008, //回复评价需要填评价id,回复评论不需要
+     "userId": 888999, // 发布者id
+     "parentId": 0, // 0代表第一级
+     "content": "这家店的商品质量非常好,物流也很快,商家服务态度也很棒!",
+     "commentType": 1,
+     //   "merchantId": 10086, 为2商户评论需要填
+     }
+     */
+    @ApiOperation(value = "新增评论", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    @PostMapping("/addComment")
+    public R addComment(@RequestBody CommonComment commonComment) {
+        Integer addComment = commonCommentService.addComment(commonComment);
+        if (addComment == 0) {
+            return R.success("新增评论成功");
+        }
+        if (addComment == 2) {
+            return R.fail("新增评论失败,评论内容包含敏感字符");
+        }
+        return R.fail("新增评论失败");
+    }
+
+    /**删除评论
+     * @param commentId 评论id
+     * @return 0:成功, 1:失败
+     */
+    @ApiOperation(value = "删除评论", notes = "0:成功, 1:失败")
+    @GetMapping("/deleteComment")
+    public R deleteComment(@RequestParam Long commentId) {
+        boolean b = commonCommentService.removeById(commentId);
+        if (b) {
+            return R.success("删除评论成功");
+        }
+        return R.fail("删除评论失败");
+    }
+
+}
+

+ 140 - 0
alien-store/src/main/java/shop/alien/store/controller/CommonRatingController.java

@@ -0,0 +1,140 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.CommonRating;
+import shop.alien.store.service.CommonCommentService;
+import shop.alien.store.service.CommonRatingService;
+
+/**
+ * 评价表 前端控制器
+ *
+ * @author lyx
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"评价管理"})
+@ApiSort(12)
+@CrossOrigin
+@RestController
+@RequestMapping("/commonRating")
+@RequiredArgsConstructor
+public class CommonRatingController {
+
+    private final CommonRatingService commonRatingService;
+    private final CommonCommentService commonCommentService;
+
+    @ApiOperation("分页查询评价列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "businessType", value = "业务类型:1-店铺", dataType = "Integer", paramType = "query"),
+            @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")
+    })
+    @GetMapping("/getList")
+    public R getList(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize,
+            @RequestParam(required = false) Integer businessType,
+            @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);
+    }
+
+    @ApiOperation("获取全部评价数量(好评,中评,差评)")
+    @ApiImplicitParams({
+           @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getRatingCount")
+    public R getRatingCount(@RequestParam Integer businessId, @RequestParam Integer businessType) {
+        log.info("CommonRatingController.getRatingCount?businessId={}&businessType={}", businessId, businessType);
+        return R.data(commonRatingService.getRatingCount(businessId, businessType));
+    }
+
+//
+//    @ApiOperation("根据ID获取评价详情")
+//    @ApiImplicitParam(name = "id", value = "评价ID", dataType = "Long", paramType = "path", required = true)
+//    @GetMapping("/getById/{id}")
+//    public R<CommonRating> getById(@PathVariable Long id) {
+//        log.info("CommonRatingController.getById?id={}", id);
+//        return R.data(commonRatingService.getById(id));
+//    }
+//
+    @ApiOperation(value = "新增评价", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    @PostMapping("/addRating")
+    public R<Integer> add(@RequestBody CommonRating commonRating) {
+        log.info("CommonRatingController.add?commonRating={}", commonRating);
+        return R.data(commonRatingService.saveCommonRating(commonRating));
+    }
+
+    @ApiOperation("获取评价详情,和所有回复")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "评价主键", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "当前用户ID", dataType = "Long", paramType = "query", required = true)
+    })
+    @GetMapping("/getRatingDetail")
+    public R getCommentDetail(@RequestParam Integer ratingId, @RequestParam Long userId) {
+        log.info("CommonRatingController.getRatingDetail?id={}&userId={}", ratingId, userId);
+        if (ratingId == null) {
+            throw new IllegalArgumentException("参数[ratingId]不能为空");
+        }
+        if (userId == null) {
+            throw new IllegalArgumentException("参数[userId]不能为空");
+        }
+        return R.data(commonRatingService.getRatingDetail(ratingId, userId));
+    }
+
+//
+//    @ApiOperation("更新评价")
+//    @PutMapping("/update")
+//    public R<Boolean> update(@RequestBody CommonRating commonRating) {
+//        log.info("CommonRatingController.update?commonRating={}", commonRating);
+//        return R.data(commonRatingService.updateById(commonRating));
+//    }
+//
+//    @ApiOperation("删除评价")
+//    @ApiImplicitParam(name = "id", value = "评价ID", dataType = "Long", paramType = "path", required = true)
+//    @DeleteMapping("/delete/{id}")
+//    public R<Boolean> delete(@PathVariable Long id) {
+//        log.info("CommonRatingController.delete?id={}", id);
+//        return R.data(commonRatingService.removeById(id));
+//    }
+//
+//    @ApiOperation("获取平均评分")
+//    @ApiImplicitParams({
+//            @ApiImplicitParam(name = "businessType", value = "业务类型", dataType = "Integer", paramType = "query", required = true),
+//            @ApiImplicitParam(name = "businessId", value = "业务ID", dataType = "Long", paramType = "query", required = true)
+//    })
+//    @GetMapping("/getAverageScore")
+//    public R<Double> getAverageScore(
+//            @RequestParam Integer businessType,
+//            @RequestParam Long businessId) {
+//        log.info("CommonRatingController.getAverageScore?businessType={}&businessId={}", businessType, businessId);
+//        return R.data(commonRatingService.getAverageScore(businessType, businessId));
+//    }
+//
+//    @ApiOperation("获取评价数量")
+//    @ApiImplicitParams({
+//            @ApiImplicitParam(name = "businessType", value = "业务类型", dataType = "Integer", paramType = "query", required = true),
+//            @ApiImplicitParam(name = "businessId", value = "业务ID", dataType = "Long", paramType = "query", required = true)
+//    })
+//    @GetMapping("/getRatingCount")
+//    public R<Long> getRatingCount(
+//            @RequestParam Integer businessType,
+//            @RequestParam Long businessId) {
+//        log.info("CommonRatingController.getRatingCount?businessType={}&businessId={}", businessType, businessId);
+//        return R.data(commonRatingService.getRatingCount(businessType, businessId));
+//    }
+
+}
+

+ 31 - 20
alien-store/src/main/java/shop/alien/store/controller/StoreCommentController.java

@@ -7,11 +7,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.result.R;
-import shop.alien.entity.store.vo.LifeUserOrderCommentVo;
-import shop.alien.entity.store.vo.StoreCommentCountVo;
-import shop.alien.entity.store.vo.StoreCommentVo;
-import shop.alien.entity.store.vo.StoreCommitPercentVo;
-import shop.alien.entity.store.vo.UserOrderVo;
+import shop.alien.entity.store.vo.*;
 import shop.alien.store.service.StoreCommentService;
 
 import java.util.Map;
@@ -120,6 +116,7 @@ public class StoreCommentController {
             @ApiImplicitParam(name = "phoneId", value = "用户id", dataType = "String", paramType = "query")
     })
     @PostMapping("/saveComment")
+    @Deprecated
     public R<Integer> saveComment(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId) {
         log.info("StoreCommentController.saveComment?id={}&businessId={}&businessType={}&storeId={}&userId={}&replyId={}&commentContent={}&score={}&otherScore={}&isAnonymous={}&evaluationTags={}&phoneId={}", id, businessId, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId);
         Set<String> fileNameSet = multipartRequest.getMultiFileMap().keySet();
@@ -127,30 +124,44 @@ public class StoreCommentController {
         return R.data(storeCommentService.addComment(multipartRequest, id, businessId, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId));
     }
 
-    @ApiOperation(value = "新增或修改评论/评价(new)", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    @ApiOperation(value = "新增或修改评论/评价(单独对商家)", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
     @ApiImplicitParams({
-            @ApiImplicitParam(name = "multipartRequest", value = "文件", dataType = "File", paramType = "query"),
+//            @ApiImplicitParam(name = "multipartRequest", value = "文件", dataType = "File", paramType = "query"),
+            @ApiImplicitParam(name = "imageUrls", value = "图片url", dataType = "String", paramType = "query"),
             @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价,6:评论店铺)", dataType = "Integer", paramType = "query"),
             @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "orderId", value = "订单id", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "lawyerId", value = "律师id", dataType = "Integer", paramType = "query"),
             @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", paramType = "query", required = true),
             @ApiImplicitParam(name = "replyId", value = "回复id", dataType = "Integer", paramType = "query"),
             @ApiImplicitParam(name = "commentContent", value = "评论内容", dataType = "String", paramType = "query"),
             @ApiImplicitParam(name = "score", value = "评分", dataType = "Double", paramType = "query"),
             @ApiImplicitParam(name = "otherScore", value = "其他评分", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "isAnonymous", value = "是否匿名(0:否(默认), 1:是)", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "evaluationTags", value = "评价标签", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "phoneId", value = "用户id", dataType = "String", paramType = "query")
+            @ApiImplicitParam(name = "isAnonymous", value = "是否匿名(0:否(默认), 1:是)", dataType = "Integer", paramType = "query")
     })
-    @PostMapping("/saveCommentNew")
-    public R<Integer> saveCommentNew(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer orderId, Integer lawyerId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId) {
-        log.info("StoreCommentController.saveComment?id={}&businessId={}&businessType={}&storeId={}&userId={}&replyId={}&commentContent={}&score={}&otherScore={}&isAnonymous={}&evaluationTags={}&phoneId={}", id, businessId, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId);
-        Set<String> fileNameSet = multipartRequest.getMultiFileMap().keySet();
-        log.info(String.valueOf(fileNameSet.size()));
-        return R.data(storeCommentService.addCommentNew(multipartRequest, id, businessId, businessType, storeId,storeId,storeId, orderId, lawyerId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId));
+    @PostMapping("/saveCommentOnlyStore")
+    public R<Integer> saveCommentOnlyStore(@RequestBody Map<String,String> map) {
+//        log.info("StoreCommentController.saveComment?id={}&businessType={}&storeId={}&userId={}&replyId={}&commentContent={}&score={}&otherScore={}&isAnonymous={}", id, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous);
+//        log.info(String.valueOf(imageUrls));
+        String imageUrls = map.get("imageUrls");
+        String s = map.get("id");
+        Integer id = null;
+        if(null != s) {
+            id =  Integer.parseInt(s);
+        }
+        Integer businessType = Integer.parseInt(map.get("businessType"));
+        Integer storeId = Integer.parseInt(map.get("storeId"));
+        Integer userId = Integer.parseInt(map.get("userId"));
+        String b = map.get("replyId");
+        Integer replyId = null;
+        if(null != b) {
+            replyId =  Integer.parseInt(b);
+        }
+
+        String commentContent = map.get("commentContent");
+        Double score = Double.parseDouble(map.get("score"));
+        String otherScore = map.get("otherScore");
+        Integer isAnonymous = Integer.parseInt(map.get("isAnonymous"));
+        return R.data(storeCommentService.saveCommentOnlyStore(imageUrls, id, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous));
     }
 
     @ApiOperation(value = "回复率, 评价比例")

+ 20 - 3
alien-store/src/main/java/shop/alien/store/controller/StoreImgController.java

@@ -7,10 +7,12 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.StoreOfficialAlbum;
 import shop.alien.entity.store.vo.StoreImgInfoVo;
 import shop.alien.entity.store.vo.StoreImgTypeVo;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.store.service.StoreInfoService;
+import shop.alien.store.service.StoreOfficialAlbumService;
 import shop.alien.store.util.GroupConstant;
 
 import java.util.List;
@@ -31,7 +33,7 @@ import java.util.List;
 public class StoreImgController {
     private final StoreImgService storeImgService;
     private final StoreInfoService storeInfoService;
-
+    private final StoreOfficialAlbumService storeOfficialAlbumService;
     @ApiOperation("获取图片")
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({@ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true),
@@ -75,11 +77,26 @@ public class StoreImgController {
         if (storeImgInfoVo.getStoreId()==null || storeImgInfoVo.getImgType()==null) {
             return R.fail("失败");
         }
-        
+        if(storeImgInfoVo.getStoreImgList().isEmpty()){
+            return R.fail("图片列表为空,请重新上传图片");
+        }
         // 判断是否是头图(20:单图模式, 21:多图模式)
         Integer imgType = storeImgInfoVo.getImgType();
         boolean isHeadImage = (imgType == 20 || imgType == 21);
-        
+        if(imgType==4){
+            Integer storeId = storeImgInfoVo.getStoreId();
+            StoreOfficialAlbum album = storeOfficialAlbumService.lambdaQuery()
+                    .eq(StoreOfficialAlbum::getStoreId, storeId).one();
+            if (null == album) {
+                return R.fail("没有默认环境相册");
+            }
+            storeImgList.forEach(storeImg -> storeImg.setBusinessId(album.getId()));
+            // 添加图片时 ,修改数量
+            storeOfficialAlbumService.lambdaUpdate()
+                    .eq(StoreOfficialAlbum::getStoreId, storeId)
+                    .setSql("img_count = img_count + " + storeImgList.size())
+                    .update();
+        }
         // 清空storeid,imgType下图片
         int deleteCount = storeImgService.saveOrUpdateImg(storeImgInfoVo.getStoreId(),storeImgInfoVo.getImgType());
         log.info("StoreImgController.updateStoreImgModeInfo?deleteCount={}", deleteCount);

+ 3 - 3
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -872,7 +872,7 @@ public class StoreInfoController {
         log.info("StoreInfoController.getStoreEvaluateTags?storeId={}, tagType={}", storeId);
         Map<String,Object> map = new HashMap<>();
         List<TagsMainVo> voList = tagsMainMapper.getStoreEvaluateTags(storeId);
-        if(voList !=null && voList.size()>0){
+//        if(voList !=null && voList.size()>0){
             StoreCommentVo storeComment = storeCommentMapper.getCommentOneInfo(storeId);
             if(storeComment!=null){
                 if(storeComment.getImgId()!=null){
@@ -882,14 +882,14 @@ public class StoreInfoController {
                 }
                 LambdaQueryWrapper<StoreComment> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
                 lambdaQueryWrapper1.eq(StoreComment :: getStoreId, storeId);
-                lambdaQueryWrapper1.eq(StoreComment :: getBusinessType, 5);
+                lambdaQueryWrapper1.eq(StoreComment :: getBusinessType, 6);
                 lambdaQueryWrapper1.eq(StoreComment :: getDeleteFlag, 0);
                 Integer commitCount = storeCommentMapper.selectCount(lambdaQueryWrapper1);
                 map.put("commitCount",commitCount);
             }
             map.put("tag",voList);
             map.put("evaluate",storeComment);
-        }
+//        }
         return R.data(map);
     }
 

+ 13 - 0
alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java

@@ -12,6 +12,8 @@ import shop.alien.entity.store.StoreInfo;
 import shop.alien.entity.store.StorePrice;
 import shop.alien.mapper.StoreInfoMapper;
 import shop.alien.store.service.StorePriceService;
+import shop.alien.util.encryption.Decrypt;
+import shop.alien.util.encryption.Encrypt;
 
 import java.util.List;
 
@@ -240,5 +242,16 @@ public class StorePriceController {
         }
         return R.fail("操作失败");
     }
+
+    @ApiOperation("加解密测试接口")
+    @ApiOperationSupport(order = 10)
+    @PostMapping("/testEncryption")
+    @Decrypt
+    @Encrypt
+    public R<StorePrice> testEncryption(@RequestBody StorePrice storePrice) {
+        log.info("加解密测试接口接收数据: {}", storePrice);
+        // 原样返回,测试响应加密
+        return R.data(storePrice, "加解密测试成功");
+    }
 }
 

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

@@ -234,8 +234,8 @@ public class StoreStaffTitleController {
             StoreStaffTitle result = storeStaffTitleService.getStaffTitleDetail(storeId);
 
             if (result == null) {
-                log.warn("查询员工标题详情失败,记录不存在:storeId={}", storeId);
-                return R.fail("该门店还没有创建标题");
+                log.info("查询员工标题详情失败,记录不存在:storeId={}", storeId);
+                return R.success("该门店还没有创建标题");
             }
 
             log.info("查询员工标题详情成功,storeId={},id={},标题名称={}", storeId, result.getId(), result.getTitleName());

+ 6 - 4
alien-store/src/main/java/shop/alien/store/service/BarPerformanceService.java

@@ -2,6 +2,7 @@ package shop.alien.store.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import shop.alien.entity.store.BarPerformance;
+import shop.alien.entity.store.vo.BarPerformanceDetailVo;
 
 /**
  * 酒吧演出服务接口
@@ -24,19 +25,20 @@ public interface BarPerformanceService {
 
     /**
      * 新增或更新酒吧演出
+     * 审核不通过时也会保存数据,但会标记为审核拒绝状态
      *
      * @param barPerformance 演出信息
-     * @return 操作结果
+     * @return 保存后的演出对象(包含ID和审核状态)
      */
-    int addOrUpdateBarPerformance(BarPerformance barPerformance);
+    BarPerformance addOrUpdateBarPerformance(BarPerformance barPerformance);
 
     /**
      * 获取酒吧演出详情
      *
      * @param id 演出ID
-     * @return 演出详情
+     * @return 演出详情(包含表演嘉宾信息)
      */
-    BarPerformance getBarPerformanceDetail(Integer id);
+    BarPerformanceDetailVo getBarPerformanceDetail(Integer id);
 
     /**
      * 删除酒吧演出

+ 16 - 0
alien-store/src/main/java/shop/alien/store/service/CommonCommentService.java

@@ -0,0 +1,16 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.CommonComment;
+
+/**
+ * 评论表 服务类
+ *
+ * @author  lyx
+ * @since 2025-01-XX
+ */
+public interface CommonCommentService extends IService<CommonComment> {
+
+    Integer addComment(CommonComment commonComment);
+}
+

+ 73 - 0
alien-store/src/main/java/shop/alien/store/service/CommonRatingService.java

@@ -0,0 +1,73 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.CommonRating;
+
+/**
+ * 评价表 服务类
+ *
+ * @author lyx
+ * @since 2025-01-XX
+ */
+public interface CommonRatingService extends IService<CommonRating> {
+
+    /**
+     * 新增评价
+     *
+     * @param commonRating 评价信息
+     * @return 是否成功
+     */
+    Integer saveCommonRating(CommonRating commonRating);
+
+/*        *
+     * 分页查询评价列表
+     *
+     * @param pageNum      页数
+     * @param pageSize     页容
+     * @param businessType 业务类型:1-订单评价 2-商品评价 3-店铺评价 4-活动评价
+     * @param businessId   业务关联ID
+     * @param userId       用户ID
+     * @param auditStatus  审核状态:0-待审核 1-通过 2-驳回
+     * @param searchScore  搜索评分:0-全部,1-好评 2-中评 3-差评,4-有图
+     * @return IPage<CommonRating>
+     */
+    R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore);
+
+     /**
+     * 获取店铺评价数量(好评,中评,差评)
+     *
+     * @param businessId  业务ID
+     * @param businessType 业务类型:1-订单评价 2-商品评价 3-店铺评价 4-活动评价
+     * @return 评价数量
+     */
+    Object getRatingCount(Integer businessId, Integer businessType);
+
+     /**
+     * 获取评价详情
+     *
+     * @param ratingId 评价ID
+     * @return 评价详情
+     */
+    Object getRatingDetail(Integer ratingId, Long userId);
+
+
+  /*  /**
+     * 根据业务类型和业务ID获取平均评分
+     *
+     * @param businessType 业务类型
+     * @param businessId   业务ID
+     * @return 平均评分
+
+    Double getAverageScore(Integer businessType, Long businessId);
+
+    *
+     * 根据业务类型和业务ID获取评价数量
+     *
+     * @param businessType 业务类型
+     * @param businessId   业务ID
+     * @return 评价数量
+
+    Long getRatingCount(Integer businessType, Long businessId);*/
+}
+

+ 40 - 1
alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java

@@ -2,6 +2,7 @@ package shop.alien.store.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
@@ -57,6 +58,10 @@ public class LifeCommentService {
 
     private final StoreStaffConfigMapper storeStaffConfigMapper;
 
+    private final  CommonRatingMapper commonRatingMapper;
+
+    private final CommonCommentMapper commonCommentMapper;
+
     /**
      * 点赞操作
      * <p>
@@ -180,6 +185,22 @@ public class LifeCommentService {
                 updateWrapper.eq(StoreStaffConfig::getId, huifuId)
                         .setSql("like_count = like_count + 1");
                 return storeStaffConfigMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_STORE.equals(type)) {
+                // 类型1:评论
+                LambdaUpdateWrapper<StoreComment> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreComment::getId, huifuId)
+                        .setSql("like_count = like_count + 1");
+                return storeCommentMapper.update(null, updateWrapper);
+            } else if (CommonConstant.RATING_LIKE.equals(type)) {
+                // 11-评价点赞:更新评价表点赞数
+                return commonRatingMapper.update(null, new UpdateWrapper<CommonRating>()
+                        .setSql("like_count = like_count + 1")
+                        .eq("id", huifuId));
+            } else if (CommonConstant.COMMENT_LIKE.equals(type)) {
+                // 12-评论点赞:更新评论表点赞数
+                return commonCommentMapper.update(null, new UpdateWrapper<CommonComment>()
+                        .setSql("like_count = like_count + 1")
+                        .eq("id", huifuId));
             } else {
                 log.warn("未知的点赞类型,type={},huifuId={}", type, huifuId);
                 return 0;
@@ -331,7 +352,25 @@ public class LifeCommentService {
                 updateWrapper.eq(StoreStaffConfig::getId, Integer.parseInt(huifuId))
                         .setSql("like_count = IF(like_count > 0, like_count - 1, 0)");
                 return storeStaffConfigMapper.update(null, updateWrapper);
-            } else {
+            } else if (CommonConstant.LIKE_TYPE_STORE.equals(type)) {
+            // 类型8:点赞店铺
+                LambdaUpdateWrapper<StoreComment> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreComment::getId, huifuId)
+                        .gt(StoreComment::getLikeCount, 0)
+                        .setSql("like_count = like_count - 1");
+                return storeCommentMapper.update(null, updateWrapper);
+            }  else if (CommonConstant.RATING_LIKE.equals(type)) {
+                // 11-评价点赞:更新评价表点赞数
+                return commonRatingMapper.update(null, new UpdateWrapper<CommonRating>()
+                        .setSql("like_count = like_count - 1")
+                        .eq("id", huifuId));
+            } else if (CommonConstant.COMMENT_LIKE.equals(type)) {
+                // 12-评论点赞:更新评论表点赞数
+                return commonCommentMapper.update(null, new UpdateWrapper<CommonComment>()
+                        .setSql("like_count = like_count - 1")
+                        .eq("id", huifuId));
+            }
+            else {
                 log.warn("未知的点赞类型,type={},huifuId={}", type, huifuId);
                 return 0;
             }

+ 34 - 0
alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java

@@ -16,6 +16,7 @@ import shop.alien.entity.store.*;
 import shop.alien.entity.store.vo.LifePinglunVo;
 import shop.alien.entity.store.vo.LifeUserDynamicsVo;
 import shop.alien.entity.store.vo.StoreCommentVo;
+import shop.alien.entity.store.vo.StoreUserVo;
 import shop.alien.mapper.*;
 
 import java.util.*;
@@ -315,6 +316,39 @@ public class LifeUserDynamicsService extends ServiceImpl<LifeUserDynamicsMapper,
             } else {
                 vo.setIsLike("0");
             }
+            
+            // 查询用户名称和头像
+            String phoneId = vo.getPhoneId();
+            if (StringUtils.hasText(phoneId)) {
+                if (phoneId.startsWith("user_")) {
+                    // 用户类型
+                    String userPhone = phoneId.substring(5);
+                    LifeUser lifeUser = lifeUserService.getUserByPhone(userPhone);
+                    if (lifeUser != null) {
+                        vo.setUserName(lifeUser.getUserName());
+                        vo.setUserImage(lifeUser.getUserImage());
+                    }
+                } else if (phoneId.startsWith("store_")) {
+                    // 商户类型
+                    String storePhoneNum = phoneId.substring(6);
+                    StoreUserVo storeUser = storeUserService.getUserByPhone(storePhoneNum);
+                    if (storeUser != null) {
+                        // 查询店铺信息获取店铺名称
+                        if (storeUser.getStoreId() != null) {
+                            StoreInfo storeInfo = storeInfoMapper.selectById(storeUser.getStoreId());
+                            if (storeInfo != null && StringUtils.hasText(storeInfo.getStoreName())) {
+                                vo.setUserName(storeInfo.getStoreName());
+                            } else {
+                                vo.setUserName(storeUser.getNickName());
+                            }
+                        } else {
+                            vo.setUserName(storeUser.getNickName());
+                        }
+                        // 设置商户头像
+                        vo.setUserImage(storeUser.getHeadImg());
+                    }
+                }
+            }
         }
             return lifeUserDynamicsVos;
     }

+ 15 - 19
alien-store/src/main/java/shop/alien/store/service/StoreCommentService.java

@@ -93,25 +93,6 @@ public interface StoreCommentService extends IService<StoreComment> {
      */
     Integer addComment(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId);
 
-    /**
-     * 新增或修改评论/评价
-     *
-     * @param multipartRequest 文件
-     * @param id               主键
-     * @param businessId       业务id
-     * @param businessType     业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
-     * @param storeId          门店id
-     * @param userId           用户id
-     * @param replyId          回复id
-     * @param commentContent   评价内容
-     * @param score            评分
-     * @param otherScore       其他评分
-     * @param isAnonymous      是否匿名(0:否(默认), 1:是)
-     * @param evaluationTags   评价标签
-     * @param phoneId          用户id
-     * @return 0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常
-     */
-    Integer addCommentNew(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer orderId, Integer lawyerId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId);
 
     /**
      * 回复率, 评价比例
@@ -145,4 +126,19 @@ public interface StoreCommentService extends IService<StoreComment> {
      */
     IPage<UserOrderVo> getUserAllOrders(Integer pageNum, Integer pageSize, Integer userId, Integer type);
 
+    /**
+     * 新增或修改评论/评价(仅店铺)
+     * @param imageUrls 图片url
+     * @param id               主键
+     * @param businessType     业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
+     * @param storeId          门店id
+     * @param userId           用户id
+     * @param replyId          回复id
+     * @param commentContent   评价内容
+     * @param score            评分
+     * @param otherScore       其他评分
+     * @param isAnonymous      是否匿名(0:否(默认), 1:是)
+     * @return 0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常
+     */
+    Integer saveCommentOnlyStore(String imageUrls, Integer id, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous);
 }

+ 159 - 41
alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java

@@ -7,15 +7,21 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import shop.alien.entity.store.BarPerformance;
+import shop.alien.entity.store.StoreStaffConfig;
+import shop.alien.entity.store.vo.BarPerformanceDetailVo;
+import shop.alien.entity.store.vo.PerformerVo;
 import shop.alien.mapper.BarPerformanceMapper;
+import shop.alien.mapper.StoreStaffConfigMapper;
 import shop.alien.store.service.BarPerformanceService;
 import shop.alien.store.util.ai.AiContentModerationUtil;
 
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 酒吧演出服务实现类
@@ -30,6 +36,7 @@ import java.util.List;
 public class BarPerformanceServiceImpl implements BarPerformanceService {
 
     private final BarPerformanceMapper barPerformanceMapper;
+    private final StoreStaffConfigMapper storeStaffConfigMapper;
     private final AiContentModerationUtil aiContentModerationUtil;
 
     @Override
@@ -46,7 +53,7 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
     }
 
     @Override
-    public int addOrUpdateBarPerformance(BarPerformance barPerformance) {
+    public BarPerformance addOrUpdateBarPerformance(BarPerformance barPerformance) {
         // 1. 演出名称验证:必填且限20字
         if (StringUtils.isEmpty(barPerformance.getPerformanceName())) {
             throw new IllegalArgumentException("演出名称不能为空");
@@ -90,14 +97,6 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                 if (barPerformance.getSingleEndDatetime() == null) {
                     throw new IllegalArgumentException("单次演出必须填写结束时间");
                 }
-                // 验证开始时间必须是今天或以后
-                if (barPerformance.getSingleStartDatetime().before(todayStart)) {
-                    throw new IllegalArgumentException("开始时间必须是今天或以后");
-                }
-                // 确保开始时间早于结束时间
-                if (barPerformance.getSingleStartDatetime().after(barPerformance.getSingleEndDatetime())) {
-                    throw new IllegalArgumentException("开始时间不能晚于结束时间");
-                }
                 // 单次演出不需要周天数据,清除周天字段(设置为空字符串,确保数据库插入时包含该字段)
                 barPerformance.setPerformanceWeek("");
                 break;
@@ -123,10 +122,6 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                 if (barPerformance.getSingleEndDatetime() == null) {
                     throw new IllegalArgumentException("每天定时演出必须填写结束时间");
                 }
-                // 验证时间逻辑:开始时间必须早于结束时间(只比较时间部分,忽略日期部分)
-                if (!isTimeBefore(barPerformance.getSingleStartDatetime(), barPerformance.getSingleEndDatetime())) {
-                    throw new IllegalArgumentException("开始时间必须早于结束时间");
-                }
                 // 每天定时不需要周天数据,清除周天字段(设置为空字符串,确保数据库插入时包含该字段)
                 barPerformance.setPerformanceWeek("");
                 break;
@@ -156,10 +151,6 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                 if (barPerformance.getSingleEndDatetime() == null) {
                     throw new IllegalArgumentException("每周定时演出必须填写结束时间");
                 }
-                // 验证时间逻辑:开始时间必须早于结束时间(只比较时间部分,忽略日期部分)
-                if (!isTimeBefore(barPerformance.getSingleStartDatetime(), barPerformance.getSingleEndDatetime())) {
-                    throw new IllegalArgumentException("开始时间必须早于结束时间");
-                }
                 break;
             default:
                 throw new IllegalArgumentException("演出频次类型无效");
@@ -190,14 +181,15 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
         }
 
         // 8. 图文内容审核(调用AI内容审核接口)- 新增或更新时都必须进行
-        // - 文本:名称 + 详情 + 须知 + 风格(如果有
+        // - 文本:名称 + 风格 + 演出详情文字 + 演出须知(文本字段,不包含图片URL
         // - 图片:海报URL(如果有) + 图文详情图片URL(如果有)
         String moderationText = buildModerationText(barPerformance);
         List<String> imageUrls = new ArrayList<>();
+        // 添加演出海报到图片审核列表
         if (StringUtils.isNotEmpty(barPerformance.getPerformancePoster())) {
             imageUrls.add(barPerformance.getPerformancePoster());
         }
-        // 添加图文详情图片到审核列表
+        // 添加图文详情图片到图片审核列表(performanceDetail是图片URL列表,只做图片审核)
         if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
             String[] detailImages = barPerformance.getPerformanceDetail().split(",");
             for (String imageUrl : detailImages) {
@@ -206,22 +198,28 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                 }
             }
         }
-        // 调用AI内容审核接口,如果检测到违规内容会抛出异常
+        // 调用AI内容审核接口,文本和图片分开审核
+        // 审核失败时保存数据但标记为审核拒绝状态
         AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(moderationText, imageUrls);
         if (auditResult == null || !auditResult.isPassed()) {
-            // AI审核失败,设置审核状态为2(审核拒绝)并记录拒绝原因
+            // AI审核失败,设置审核状态为2(审核拒绝)并记录拒绝原因,但仍然保存数据
             String failureReason = (auditResult != null && StringUtils.isNotEmpty(auditResult.getFailureReason()))
                     ? auditResult.getFailureReason()
                     : "内容包含违规信息";
             barPerformance.setReviewStatus(2); // 审核拒绝
             barPerformance.setRejectReason(failureReason);
-            log.warn("酒吧演出内容审核失败:{}", failureReason);
-            throw new IllegalArgumentException("内容审核未通过:" + failureReason);
+            log.warn("酒吧演出内容审核失败:{},将保存数据并标记为审核拒绝状态", failureReason);
+            // 不抛出异常,继续保存数据,但reviewStatus已设置为2(审核拒绝)
+        } else {
+            // AI审核通过,设置审核状态为1(审核通过),清除拒绝原因
+            barPerformance.setReviewStatus(1); // 审核通过
+            barPerformance.setRejectReason(null); // 清除拒绝原因
+            // AI审核通过后,如果上线状态未设置,则设置为上线(1)
+            if (barPerformance.getOnlineStatus() == null) {
+                barPerformance.setOnlineStatus(1); // 上线
+            }
+            log.info("酒吧演出内容审核通过");
         }
-        // AI审核通过,设置审核状态为1(审核通过),清除拒绝原因
-        barPerformance.setReviewStatus(1); // 审核通过
-        barPerformance.setRejectReason(null); // 清除拒绝原因
-        log.info("酒吧演出内容审核通过");
 
         Integer id = barPerformance.getId();
 
@@ -238,14 +236,21 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                 barPerformance.setStatus(0);
             }
             // 审核状态已在AI审核结果后设置(1-审核通过 或 2-审核拒绝)
-            // 如果AI审核通过,reviewStatus已设置为1;如果AI审核失败,reviewStatus已设置为2
+            // 如果AI审核通过,reviewStatus已设置为1,并且onlineStatus已设置为1(上线)
+            // 如果AI审核失败,reviewStatus已设置为2,onlineStatus保持为null或0(下线)
             // 如果为null(理论上不应该出现,因为已经执行了AI审核),设置为0(待审核)
             if (barPerformance.getReviewStatus() == null) {
                 barPerformance.setReviewStatus(0);
             }
-            // 设置默认上线状态为下线(0),AI审核通过后仍然保持下线状态
+            // 如果上线状态未设置,则根据审核状态设置:
+            // - 审核通过(reviewStatus=1):设置为上线(1)
+            // - 其他情况:设置为下线(0)
             if (barPerformance.getOnlineStatus() == null) {
-                barPerformance.setOnlineStatus(0);
+                if (barPerformance.getReviewStatus() != null && barPerformance.getReviewStatus() == 1) {
+                    barPerformance.setOnlineStatus(1); // 审核通过,设置为上线
+                } else {
+                    barPerformance.setOnlineStatus(0); // 审核未通过或其他情况,设置为下线
+                }
             }
             // 设置默认隐藏状态为不隐藏(0)
             if (barPerformance.getHidden() == null) {
@@ -257,7 +262,12 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
             if (barPerformance.getPerformanceWeek() == null) {
                 barPerformance.setPerformanceWeek("");
             }
-            return barPerformanceMapper.insert(barPerformance);
+            int result = barPerformanceMapper.insert(barPerformance);
+            if (result > 0) {
+                // 返回保存后的对象,包含自动生成的ID和审核状态
+                return barPerformance;
+            }
+            return null;
         } else {
             // 更新操作:先查询记录是否存在
             BarPerformance existing = barPerformanceMapper.selectById(id);
@@ -274,14 +284,21 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                     barPerformance.setStatus(0);
                 }
                 // 审核状态已在AI审核结果后设置(1-审核通过 或 2-审核拒绝)
-                // 如果AI审核通过,reviewStatus已设置为1;如果AI审核失败,reviewStatus已设置为2
+                // 如果AI审核通过,reviewStatus已设置为1,并且onlineStatus已设置为1(上线)
+                // 如果AI审核失败,reviewStatus已设置为2,onlineStatus保持为null或0(下线)
                 // 如果为null(理论上不应该出现,因为已经执行了AI审核),设置为0(待审核)
                 if (barPerformance.getReviewStatus() == null) {
                     barPerformance.setReviewStatus(0);
                 }
-                // 设置默认上线状态为下线(0),AI审核通过后仍然保持下线状态
+                // 如果上线状态未设置,则根据审核状态设置:
+                // - 审核通过(reviewStatus=1):设置为上线(1)
+                // - 其他情况:设置为下线(0)
                 if (barPerformance.getOnlineStatus() == null) {
-                    barPerformance.setOnlineStatus(0);
+                    if (barPerformance.getReviewStatus() != null && barPerformance.getReviewStatus() == 1) {
+                        barPerformance.setOnlineStatus(1); // 审核通过,设置为上线
+                    } else {
+                        barPerformance.setOnlineStatus(0); // 审核未通过或其他情况,设置为下线
+                    }
                 }
                 if (barPerformance.getHidden() == null) {
                     barPerformance.setHidden(0);
@@ -290,25 +307,117 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
                 if (barPerformance.getPerformanceWeek() == null) {
                     barPerformance.setPerformanceWeek("");
                 }
-                return barPerformanceMapper.insert(barPerformance);
+                int result = barPerformanceMapper.insert(barPerformance);
+                if (result > 0) {
+                    // 返回保存后的对象,包含自动生成的ID和审核状态
+                    return barPerformance;
+                }
+                return null;
             } else {
                 // 记录存在,执行更新
-                return barPerformanceMapper.updateById(barPerformance);
+                int result = barPerformanceMapper.updateById(barPerformance);
+                if (result > 0) {
+                    // 返回更新后的对象(重新查询以获取完整信息)
+                    return barPerformanceMapper.selectById(id);
+                }
+                return null;
             }
         }
     }
 
     @Override
-    public BarPerformance getBarPerformanceDetail(Integer id) {
+    public BarPerformanceDetailVo getBarPerformanceDetail(Integer id) {
         if (id == null || id <= 0) {
             return null;
         }
 
+        // 查询演出基本信息
         QueryWrapper<BarPerformance> queryWrapper = new QueryWrapper<>();
         queryWrapper.eq("id", id);
         queryWrapper.eq("delete_flag", 0);
 
-        return barPerformanceMapper.selectOne(queryWrapper);
+        BarPerformance barPerformance = barPerformanceMapper.selectOne(queryWrapper);
+        if (barPerformance == null) {
+            return null;
+        }
+
+        // 转换为VO对象
+        BarPerformanceDetailVo detailVo = new BarPerformanceDetailVo();
+        BeanUtils.copyProperties(barPerformance, detailVo);
+
+        // 查询并设置表演嘉宾列表
+        List<PerformerVo> performers = queryPerformersByStaffConfigIds(barPerformance.getStaffConfigIds());
+        detailVo.setPerformers(performers);
+
+        return detailVo;
+    }
+
+    /**
+     * 根据员工配置ID字符串查询表演嘉宾列表
+     * 
+     * @param staffConfigIds 员工配置ID字符串,逗号分隔,如 "41,25"
+     * @return 表演嘉宾列表
+     */
+    private List<PerformerVo> queryPerformersByStaffConfigIds(String staffConfigIds) {
+        List<PerformerVo> performerList = new ArrayList<>();
+        
+        if (StringUtils.isEmpty(staffConfigIds)) {
+            return performerList;
+        }
+
+        try {
+            // 解析员工ID字符串
+            String[] idArray = staffConfigIds.split(",");
+            List<Integer> staffIdList = new ArrayList<>();
+            
+            for (String idStr : idArray) {
+                if (StringUtils.isNotEmpty(idStr.trim())) {
+                    try {
+                        Integer staffId = Integer.parseInt(idStr.trim());
+                        if (staffId > 0) {
+                            staffIdList.add(staffId);
+                        }
+                    } catch (NumberFormatException e) {
+                        log.warn("解析员工配置ID失败,无效的ID:{}", idStr);
+                    }
+                }
+            }
+
+            if (staffIdList.isEmpty()) {
+                return performerList;
+            }
+
+            // 批量查询员工配置信息
+            List<StoreStaffConfig> staffList = storeStaffConfigMapper.selectBatchIds(staffIdList);
+            
+            if (staffList != null && !staffList.isEmpty()) {
+                // 过滤已删除的员工,并转换为PerformerVo
+                performerList = staffList.stream()
+                        .filter(staff -> staff != null && staff.getDeleteFlag() != null && staff.getDeleteFlag() == 0)
+                        .map(this::convertToPerformerVo)
+                        .collect(Collectors.toList());
+            }
+        } catch (Exception e) {
+            log.error("查询表演嘉宾列表异常,staffConfigIds={},异常信息:{}", 
+                    staffConfigIds, e.getMessage(), e);
+        }
+
+        return performerList;
+    }
+
+    /**
+     * 将StoreStaffConfig转换为PerformerVo
+     * 
+     * @param staffConfig 员工配置信息
+     * @return 表演嘉宾VO
+     */
+    private PerformerVo convertToPerformerVo(StoreStaffConfig staffConfig) {
+        PerformerVo performerVo = new PerformerVo();
+        performerVo.setId(staffConfig.getId());
+        performerVo.setAvatar(staffConfig.getStaffImage());
+        performerVo.setName(staffConfig.getName());
+        performerVo.setStyle(staffConfig.getStaffPosition());
+        return performerVo;
     }
 
     @Override
@@ -471,26 +580,35 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
         return second1 < second2;
     }
 
+    /**
+     * 构建AI审核文本内容
+     * 只包含文本字段,不包含图片URL
+     * 
+     * @param barPerformance 演出信息
+     * @return 审核文本内容
+     */
     private String buildModerationText(BarPerformance barPerformance) {
         StringBuilder sb = new StringBuilder();
         if (barPerformance == null) {
             return "";
         }
+        // 演出名称(文本)
         if (StringUtils.isNotEmpty(barPerformance.getPerformanceName())) {
             sb.append("演出名称:").append(barPerformance.getPerformanceName()).append("\n");
         }
+        // 演出风格(文本)
         if (StringUtils.isNotEmpty(barPerformance.getPerformanceStyle())) {
             sb.append("演出风格:").append(barPerformance.getPerformanceStyle()).append("\n");
         }
+        // 演出详情文字(文本,performanceContent字段)
         if (StringUtils.isNotEmpty(barPerformance.getPerformanceContent())) {
             sb.append("演出详情:").append(barPerformance.getPerformanceContent()).append("\n");
         }
+        // 演出须知(文本)
         if (StringUtils.isNotEmpty(barPerformance.getPerformanceNotice())) {
             sb.append("演出须知:").append(barPerformance.getPerformanceNotice()).append("\n");
         }
-        if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
-            sb.append("图文详情:").append(barPerformance.getPerformanceDetail()).append("\n");
-        }
+        // 注意:performanceDetail是图片URL列表,不作为文本审核,只作为图片审核
         return sb.toString();
     }
 }

+ 103 - 0
alien-store/src/main/java/shop/alien/store/service/impl/CommonCommentServiceImpl.java

@@ -0,0 +1,103 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.CommonComment;
+import shop.alien.mapper.CommonCommentMapper;
+import shop.alien.store.service.CommonCommentService;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+
+/**
+ * 评论表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class CommonCommentServiceImpl extends ServiceImpl<CommonCommentMapper, CommonComment> implements CommonCommentService {
+
+    @Autowired
+    private TextModerationUtil textModerationUtil;
+
+    @Override
+    public Integer addComment(CommonComment commonComment) {
+        TextModerationResultVO textCheckResult = null;
+        try {
+            textCheckResult = textModerationUtil.invokeFunction(commonComment.getContent(), CommonRatingServiceImpl.SERVICES_LIST);
+            if ("high".equals(textCheckResult.getRiskLevel())) {
+                return 2;
+            }
+            return this.save(commonComment) ? 0 : 1;
+        } catch (Exception e) {
+            return 1;
+        }
+    }
+//
+//    @Override
+//    public IPage<CommonComment> getCommentList(Integer pageNum, Integer pageSize, Integer sourceType, Long sourceId, Long parentId, Integer commentType, Integer auditStatus) {
+//        Page<CommonComment> page = new Page<>(pageNum, pageSize);
+//        LambdaQueryWrapper<CommonComment> wrapper = new LambdaQueryWrapper<>();
+//
+//        if (sourceType != null) {
+//            wrapper.eq(CommonComment::getSourceType, sourceType);
+//        }
+//        if (sourceId != null) {
+//            wrapper.eq(CommonComment::getSourceId, sourceId);
+//        }
+//        if (parentId != null) {
+//            wrapper.eq(CommonComment::getParentId, parentId);
+//        }
+//        if (commentType != null) {
+//            wrapper.eq(CommonComment::getCommentType, commentType);
+//        }
+//        if (auditStatus != null) {
+//            wrapper.eq(CommonComment::getAuditStatus, auditStatus);
+//        }
+//
+//        wrapper.eq(CommonComment::getIsShow, 1);
+//        wrapper.orderByDesc(CommonComment::getCreatedTime);
+//
+//        return this.page(page, wrapper);
+//    }
+//
+//    @Override
+//    public List<CommonComment> getRootComments(Integer sourceType, Long sourceId) {
+//        LambdaQueryWrapper<CommonComment> wrapper = new LambdaQueryWrapper<>();
+//        wrapper.eq(CommonComment::getSourceType, sourceType)
+//                .eq(CommonComment::getSourceId, sourceId)
+//                .eq(CommonComment::getParentId, 0)
+//                .eq(CommonComment::getIsShow, 1)
+//                .eq(CommonComment::getAuditStatus, 1)
+//                .orderByDesc(CommonComment::getCreatedTime);
+//
+//        return this.list(wrapper);
+//    }
+//
+//    @Override
+//    public List<CommonComment> getReplyList(Long parentId) {
+//        LambdaQueryWrapper<CommonComment> wrapper = new LambdaQueryWrapper<>();
+//        wrapper.eq(CommonComment::getParentId, parentId)
+//                .eq(CommonComment::getIsShow, 1)
+//                .eq(CommonComment::getAuditStatus, 1)
+//                .orderByAsc(CommonComment::getCreatedTime);
+//
+//        return this.list(wrapper);
+//    }
+//
+//    @Override
+//    public void incrementReplyCount(Long parentId) {
+//        LambdaUpdateWrapper<CommonComment> updateWrapper = new LambdaUpdateWrapper<>();
+//        updateWrapper.eq(CommonComment::getId, parentId)
+//                .setSql("reply_count = reply_count + 1");
+//        this.update(updateWrapper);
+//    }
+}
+

+ 449 - 0
alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java

@@ -0,0 +1,449 @@
+package shop.alien.store.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.ImmutableList;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.CommonCommentVo;
+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.store.config.WebSocketProcess;
+import shop.alien.store.service.CommonRatingService;
+import shop.alien.store.util.CommonConstant;
+import shop.alien.util.common.constant.CommentSourceTypeEnum;
+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 java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 评价表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, CommonRating> implements CommonRatingService {
+
+    @Autowired
+    private TextModerationUtil textModerationUtil;
+
+    private final  CommonRatingMapper commonRatingMapper;
+    private final StoreInfoMapper storeInfoMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final WebSocketProcess webSocketProcess;
+    private final LifeUserMapper lifeUserMapper;
+    private final CommonCommentMapper commonCommentMapper;
+    private final LifeLikeRecordMapper lifeLikeRecordMapper;
+    private final LifeCollectMapper lifeCollectMapper;
+
+
+    public static final List<String> SERVICES_LIST = ImmutableList.of(
+            TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService(),
+            TextReviewServiceEnum.LLM_QUERY_MODERATION.getService()
+    );
+
+
+
+    @Override
+    public Integer saveCommonRating(CommonRating commonRating) {
+        // 1. 文本审核
+        try {
+            TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(commonRating.getContent(), SERVICES_LIST);
+            if ("high".equals(textCheckResult.getRiskLevel())) {
+                return 2;
+            }
+            // 手动存评分1,2,3
+            if (StringUtils.isNotEmpty(commonRating.getOtherScore())) {
+                JSONObject parse = JSONObject.parse(commonRating.getOtherScore());
+                commonRating.setScoreOne(parse.getDouble("scoreOne"));
+                commonRating.setScoreTwo(parse.getDouble("scoreTwo"));
+                commonRating.setScoreThree(parse.getDouble("scoreThree"));
+            }
+            int i = this.save(commonRating) ? 0 : 1;
+            // 对不同的businessType进行不同的处理,
+            doBusinessWithType(commonRating);
+            return i;
+        } catch (Exception e) {
+            log.error("CommonRatingService.saveCommonRating ERROR Msg={}", e.getMessage());
+            return 1;
+        }
+    }
+
+    /**
+     * 根据不同的businessType进行不同的处理
+     * @param commonRating 评价信息
+     */
+    private void doBusinessWithType(CommonRating commonRating) throws Exception {
+        Integer businessId = commonRating.getBusinessId();
+        Double score = commonRating.getScore();
+        if(1 == commonRating.getBusinessType()){
+            // 更新门店评价信息
+            StoreInfoScoreVo storeInfoScoreVo = commonRatingMapper.getCommentCountAndScoreInfo(commonRating.getBusinessType(),businessId);
+            double total = storeInfoScoreVo.getTotal();
+            double scoreAvg = (total == 0 ? 0 : storeInfoScoreVo.getScore() / total);
+            double scoreOne = (total == 0 ? 0 : storeInfoScoreVo.getScoreOne() / total);
+            double scoreTwo = (total == 0 ? 0 : storeInfoScoreVo.getScoreTwo() / total);
+            double scoreThree = (total == 0 ? 0 : storeInfoScoreVo.getScoreThree() / total);
+            StoreInfo storeInfo = new StoreInfo();
+            storeInfo.setId(businessId);
+            storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfo.setScoreOne(new BigDecimal(scoreOne).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfo.setScoreTwo(new BigDecimal(scoreTwo).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfo.setScoreThree(new BigDecimal(scoreThree).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfoMapper.updateById(storeInfo);
+            StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeInfo.getId()).eq(StoreUser::getDeleteFlag, 0));
+
+            // 如果差评,则发送差评提醒
+            if(score != null && score >= 0.5 && score <= 2.5){
+                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                String commonDate = simpleDateFormat.format(new Date());
+                LifeNotice lifeMessage = new LifeNotice();
+                lifeMessage.setReceiverId("store_" + storeUser.getPhone());
+                String text = "在"+commonDate+",您的店铺有一条差评记录,您可查看评价内容是否属实,如不属实,可向平台进行申诉。";
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("message", text);
+                lifeMessage.setContext(jsonObject.toJSONString());
+                lifeMessage.setTitle("差评通知");
+                lifeMessage.setSenderId("system");
+                lifeMessage.setIsRead(0);
+                lifeMessage.setNoticeType(1);
+                lifeNoticeMapper.insert(lifeMessage);
+
+                WebSocketVo websocketVo = new WebSocketVo();
+                websocketVo.setSenderId("system");
+                websocketVo.setReceiverId("store_" + storeUser.getPhone());
+                websocketVo.setCategory("notice");
+                websocketVo.setNoticeType("1");
+                websocketVo.setIsRead(0);
+                websocketVo.setText(JSONObject.from(lifeMessage).toJSONString());
+                webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
+            }
+        }
+
+    }
+
+    @Override
+    public R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore) {
+        Page<CommonRating> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
+        
+        if (businessType != null) {
+            wrapper.eq(CommonRating::getBusinessType, businessType);
+        }
+        if (businessId != null) {
+            wrapper.eq(CommonRating::getBusinessId, businessId);
+        }
+        if (auditStatus != null) {
+            wrapper.eq(CommonRating::getAuditStatus, auditStatus);
+        }
+        if (searchScore != null) {
+            if(searchScore == 1){
+                // 1-好评
+                wrapper.ge(CommonRating::getScore, 4.5);
+            }else if(searchScore == 2){
+                // 2-中评
+                wrapper.ge(CommonRating::getScore, 3.5);
+                wrapper.lt(CommonRating::getScore, 4.5);
+            }else if(searchScore == 3){
+                // 3-差评
+                wrapper.lt(CommonRating::getScore, 3.5);
+            }else if(searchScore == 4){
+                // 4-有图
+                wrapper.isNotNull(CommonRating::getImageUrls);
+            }
+        }
+        
+        wrapper.eq(CommonRating::getIsShow, 1);
+        wrapper.orderByDesc(CommonRating::getId);
+        IPage<CommonRating> page1 = this.page(page, wrapper);
+        return doListBusinessWithType(page1, businessType,userId);
+    }
+
+    @Override
+    public Object getRatingCount(Integer businessId, Integer businessType) {
+        // 查询全部评价记录
+        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(CommonRating::getBusinessId, businessId);
+        wrapper.eq(CommonRating::getBusinessType, businessType);
+        wrapper.eq(CommonRating::getIsShow, 1);
+        List<CommonRating> commonRatings = commonRatingMapper.selectList(wrapper);
+        List<Long> collect = commonRatings.stream().map(x -> x.getId()).collect(Collectors.toList());
+        // 获取评价统计信息(总评论数、有图评论数、好评数、中评数、差评数)
+        Map<String, Object> ratingCount = commonRatingMapper.getRatingCount(new QueryWrapper<CommonRating>().in("id", collect));
+        if(RatingBusinessTypeEnum.STORE_RATING.getBusinessType() == businessType){
+            // 1店铺评分
+            StoreInfo storeInfo = storeInfoMapper.selectById(businessId);
+            ratingCount.put("storeScore", storeInfo.getScoreAvg());
+
+            // 2统计评价数量
+            AtomicReference<Integer> count = new AtomicReference<>(0);
+            count.updateAndGet(v -> v + collect.size());
+            // 1.查询评价的评论的记录
+            LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<CommonComment>()
+                    .eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
+                    .in(CommonComment::getSourceId, collect);
+            List<Long> collect1 = commonCommentMapper.selectList(commentWrapper).stream().map(x -> x.getId()).collect(Collectors.toList());
+            if(!collect1.isEmpty()) {
+                count.updateAndGet(v -> v + collect1.size());
+                // 2.在再评论的表中查询评论的回复(根评论)
+                Integer allCommentsOnCommentsNum = getAllCommentsOnCommentsNum(collect1);
+                count.updateAndGet(v -> v + allCommentsOnCommentsNum);
+            }
+            ratingCount.put("totalCount", count.get());
+
+            // 3用户图片
+            List<Long> collect2 = commonRatings.stream().filter(i -> i.getScore() >= 4.5).map(CommonRating::getUserId).distinct().limit(6).collect(Collectors.toList());
+            if(!collect2.isEmpty()) {
+                List<LifeUser> lifeUsers = lifeUserMapper.selectList(new QueryWrapper<LifeUser>().lambda().in(LifeUser::getId, collect2));
+                ratingCount.put("img", lifeUsers.stream().map(LifeUser::getUserImage).collect(Collectors.toList()));
+            } else {
+                ratingCount.put("img", new ArrayList<>());
+            }
+
+
+        }
+        return ratingCount;
+    }
+
+    @Override
+    public Object getRatingDetail(Integer ratingId, Long userId) {
+        CommonRating commonRating = commonRatingMapper.selectById(ratingId);
+        if(commonRating == null){
+            throw new IllegalArgumentException("评价不存在");
+        }
+        CommonRatingVo commonRatingVo = new CommonRatingVo();
+        BeanUtils.copyProperties(commonRating, commonRatingVo);
+        LifeUser lifeUser = lifeUserMapper.selectById(Integer.parseInt(commonRating.getUserId().toString()));
+        // 设置评论用户信息
+        commonRatingVo.setUserImage(lifeUser.getUserImage());
+        commonRatingVo.setUserName(lifeUser.getUserName());
+        // 查询当前用户点赞列表(仅评价)
+        List<LifeLikeRecord> lifeLikeRecords = lifeLikeRecordMapper.selectList(new QueryWrapper<LifeLikeRecord>().lambda()
+                .eq(LifeLikeRecord::getDianzanId, userId)
+                .eq(LifeLikeRecord::getHuifuId, ratingId)
+                .eq(LifeLikeRecord::getDeleteFlag, 0)
+                .eq(LifeLikeRecord::getType, CommonConstant.RATING_LIKE));
+        if(lifeLikeRecords.size() > 0){
+            commonRatingVo.setIsLike(1);
+        } else {
+            commonRatingVo.setIsLike(0);
+        }
+        // 根据业务类型处理
+        if(commonRatingVo.getBusinessType().equals(RatingBusinessTypeEnum.STORE_RATING.getBusinessType())){
+            // 1店铺信息
+            StoreInfo storeInfo = storeInfoMapper.selectById(commonRatingVo.getBusinessId());
+            if( null != storeInfo) {
+                commonRatingVo.setStoreName(storeInfo.getStoreName());
+                commonRatingVo.setStoreEvaluate(storeInfo.getStoreEvaluate());
+                commonRatingVo.setStoreScore(storeInfo.getScoreAvg());
+                commonRatingVo.setScoreOne(storeInfo.getScoreOne());
+                commonRatingVo.setScoreTwo(storeInfo.getScoreTwo());
+                commonRatingVo.setScoreThree(storeInfo.getScoreThree());
+            }
+            // 1.1 查询是否收藏该店铺
+            LambdaQueryWrapper<LifeCollect> collectWrapper = new LambdaQueryWrapper<LifeCollect>()
+                    .eq(LifeCollect::getUserId, userId)
+                    .eq(LifeCollect::getStoreId, storeInfo.getId())
+                    .eq(LifeCollect::getDeleteFlag, 0);
+            LifeCollect lifeCollect = lifeCollectMapper.selectOne(collectWrapper);
+            if(lifeCollect != null){
+                commonRatingVo.setIsCollect(1);
+            } else {
+                commonRatingVo.setIsCollect(0);
+            }
+            // 2查询一级评价
+            QueryWrapper<CommonCommentVo> commentWrapper = new QueryWrapper<CommonCommentVo>()
+                    .eq("cc.source_type", CommentSourceTypeEnum.STORE_COMMENT.getType())
+                    .eq("cc.source_id", ratingId)
+                    .eq("cc.parent_id", 0);
+            List<CommonCommentVo> commonComments = commonCommentMapper.selectALlComment(commentWrapper,CommonConstant.COMMENT_LIKE, userId);
+
+            // 定义评论总数
+            AtomicReference<Long> count = new AtomicReference<>(0L);
+            count.updateAndGet(v -> v + commonComments.size());
+            List<CommonCommentVo> commonCommentVos = new ArrayList<>();
+            for (CommonCommentVo commonComment : commonComments) {
+//                CommonCommentVo commonCommentVo = new CommonCommentVo();
+//                BeanUtils.copyProperties(commonComment, commonCommentVo);
+                // 递归获取所有子评论(扁平化)
+                List<CommonCommentVo> allChildComments = getChildCommentsRecursively(commonComment.getId(), userId);
+                count.updateAndGet(v -> v + allChildComments.size());
+                // 一级评论本身的商家/用户标识和商家信息
+                // setStoreUserInfo(first);
+
+                // 按时间排序后绑定子评论列表
+                allChildComments.sort(Comparator.comparing(CommonCommentVo::getCreatedTime));
+
+                commonComment.setChildCommonComments(allChildComments);
+                commonCommentVos.add(commonComment);
+            }
+            commonRatingVo.setCommentCount(count.get());
+            commonRatingVo.setChildCommonComments(commonCommentVos);
+        }
+        return commonRatingVo;
+    }
+
+    private List<CommonCommentVo> getChildCommentsRecursively(Long id, Long userId) {
+        List<CommonCommentVo> allChildComments = new ArrayList<>();
+
+        // 查询直接回复当前评论的所有记录
+        QueryWrapper<CommonCommentVo> wrapper = new QueryWrapper<>();
+        wrapper.eq("cc.delete_flag", 0)
+                .eq("cc.parent_id", id)
+                .orderByAsc("cc.created_time");
+        List<CommonCommentVo> directChildren = commonCommentMapper.selectALlComment(wrapper,CommonConstant.COMMENT_LIKE, userId);
+
+        if (CollectionUtils.isEmpty(directChildren)) {
+            return allChildComments;
+        }
+        // 处理每个直接子评论
+        for (CommonCommentVo child : directChildren) {
+            // 设置商家/用户标识
+//            setStoreUserInfo(child);
+            // 递归获取该子评论的所有子评论
+            List<CommonCommentVo> grandChildren = getChildCommentsRecursively(child.getId(), userId);
+            // 将当前子评论添加到结果列表
+            allChildComments.add(child);
+            // 将该子评论的所有子评论也添加到结果列表(扁平化)
+            allChildComments.addAll(grandChildren);
+        }
+
+        return allChildComments;
+    }
+
+    @NotNull
+    private Integer getAllCommentsOnCommentsNum(List<Long> collect1) {
+        LambdaQueryWrapper<CommonComment> commentReplyWrapper = new LambdaQueryWrapper<CommonComment>()
+                .in(CommonComment::getParentId, collect1);
+        List<Long> collect2 = commonCommentMapper.selectList(commentReplyWrapper).stream().map(x -> x.getId()).collect(Collectors.toList());
+        if(collect2.isEmpty()){
+            return 0;
+        } else {
+            return collect2.size() + getAllCommentsOnCommentsNum(collect2);
+        }
+    }
+
+    private R doListBusinessWithType(IPage<CommonRating> page1, Integer businessType, Long userId) {
+        if(businessType == RatingBusinessTypeEnum.STORE_RATING.getBusinessType()){
+            IPage<CommonRatingVo> result = new Page<>(page1.getPages(), page1.getSize(), page1.getTotal());
+            List resultList = new ArrayList();
+            if(page1.getRecords().isEmpty()){
+                result.setRecords(resultList);
+                return R.data(result);
+            }
+            // 1查询评价用户信息
+            Set<Long> userIdSet = page1.getRecords().stream()
+                    .map(CommonRating::getUserId)
+                    .collect(Collectors.toSet());
+            List<LifeUser> lifeUsers = new  ArrayList<>();
+            Map<Integer, LifeUser> lifeUserMap = new HashMap<>();
+            if (!userIdSet.isEmpty()){
+                lifeUsers = lifeUserMapper.selectList(
+                        new QueryWrapper<LifeUser>().lambda()
+                                .in(LifeUser::getId, userIdSet));
+                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()));
+
+
+            // 1.查询评价的评论的记录个数
+            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()
+                    .collect(Collectors.groupingBy(CommonComment::getSourceId, Collectors.counting()));
+
+            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;
+    }
+/*
+    @Override
+    public Double getAverageScore(Integer businessType, Long businessId) {
+        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(CommonRating::getBusinessType, businessType)
+                .eq(CommonRating::getBusinessId, businessId)
+                .eq(CommonRating::getIsShow, 1)
+                .eq(CommonRating::getAuditStatus, 1);
+        
+        return this.list(wrapper).stream()
+                .map(CommonRating::getScore)
+                .filter(score -> score != null)
+                .mapToDouble(BigDecimal::doubleValue)
+                .average()
+                .orElse(0.0);
+    }
+
+    @Override
+    public Long getRatingCount(Integer businessType, Long businessId) {
+        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(CommonRating::getBusinessType, businessType)
+                .eq(CommonRating::getBusinessId, businessId)
+                .eq(CommonRating::getIsShow, 1)
+                .eq(CommonRating::getAuditStatus, 1);
+        
+        return (long) this.count(wrapper);
+    }*/
+}
+

+ 3 - 0
alien-store/src/main/java/shop/alien/store/service/impl/PerformanceListServiceImpl.java

@@ -838,6 +838,9 @@ public class PerformanceListServiceImpl implements PerformanceListService {
             // 设置演出人员数量
             vo.setGuestCount(guestList != null ? guestList.size() : 0);
 
+            // 设置演出图片
+            vo.setPerformanceDetail(performance.getPerformanceDetail());
+
             log.info("查询演出详情成功,id={},嘉宾数量:{}", id,
                     vo.getGuestCount());
             return vo;

+ 138 - 154
alien-store/src/main/java/shop/alien/store/service/impl/StoreCommentServiceImpl.java

@@ -1,10 +1,8 @@
 package shop.alien.store.service.impl;
 
-import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
@@ -22,11 +20,8 @@ import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.vo.*;
 import shop.alien.mapper.*;
-import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
-import shop.alien.entity.store.vo.UserOrderVo;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.StoreCommentService;
-import shop.alien.store.service.StoreImgService;
 import shop.alien.store.util.FileUploadUtil;
 import shop.alien.util.common.DateUtils;
 import shop.alien.util.common.netease.ImageCheckUtil;
@@ -582,7 +577,7 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
      */
     @Override
     public Integer addComment(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId) {
-        try {
+      /*  try {
             List<String> servicesList = Lists.newArrayList();
             servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
             servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
@@ -591,10 +586,10 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
                 return 2;
             }
 
-            /*Map<String, String> checkText = TextCheckUtil.check(commentContent);
+            *//*Map<String, String> checkText = TextCheckUtil.check(commentContent);
             if (null == checkText || checkText.get("result").equals("1")) {
                 return 2;
-            }*/
+            }*//*
             StoreComment storeComment = new StoreComment();
             storeComment.setId(id);
             storeComment.setStoreId(storeId);
@@ -704,153 +699,10 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
             log.error("StoreCommentService.userComment ERROR Msg={}", e.getMessage());
             return 1;
         }
-
+*/
+        return 0;
     }
 
-    /**
-     * 新增或修改评论/评价
-     *
-     * @param multipartRequest 文件
-     * @param id               主键
-     * @param businessId       业务id
-     * @param businessType     业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
-     * @param storeId          门店id
-     * @param userId           用户id
-     * @param replyId          回复id
-     * @param commentContent   评价内容
-     * @param score            评分
-     * @param otherScore       其他评分
-     * @param isAnonymous      是否匿名(0:否(默认), 1:是)
-     * @param evaluationTags   评价标签
-     * @param phoneId          用户id
-     * @return 0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常
-     */
-    @Override
-    public Integer addCommentNew(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer orderId, Integer lawyerId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId) {
-        try {
-            List<String> servicesList = Lists.newArrayList();
-            servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
-            servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
-            TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(commentContent, servicesList);
-            if ("high".equals(textCheckResult.getRiskLevel())) {
-                return 2;
-            }
-
-            /*Map<String, String> checkText = TextCheckUtil.check(commentContent);
-            if (null == checkText || checkText.get("result").equals("1")) {
-                return 2;
-            }*/
-            StoreComment storeComment = new StoreComment();
-            storeComment.setId(id);
-            storeComment.setStoreId(storeId);
-            storeComment.setUserId(userId);
-            storeComment.setCommentContent(commentContent);
-            storeComment.setReplyId(replyId);
-            storeComment.setBusinessId(businessId);
-            storeComment.setBusinessType(businessType);
-            storeComment.setScore(score);
-
-            if (StringUtils.isNotEmpty(otherScore)) {
-                List<LifeCouponVo> lifeCouponVos = JSONArray.parseArray(otherScore, LifeCouponVo.class);
-                lifeCouponVos.stream().filter(i -> "口味".equals(i.getName())).findFirst().ifPresent(item -> storeComment.setTasteScore(Double.valueOf(item.getRateScore())));
-                lifeCouponVos.stream().filter(i -> "环境".equals(i.getName())).findFirst().ifPresent(item -> storeComment.setEnScore(Double.valueOf(item.getRateScore())));
-                lifeCouponVos.stream().filter(i -> "服务".equals(i.getName())).findFirst().ifPresent(item -> storeComment.setServiceScore(Double.valueOf(item.getRateScore())));
-            }
-            storeComment.setOtherScore(otherScore);
-            storeComment.setIsAnonymous(isAnonymous);
-            storeComment.setEvaluationTags(evaluationTags);
-            storeComment.setPhoneId(phoneId);
-            List<String> fileNameSet = new ArrayList<>(multipartRequest.getMultiFileMap().keySet());
-            if (!fileNameSet.isEmpty() && storeId != null) {
-                StringBuilder imgId = new StringBuilder();
-                for (int i = 0; i < fileNameSet.size(); i++) {
-                    MultipartFile multipartFile = multipartRequest.getFileMap().get(fileNameSet.get(i));
-                    //b
-                    System.out.println(multipartFile.getSize());
-                    //kb
-                    System.out.println(multipartFile.getSize() / 1024);
-                    if (null != multipartFile && multipartFile.getSize() / 1024 > 0) {
-                        byte[] fileByte;
-                        try {
-                            fileByte = multipartFile.getBytes();
-                        } catch (IOException e) {
-                            return 1;
-                        }
-                        String base64 = Base64.getEncoder().encodeToString(fileByte);
-                        Map<String, String> checkImage = ImageCheckUtil.check(base64, 2);
-                        if (checkImage != null && checkImage.get("result").equals("1")) {
-                            return 3;
-                        }
-                        StoreImg storeImg = new StoreImg();
-                        storeImg.setStoreId(storeComment.getStoreId());
-                        storeImg.setImgType(8);
-                        storeImg.setImgSort(i + 1);
-                        storeImg.setImgUrl(fileUploadUtil.uploadOneFile(multipartFile));
-                        storeImgMapper.insert(storeImg);
-                        imgId.append(storeImg.getId()).append(",");
-                    }
-                }
-                if (!imgId.toString().isEmpty()) {
-                    storeComment.setImgId(imgId.substring(0, imgId.length() - 1));
-                }
-            }
-            storeComment.setCreatedUserId(storeComment.getUserId());
-            int i = this.save(storeComment) ? 0 : 1;
-
-            //判断类型如果为5是评价 更新订单表orderAppraise为1 订单已评价
-            if(businessType == 5){
-                LambdaUpdateWrapper<LifeUserOrder> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-                lambdaUpdateWrapper.eq(LifeUserOrder :: getId, businessId );
-                lambdaUpdateWrapper.set(LifeUserOrder :: getOrderAppraise, 1);
-                lifeUserOrderMapper.update(null,lambdaUpdateWrapper);
-            }
-            StoreInfoScoreVo storeInfoScoreVo = storeCommentMapper.getCommentCountAndScoreInfo(storeId);
-            double total = storeInfoScoreVo.getTotal();
-            double scoreAvg = (total == 0 ? 0 : storeInfoScoreVo.getScore() / total);
-            double tasteScore = (total == 0 ? 0 : storeInfoScoreVo.getTasteScore() / total);
-            double enScore = (total == 0 ? 0 : storeInfoScoreVo.getEnScore() / total);
-            double serviceScore = (total == 0 ? 0 : storeInfoScoreVo.getServiceScore() / total);
-            StoreInfo storeInfo = new StoreInfo();
-            storeInfo.setId(storeId);
-            storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            storeInfo.setTasteScore(new BigDecimal(tasteScore).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            storeInfo.setEnScore(new BigDecimal(enScore).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            storeInfo.setServiceScore(new BigDecimal(serviceScore).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            storeInfoMapper.updateById(storeInfo);
-            StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeInfo.getId()).eq(StoreUser::getDeleteFlag, 0));
-
-            // 如果差评,则发送差评提醒
-            if(score != null && score >= 0.5 && score <= 2.5){
-                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                String commonDate = simpleDateFormat.format(new Date());
-                LifeNotice lifeMessage = new LifeNotice();
-                lifeMessage.setReceiverId("store_" + storeUser.getPhone());
-                String text = "在"+commonDate+",您的店铺有一条差评记录,您可查看评价内容是否属实,如不属实,可向平台进行申诉。";
-                JSONObject jsonObject = new JSONObject();
-                jsonObject.put("message", text);
-                lifeMessage.setContext(jsonObject.toJSONString());
-                lifeMessage.setTitle("差评通知");
-                lifeMessage.setSenderId("system");
-                lifeMessage.setIsRead(0);
-                lifeMessage.setNoticeType(1);
-                lifeNoticeMapper.insert(lifeMessage);
-
-                WebSocketVo websocketVo = new WebSocketVo();
-                websocketVo.setSenderId("system");
-                websocketVo.setReceiverId("store_" + storeUser.getPhone());
-                websocketVo.setCategory("notice");
-                websocketVo.setNoticeType("1");
-                websocketVo.setIsRead(0);
-                websocketVo.setText(JSONObject.from(lifeMessage).toJSONString());
-                webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
-            }
-            return i;
-        } catch (Exception e) {
-            log.error("StoreCommentService.userComment ERROR Msg={}", e.getMessage());
-            return 1;
-        }
-
-    }
 
     /**
      * 回复率, 评价比例
@@ -1054,7 +906,7 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
         return new LambdaQueryWrapper<StoreComment>()
                 .eq(StoreComment::getStoreId, storeId)
                 .gt(StoreComment::getScore,0 )
-                .eq(StoreComment::getBusinessType, 5)
+                .eq(StoreComment::getBusinessType, 6)
                 .eq(StoreComment::getDeleteFlag, 0);
     }
 
@@ -1189,6 +1041,138 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
         return resultPage;
     }
 
+     @Override
+    public Integer saveCommentOnlyStore(String imageUrls, Integer id, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous) {
+        try {
+            // 1.文本审核,
+            List<String> servicesList = Lists.newArrayList();
+            servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
+            servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+            TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(commentContent, servicesList);
+            if ("high".equals(textCheckResult.getRiskLevel())) {
+                return 2;
+            }
+            // 2.构造评价实体类
+            StoreComment storeComment = new StoreComment();
+            storeComment.setId(id);
+            storeComment.setStoreId(storeId);
+            storeComment.setUserId(userId);
+            storeComment.setCommentContent(commentContent);
+            storeComment.setReplyId(replyId);
+            storeComment.setBusinessType(businessType);
+            storeComment.setScore(score);
+
+            if (StringUtils.isNotEmpty(otherScore)) {
+                JSONObject parse = JSONObject.parse(otherScore);
+                storeComment.setScoreOne(parse.getDouble("scoreOne"));
+                storeComment.setScoreTwo(parse.getDouble("scoreTwo"));
+                storeComment.setScoreThree(parse.getDouble("scoreThree"));
+            }
+            storeComment.setOtherScore(otherScore);
+            storeComment.setIsAnonymous(isAnonymous);
+//            List<String> fileNameSet = new ArrayList<>(multipartRequest.getMultiFileMap().keySet());
+            StringBuilder imgId = new StringBuilder();
+            String[] split = imageUrls.split(",");
+
+            for (int i = 0; i < split.length; i++) {
+                StoreImg storeImg = new StoreImg();
+                storeImg.setStoreId(storeComment.getStoreId());
+                storeImg.setImgType(8);
+                storeImg.setImgSort(i + 1);
+                storeImg.setImgUrl(split[i]);
+                storeImgMapper.insert(storeImg);
+                imgId.append(storeImg.getId()).append(",");
+            }
+            if (!imgId.toString().isEmpty()) {
+                storeComment.setImgId(imgId.substring(0, imgId.length() - 1));
+            }
+            storeComment.setCreatedUserId(storeComment.getUserId());
+
+/*            List<MultipartFile> fileNameSet = multipartRequest.getMultiFileMap().get("file");
+            if (!fileNameSet.isEmpty() && storeId != null) {
+                StringBuilder imgId = new StringBuilder();
+                for (int i = 0; i < fileNameSet.size(); i++) {
+                    MultipartFile multipartFile = fileNameSet.get(i);
+                    //b
+                    System.out.println(multipartFile.getSize());
+                    //kb
+                    System.out.println(multipartFile.getSize() / 1024);
+                    if (null != multipartFile && !multipartFile.isEmpty()) {
+                        byte[] fileByte;
+                        try {
+                            fileByte = multipartFile.getBytes();
+                        } catch (IOException e) {
+                            return 1;
+                        }
+                        String base64 = Base64.getEncoder().encodeToString(fileByte);
+                        Map<String, String> checkImage = ImageCheckUtil.check(base64, 2);
+                        if (checkImage != null && checkImage.get("result").equals("1")) {
+                            return 3;
+                        }
+                        StoreImg storeImg = new StoreImg();
+                        storeImg.setStoreId(storeComment.getStoreId());
+                        storeImg.setImgType(8);
+                        storeImg.setImgSort(i + 1);
+                        storeImg.setImgUrl(fileUploadUtil.uploadOneFile(multipartFile));
+                        storeImgMapper.insert(storeImg);
+                        imgId.append(storeImg.getId()).append(",");
+                    }
+                }
+                if (!imgId.toString().isEmpty()) {
+                    storeComment.setImgId(imgId.substring(0, imgId.length() - 1));
+                }
+            }*/
+            storeComment.setCreatedUserId(storeComment.getUserId());
+            int i = this.save(storeComment) ? 0 : 1;
+
+            // 更新门店评价信息
+            StoreInfoScoreVo storeInfoScoreVo = storeCommentMapper.getCommentCountAndScoreInfo(storeId);
+            double total = storeInfoScoreVo.getTotal();
+            double scoreAvg = (total == 0 ? 0 : storeInfoScoreVo.getScore() / total);
+            double scoreOne = (total == 0 ? 0 : storeInfoScoreVo.getScoreOne() / total);
+            double scoreTwo = (total == 0 ? 0 : storeInfoScoreVo.getScoreTwo() / total);
+            double scoreThree = (total == 0 ? 0 : storeInfoScoreVo.getScoreThree() / total);
+            StoreInfo storeInfo = new StoreInfo();
+            storeInfo.setId(storeId);
+            storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfo.setScoreOne(new BigDecimal(scoreOne).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfo.setScoreTwo(new BigDecimal(scoreTwo).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfo.setScoreThree(new BigDecimal(scoreThree).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            storeInfoMapper.updateById(storeInfo);
+            StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeInfo.getId()).eq(StoreUser::getDeleteFlag, 0));
+
+            // 如果差评,则发送差评提醒
+            if(score != null && score >= 0.5 && score <= 2.5){
+                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                String commonDate = simpleDateFormat.format(new Date());
+                LifeNotice lifeMessage = new LifeNotice();
+                lifeMessage.setReceiverId("store_" + storeUser.getPhone());
+                String text = "在"+commonDate+",您的店铺有一条差评记录,您可查看评价内容是否属实,如不属实,可向平台进行申诉。";
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("message", text);
+                lifeMessage.setContext(jsonObject.toJSONString());
+                lifeMessage.setTitle("差评通知");
+                lifeMessage.setSenderId("system");
+                lifeMessage.setIsRead(0);
+                lifeMessage.setNoticeType(1);
+                lifeNoticeMapper.insert(lifeMessage);
+
+                WebSocketVo websocketVo = new WebSocketVo();
+                websocketVo.setSenderId("system");
+                websocketVo.setReceiverId("store_" + storeUser.getPhone());
+                websocketVo.setCategory("notice");
+                websocketVo.setNoticeType("1");
+                websocketVo.setIsRead(0);
+                websocketVo.setText(JSONObject.from(lifeMessage).toJSONString());
+                webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
+            }
+            return i;
+        } catch (Exception e) {
+            log.error("StoreCommentService.saveCommentOnlyStore ERROR Msg={}", e.getMessage());
+            return 1;
+        }
+    }
+
     /**
      * 转换商户订单为统一VO
      */

+ 9 - 1
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -154,6 +154,8 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     private final StoreImgService storeImgService;
 
+    private final StoreOfficialAlbumMapper storeOfficialAlbumMapper;
+
     /**
      * 运营活动数据访问对象
      */
@@ -1156,6 +1158,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
         storeInfoMapper.insert(storeInfo);
         result.setId(storeInfo.getId());
+        // 新建默认  环境 相册
+        StoreOfficialAlbum officialAlbum = new StoreOfficialAlbum();
+        officialAlbum.setStoreId(storeInfo.getId());
+        officialAlbum.setAlbumName("环境");
+        officialAlbum.setImgCount(0);
+        storeOfficialAlbumMapper.insert(officialAlbum);
         if (StringUtils.isNotEmpty(storeInfoDto.getStorePositionLongitude()) && StringUtils.isNotEmpty(storeInfoDto.getStorePositionLatitude())) {
             nearMeService.inGeolocation(new Point(Double.parseDouble(storeInfoDto.getStorePositionLongitude()), Double.parseDouble(storeInfoDto.getStorePositionLatitude())), storeInfo.getId().toString(), Boolean.TRUE);
         }
@@ -3075,7 +3083,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 return result;
             }
             int imgType = storeImgInfoVo.getImgType();
-            if (imgType == GroupConstant.IMG_TYPE_SINGLE_MODE || imgType == GroupConstant.IMG_TYPE_MULTI_MODE) {
+            if (imgType == GroupConstant.IMG_TYPE_SINGLE_MODE || imgType == GroupConstant.IMG_TYPE_MULTI_MODE||imgType==4) {
                 LambdaUpdateWrapper<StoreInfo> wrapper = new LambdaUpdateWrapper<>();
                 wrapper.eq(StoreInfo::getId, storeImgInfoVo.getStoreId());
                 wrapper.set(StoreInfo::getImgMode, storeImgInfoVo.getImgMode());

+ 2 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreOfficialAlbumServiceImpl.java

@@ -23,6 +23,7 @@ import shop.alien.mapper.StoreOfficialAlbumMapper;
 import shop.alien.mapper.StoreVideoMapper;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.store.service.StoreOfficialAlbumService;
+import shop.alien.store.service.StoreVideoService;
 import shop.alien.store.util.CommonConstant;
 import java.util.Collections;
 import java.util.List;
@@ -44,6 +45,7 @@ public class StoreOfficialAlbumServiceImpl extends ServiceImpl<StoreOfficialAlbu
 
     private final StoreImgService storeImgService;
 
+    private final StoreVideoService storeVideoService;
     /**
      * 创建官方相册
      *

+ 171 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffAuditAsyncService.java

@@ -0,0 +1,171 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.StoreStaffConfig;
+import shop.alien.mapper.StoreStaffConfigMapper;
+import shop.alien.store.util.ai.AiContentModerationUtil;
+import shop.alien.store.util.ai.AiVideoModerationUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 员工审核异步服务
+ * 用于异步处理员工内容的AI审核
+ *
+ * @author assistant
+ * @since 2025-01-07
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StoreStaffAuditAsyncService {
+
+    private final StoreStaffConfigMapper storeStaffConfigMapper;
+    private final AiContentModerationUtil aiContentModerationUtil;
+    private final AiVideoModerationUtil aiVideoModerationUtil;
+
+    /**
+     * 判断URL是否为视频
+     *
+     * @param url URL地址
+     * @return 是否为视频
+     */
+    private boolean isVideoUrl(String url) {
+        if (url == null || url.isEmpty()) {
+            return false;
+        }
+        String lowerUrl = url.toLowerCase();
+        return lowerUrl.endsWith(".mp4") ||
+               lowerUrl.endsWith(".avi") ||
+               lowerUrl.endsWith(".flv") ||
+               lowerUrl.endsWith(".mkv") ||
+               lowerUrl.endsWith(".rmvb") ||
+               lowerUrl.endsWith(".wmv") ||
+               lowerUrl.endsWith(".3gp") ||
+               lowerUrl.endsWith(".mov");
+    }
+
+    /**
+     * 异步审核员工内容
+     *
+     * @param staffId 员工ID
+     * @param storeStaffConfig 员工配置信息
+     */
+    @Async
+    public void auditStaffContentAsync(Integer staffId, StoreStaffConfig storeStaffConfig) {
+        try {
+            log.info("开始异步审核员工内容,staffId={}", staffId);
+
+            // 将状态置为"审核中"(0),清空拒绝原因
+            StoreStaffConfig auditingUpdate = new StoreStaffConfig();
+            auditingUpdate.setId(staffId);
+            auditingUpdate.setStatus("0");
+            auditingUpdate.setRejectionReason(null);
+            storeStaffConfigMapper.updateById(auditingUpdate);
+
+            // 组装 AI 审核文本和图片
+            StringBuilder textContent = new StringBuilder();
+            if (StringUtils.isNotEmpty(storeStaffConfig.getName())) {
+                textContent.append(storeStaffConfig.getName()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(storeStaffConfig.getStaffPosition())) {
+                textContent.append(storeStaffConfig.getStaffPosition()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(storeStaffConfig.getPersonalIntroduction())) {
+                textContent.append(storeStaffConfig.getPersonalIntroduction());
+            }
+            if (StringUtils.isNotEmpty(storeStaffConfig.getProficientProjects())) {
+                textContent.append(storeStaffConfig.getProficientProjects());
+            }
+
+            List<String> imageUrls = new ArrayList<>();
+            List<String> videoUrls = new ArrayList<>();
+
+            if (StringUtils.isNotEmpty(storeStaffConfig.getStaffImage())) {
+                imageUrls.add(storeStaffConfig.getStaffImage());
+            }
+            if (StringUtils.isNotEmpty(storeStaffConfig.getBackgroundUrl())) {
+                String[] urls = storeStaffConfig.getBackgroundUrl().split(",");
+                for (String url : urls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        String trimmedUrl = url.trim();
+                        // 判断是视频还是图片
+                        if (isVideoUrl(trimmedUrl)) {
+                            videoUrls.add(trimmedUrl);
+                        } else {
+                            imageUrls.add(trimmedUrl);
+                        }
+                    }
+                }
+            }
+
+            // 1. 审核文本和图片
+            AiContentModerationUtil.AuditResult textImageAuditResult = aiContentModerationUtil.auditContent(
+                    textContent.toString().trim(), imageUrls
+            );
+
+            // 2. 审核视频(如果有)
+            AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
+            if (!videoUrls.isEmpty()) {
+                log.info("开始审核视频,视频数量:{}", videoUrls.size());
+                videoAuditResult = aiVideoModerationUtil.auditVideos(videoUrls);
+            }
+
+            // 3. 综合审核结果:文本图片审核和视频审核都必须通过
+            boolean allPassed = (textImageAuditResult != null && textImageAuditResult.isPassed()) &&
+                                (videoAuditResult == null || videoAuditResult.isPassed());
+
+            // 根据 AI 审核结果更新状态
+            // 审核通过:状态保持为"1"(审核通过)
+            // 审核失败:状态设置为"2"(审核拒绝)
+            StoreStaffConfig auditUpdate = new StoreStaffConfig();
+            auditUpdate.setId(staffId);
+            if (allPassed) {
+                // AI审核通过,状态保持为"审核中"(1)
+                auditUpdate.setStatus("1");
+                auditUpdate.setRejectionReason(null);
+                log.info("人员AI审核通过,状态设置为审核通过:staffId={}", staffId);
+            } else {
+                // AI审核失败,状态设置为"审核拒绝"(2)
+                // 收集所有失败原因
+                List<String> failureReasons = new ArrayList<>();
+                if (textImageAuditResult != null && !textImageAuditResult.isPassed()) {
+                    if (StringUtils.isNotEmpty(textImageAuditResult.getFailureReason())) {
+                        failureReasons.add("图文审核:" + textImageAuditResult.getFailureReason());
+                    } else {
+                        failureReasons.add("图文审核未通过");
+                    }
+                }
+                if (videoAuditResult != null && !videoAuditResult.isPassed()) {
+                    // 业务要求:视频审核失败统一记录"视频内容违规"
+                    failureReasons.add("视频内容违规");
+                }
+
+                String reason = failureReasons.isEmpty() ? "审核未通过" : String.join("; ", failureReasons);
+                log.warn("人员AI审核失败,状态设置为审核拒绝:staffId={}, reason={}", staffId, reason);
+                auditUpdate.setStatus("2");
+                auditUpdate.setRejectionReason(reason);
+            }
+            storeStaffConfigMapper.updateById(auditUpdate);
+
+            log.info("异步审核员工内容完成,staffId={}", staffId);
+        } catch (Exception e) {
+            log.error("异步审核员工内容异常,staffId={},异常信息:{}", staffId, e.getMessage(), e);
+            // 审核失败时,设置为审核拒绝状态
+            try {
+                StoreStaffConfig auditUpdate = new StoreStaffConfig();
+                auditUpdate.setId(staffId);
+                auditUpdate.setStatus("2");
+                auditUpdate.setRejectionReason("审核系统异常,请稍后重试");
+                storeStaffConfigMapper.updateById(auditUpdate);
+            } catch (Exception updateException) {
+                log.error("更新审核失败状态异常,staffId={},异常信息:{}", staffId, updateException.getMessage(), updateException);
+            }
+        }
+    }
+}

+ 113 - 91
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java

@@ -96,6 +96,8 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
 
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
 
+    private final StoreStaffAuditAsyncService storeStaffAuditAsyncService;
+
     /**
      * 认证类型:认证
      */
@@ -190,94 +192,13 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             updateTitleStaffCount(storeStaffConfig.getTitleId());
         }
 
-        // 新增 / 修改成功后,先将状态置为"审核中"(0),清空拒绝原因
-        StoreStaffConfig auditingUpdate = new StoreStaffConfig();
-        auditingUpdate.setId(staffId);
-        auditingUpdate.setStatus("0");
-        auditingUpdate.setRejectionReason(null);
-        storeStaffConfigMapper.updateById(auditingUpdate);
-
-        // 组装 AI 审核文本和图片
-        StringBuilder textContent = new StringBuilder();
-        if (StringUtils.isNotEmpty(storeStaffConfig.getName())) {
-            textContent.append(storeStaffConfig.getName()).append(" ");
-        }
-        if (StringUtils.isNotEmpty(storeStaffConfig.getStaffPosition())) {
-            textContent.append(storeStaffConfig.getStaffPosition()).append(" ");
-        }
-        if (StringUtils.isNotEmpty(storeStaffConfig.getPersonalIntroduction())) {
-            textContent.append(storeStaffConfig.getPersonalIntroduction());
+        // 处理视频封面生成
+        if (affected > 0 && StringUtils.isNotEmpty(storeStaffConfig.getBackgroundUrl())) {
+            processVideoCovers(storeStaffConfig);
         }
 
-        List<String> imageUrls = new ArrayList<>();
-        List<String> videoUrls = new ArrayList<>();
-        
-        if (StringUtils.isNotEmpty(storeStaffConfig.getStaffImage())) {
-            imageUrls.add(storeStaffConfig.getStaffImage());
-        }
-        if (StringUtils.isNotEmpty(storeStaffConfig.getBackgroundUrl())) {
-            String[] urls = storeStaffConfig.getBackgroundUrl().split(",");
-            for (String url : urls) {
-                if (StringUtils.isNotEmpty(url.trim())) {
-                    String trimmedUrl = url.trim();
-                    // 判断是视频还是图片
-                    if (isVideoUrl(trimmedUrl)) {
-                        videoUrls.add(trimmedUrl);
-                    } else {
-                        imageUrls.add(trimmedUrl);
-                    }
-                }
-            }
-        }
-
-        // 1. 审核文本和图片
-        AiContentModerationUtil.AuditResult textImageAuditResult = aiContentModerationUtil.auditContent(
-                textContent.toString().trim(), imageUrls
-        );
-
-        // 2. 审核视频(如果有)
-        AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
-        if (!videoUrls.isEmpty()) {
-            log.info("开始审核视频,视频数量:{}", videoUrls.size());
-            videoAuditResult = aiVideoModerationUtil.auditVideos(videoUrls);
-        }
-
-        // 3. 综合审核结果:文本图片审核和视频审核都必须通过
-        boolean allPassed = (textImageAuditResult != null && textImageAuditResult.isPassed()) &&
-                            (videoAuditResult == null || videoAuditResult.isPassed());
-
-        // 根据 AI 审核结果更新状态
-        // 审核通过:状态保持为"1"(审核通过)
-        // 审核失败:状态设置为"2"(审核拒绝)
-        StoreStaffConfig auditUpdate = new StoreStaffConfig();
-        auditUpdate.setId(staffId);
-        if (allPassed) {
-            // AI审核通过,状态保持为"审核中"(1)
-            auditUpdate.setStatus("1");
-            auditUpdate.setRejectionReason(null);
-            log.info("人员AI审核通过,状态设置为审核通过:staffId={}", staffId);
-        } else {
-            // AI审核失败,状态设置为"审核拒绝"(2)
-            // 收集所有失败原因
-            List<String> failureReasons = new ArrayList<>();
-            if (textImageAuditResult != null && !textImageAuditResult.isPassed()) {
-                if (StringUtils.isNotEmpty(textImageAuditResult.getFailureReason())) {
-                    failureReasons.add("图文审核:" + textImageAuditResult.getFailureReason());
-                } else {
-                    failureReasons.add("图文审核未通过");
-                }
-            }
-            if (videoAuditResult != null && !videoAuditResult.isPassed()) {
-                // 业务要求:视频审核失败统一记录“视频内容违规”
-                failureReasons.add("视频内容违规");
-            }
-            
-            String reason = failureReasons.isEmpty() ? "审核未通过" : String.join("; ", failureReasons);
-            log.warn("人员AI审核失败,状态设置为审核拒绝:staffId={}, reason={}", staffId, reason);
-            auditUpdate.setStatus("2");
-            auditUpdate.setRejectionReason(reason);
-        }
-        storeStaffConfigMapper.updateById(auditUpdate);
+        // 异步调用AI审核
+        storeStaffAuditAsyncService.auditStaffContentAsync(staffId, storeStaffConfig);
 
         return affected;
     }
@@ -425,7 +346,7 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
         // 构建查询条件:查询指定店铺下未删除的员工
         LambdaQueryWrapper<StoreStaffConfig> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(StoreStaffConfig::getStoreId, storeId)
-                .eq(StoreStaffConfig::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
+                .eq(StoreStaffConfig::getDeleteFlag, 0)
                 // 只查询staff_position不为空的记录
                 .isNotNull(StoreStaffConfig::getStaffPosition)
                 .ne(StoreStaffConfig::getStaffPosition, "")
@@ -970,6 +891,24 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             // 判断是否喜欢(根据userId查询点赞记录)
             Boolean isLiked = checkIsLiked(id, userId);
 
+            // 查询店铺信息(店铺名称和店铺电话)
+            String storeName = null;
+            String storePhone = null;
+            if (staffConfig.getStoreId() != null && staffConfig.getStoreId() > 0) {
+                try {
+                    StoreInfo storeInfo = storeInfoMapper.selectById(staffConfig.getStoreId());
+                    if (storeInfo != null) {
+                        storeName = storeInfo.getStoreName();
+                        storePhone = storeInfo.getStoreTel();
+                    } else {
+                        log.warn("查询店铺信息失败,店铺不存在:storeId={}", staffConfig.getStoreId());
+                    }
+                } catch (Exception e) {
+                    log.warn("查询店铺信息异常,storeId={},异常信息:{}", staffConfig.getStoreId(), e.getMessage());
+                    // 店铺信息查询失败不影响整体返回,设置为null即可
+                }
+            }
+
             // 构建返回对象
             StoreStaffDetailWithPerformanceVo result =
                     new StoreStaffDetailWithPerformanceVo();
@@ -977,9 +916,11 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             result.setPerformanceScheduleList(performanceScheduleList);
             result.setHasPerformanceToday(hasPerformanceToday);
             result.setIsLiked(isLiked);
+            result.setStoreName(storeName);
+            result.setStorePhone(storePhone);
 
-            log.info("查询员工详情(包含演出列表)成功,id={},演出安排数量:{},今日是否有演出:{},是否喜欢:{}",
-                    id, performanceScheduleList != null ? performanceScheduleList.size() : 0, hasPerformanceToday, isLiked);
+            log.info("查询员工详情(包含演出列表)成功,id={},演出安排数量:{},今日是否有演出:{},是否喜欢:{},店铺名称:{},店铺电话:{}",
+                    id, performanceScheduleList != null ? performanceScheduleList.size() : 0, hasPerformanceToday, isLiked, storeName, storePhone);
             return result;
         } catch (Exception e) {
             log.error("查询员工详情(包含演出列表)异常,id={},异常信息:{}", id, e.getMessage(), e);
@@ -1614,8 +1555,12 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             List<StoreStaffConfig> allStaffList = storeStaffConfigMapper.selectBatchIds(allStaffIds);
 
             // 4. 构建员工ID到员工对象的映射
+            // 过滤条件:未删除、status为1(审核通过)、online_status为0(上线)
             Map<Integer, StoreStaffConfig> staffMap = allStaffList.stream()
-                    .filter(staff -> staff != null && CommonConstant.DELETE_FLAG_UNDELETE.equals(staff.getDeleteFlag()))
+                    .filter(staff -> staff != null 
+                            && CommonConstant.DELETE_FLAG_UNDELETE.equals(staff.getDeleteFlag())
+                            && "1".equals(staff.getStatus())
+                            && staff.getOnlineStatus() != null && staff.getOnlineStatus() == 0)
                     .collect(Collectors.toMap(StoreStaffConfig::getId, staff -> staff, (a, b) -> a));
 
             // 5. 批量查询今日有演出的员工ID集合(性能优化)
@@ -1684,7 +1629,7 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             // 查询好评(overall_rating>4.5, staff_user_id in staffIds)
             LambdaQueryWrapper<StoreStaffReview> queryWrapper = new LambdaQueryWrapper<>();
             queryWrapper.eq(StoreStaffReview::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
-                    .gt(StoreStaffReview::getOverallRating, new java.math.BigDecimal("4.5"))
+                    .ge(StoreStaffReview::getOverallRating, new java.math.BigDecimal("4.5"))
                     .in(StoreStaffReview::getStaffUserId, staffIds);
 
             List<StoreStaffReview> positiveReviews = storeStaffReviewMapper.selectList(queryWrapper);
@@ -1848,4 +1793,81 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             return false;
         }
     }
+
+    /**
+     * 处理视频封面URL生成
+     * 为background_url中的视频文件生成封面URL(通过改变后缀)并保存到video_head_url字段
+     *
+     * @param storeStaffConfig 员工配置信息
+     */
+    private void processVideoCovers(StoreStaffConfig storeStaffConfig) {
+        try {
+            log.info("开始处理视频封面URL生成,staffId={}", storeStaffConfig.getId());
+
+            String backgroundUrl = storeStaffConfig.getBackgroundUrl();
+            if (StringUtils.isEmpty(backgroundUrl)) {
+                return;
+            }
+
+            String[] urls = backgroundUrl.split(",");
+            List<String> videoCoverUrls = new ArrayList<>();
+
+            for (String url : urls) {
+                if (StringUtils.isNotEmpty(url.trim())) {
+                    String trimmedUrl = url.trim();
+                    if (isVideoUrl(trimmedUrl)) {
+                        // 为视频生成封面URL
+                        String coverUrl = generateVideoCover(trimmedUrl);
+                        if (StringUtils.isNotEmpty(coverUrl)) {
+                            videoCoverUrls.add(coverUrl);
+                            log.info("视频封面URL生成成功,视频URL={},封面URL={}", trimmedUrl, coverUrl);
+                        } else {
+                            log.warn("视频封面URL生成失败,视频URL={}", trimmedUrl);
+                        }
+                    }
+                }
+            }
+
+            // 更新video_head_url字段
+            if (!videoCoverUrls.isEmpty()) {
+                String videoHeadUrlStr = String.join(",", videoCoverUrls);
+                StoreStaffConfig updateCover = new StoreStaffConfig();
+                updateCover.setId(storeStaffConfig.getId());
+                updateCover.setVideoHeadUrl(videoHeadUrlStr);
+                storeStaffConfigMapper.updateById(updateCover);
+                log.info("更新视频封面URL成功,staffId={},videoHeadUrl={}", storeStaffConfig.getId(), videoHeadUrlStr);
+            }
+
+        } catch (Exception e) {
+            log.error("处理视频封面URL生成异常,staffId={},异常信息:{}", storeStaffConfig.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 为单个视频生成封面URL
+     * 直接通过改变视频URL的后缀来生成封面URL
+     *
+     * @param videoUrl 视频URL
+     * @return 封面图片URL
+     */
+    private String generateVideoCover(String videoUrl) {
+        if (StringUtils.isEmpty(videoUrl)) {
+            return null;
+        }
+
+        try {
+            // 找到最后一个点的位置
+            int lastDotIndex = videoUrl.lastIndexOf('.');
+            if (lastDotIndex == -1) {
+                // 如果没有扩展名,直接添加.jpg
+                return videoUrl + ".jpg";
+            }
+
+            // 替换扩展名为.jpg
+            return videoUrl.substring(0, lastDotIndex) + ".jpg";
+        } catch (Exception e) {
+            log.error("生成视频封面URL异常,videoUrl={},异常信息:{}", videoUrl, e.getMessage(), e);
+            return null;
+        }
+    }
 }

+ 4 - 1
alien-store/src/main/java/shop/alien/store/util/CommonConstant.java

@@ -98,7 +98,7 @@ public class CommonConstant {
     public static final String PERFORMANCE_FREQUENCY_WEEKLY = "2";
 
     /**
-     * 点赞类型:1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工
+     * 点赞类型:1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工,9-点赞店铺,11-评价,12-评论
      */
     public static final String LIKE_TYPE_COMMENT = "1";
     public static final String LIKE_TYPE_DYNAMICS = "2";
@@ -108,4 +108,7 @@ public class CommonConstant {
     public static final String LIKE_TYPE_SECOND_GOODS = "6";
     public static final String LIKE_TYPE_LAWYER_SCORE = "7";
     public static final String LIKE_TYPE_STAFF = "8";
+    public static final String LIKE_TYPE_STORE = "9";
+    public static final String RATING_LIKE = "11";
+    public static final String COMMENT_LIKE = "12";
 }

+ 20 - 7
alien-store/src/main/java/shop/alien/store/util/FileUploadUtil.java

@@ -61,7 +61,9 @@ public class FileUploadUtil {
             } else if (videoFileType.contains(fileNameAndType.get("type").toLowerCase())) {
                 prefix = "video/";
             }
-            return aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type"));
+            // 去除文件名中的逗号,避免URL拼接时被错误分割
+            String fileName = fileNameAndType.get("name").replaceAll(",", "");
+            return aliOSSUtil.uploadFile(multipartFile, prefix + fileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type"));
         } catch (Exception e) {
             log.error("FileUpload.uploadOneFile ERROR Msg={}", e.getMessage());
             throw new RuntimeException(e);
@@ -111,14 +113,17 @@ public class FileUploadUtil {
                     uploadDir += "/image";
                     prefix = "image/";
                     log.info("FileUpload.uploadMoreFile 获取到图片文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
-                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
+                    // 去除文件名中的逗号,避免URL拼接时被错误分割
+                    String imageFileName = fileNameAndType.get("name").replaceAll(",", "");
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + imageFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
                     ;
                 } else if (videoFileType.contains(fileNameAndType.get("type").toLowerCase())) {
                     uploadDir += "/video/";
                     prefix = "video/";
                     //上传视频文件
                     log.info("FileUpload.uploadMoreFile 获取到视频文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
-                    String videoFileName = fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6);
+                    // 去除文件名中的逗号,避免URL拼接时被错误分割
+                    String videoFileName = fileNameAndType.get("name").replaceAll(",", "") + RandomCreateUtil.getRandomNum(6);
                     String cacheVideoPath = copyFile(uploadDir, multipartFile);
                     filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + videoFileName + "." + fileNameAndType.get("type")));
                     //缓存视频截图使用
@@ -140,17 +145,23 @@ public class FileUploadUtil {
                     uploadDir += "/voice";
                     prefix = "voice/";
                     log.info("FileUpload.uploadMoreFile 获取到语音文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
-                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
+                    // 去除文件名中的逗号,避免URL拼接时被错误分割
+                    String voiceFileName = fileNameAndType.get("name").replaceAll(",", "");
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + voiceFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
                 } else if (privacyFileType.contains(fileNameAndType.get("type").toLowerCase())) {
                     uploadDir += "/privacy/";
                     prefix = "privacy/";
                     log.info("FileUpload.uploadMoreFile 获取到隐私文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
-                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
+                    // 去除文件名中的逗号,避免URL拼接时被错误分割
+                    String privacyFileName = fileNameAndType.get("name").replaceAll(",", "");
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + privacyFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
                 } else if (ohterFileType.contains(fileNameAndType.get("type").toLowerCase())) {
                     uploadDir += "/other/";
                     prefix = "other/";
                     log.info("FileUpload.uploadMoreFile 获取到其他文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
-                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
+                    // 去除文件名中的逗号,避免URL拼接时被错误分割
+                    String otherFileName = fileNameAndType.get("name").replaceAll(",", "");
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + otherFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
                 }
             }
             return filePathList;
@@ -186,7 +197,9 @@ public class FileUploadUtil {
             //获取文件
             MultipartFile multipartFile = multipartRequest.getFileMap().get(storeImgVo.getName());
             Map<String, String> fileInfo = FileUtil.getFileNameAndType(multipartFile);
-            String videoFileName = fileInfo.get("name") + RandomCreateUtil.getRandomNum(6);
+            // 去除文件名中的逗号,避免URL拼接时被错误分割
+            String cleanFileName = fileInfo.get("name").replaceAll(",", "");
+            String videoFileName = cleanFileName + RandomCreateUtil.getRandomNum(6);
             String prefix;
             String filePath = "";
             if (imageFileType.contains(fileInfo.get("type").toLowerCase())) {

+ 1 - 2
alien-store/src/main/java/shop/alien/store/util/ai/AiFeedbackAssignUtils.java

@@ -14,7 +14,6 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.store.LifeFeedback;
-import shop.alien.store.feign.AlienAIFeign;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -41,7 +40,7 @@ public class AiFeedbackAssignUtils {
     private String assignStaffUrl;
 
     // 语音识别接口地址(从配置中读取,如果没有配置则使用默认值)
-    @Value("${feign.alienAI.url}")
+    @Value("${feign.alienAI.url:}")
     private String aiServiceBaseUrl;
 
     /**登录的语音识别文件登录

+ 21 - 0
alien-util/src/main/java/shop/alien/util/common/constant/CommentSourceTypeEnum.java

@@ -0,0 +1,21 @@
+package shop.alien.util.common.constant;
+
+
+public enum CommentSourceTypeEnum {
+    STORE_COMMENT(1, "店铺评价");
+
+    private final Integer type;
+    private final String info;
+
+    CommentSourceTypeEnum(Integer type, String info) {
+        this.type = type;
+        this.info = info;
+    }
+    public Integer getType() {
+        return type;
+    }
+
+    public String getInfo() {
+        return info;
+    }
+}

+ 21 - 0
alien-util/src/main/java/shop/alien/util/common/constant/RatingBusinessTypeEnum.java

@@ -0,0 +1,21 @@
+package shop.alien.util.common.constant;
+
+public enum RatingBusinessTypeEnum {
+    STORE_RATING(1, "门店评价")
+    ;
+    private final int businessType;
+    private final String info;
+
+    RatingBusinessTypeEnum(int businessType, String info) {
+        this.businessType = businessType;
+        this.info = info;
+    }
+
+    public int getBusinessType() {
+        return businessType;
+    }
+
+    public String getInfo() {
+        return info;
+    }
+}

+ 12 - 0
alien-util/src/main/java/shop/alien/util/encryption/Decrypt.java

@@ -0,0 +1,12 @@
+package shop.alien.util.encryption;
+
+import java.lang.annotation.*;
+
+/**
+ * 解密注解,用于控制请求体是否解密
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Decrypt {
+}

+ 12 - 0
alien-util/src/main/java/shop/alien/util/encryption/Encrypt.java

@@ -0,0 +1,12 @@
+package shop.alien.util.encryption;
+
+import java.lang.annotation.*;
+
+/**
+ * 加密注解,用于控制响应体是否加密
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Encrypt {
+}

+ 11 - 0
alien-util/src/main/java/shop/alien/util/encryption/EncryptTest.java

@@ -0,0 +1,11 @@
+package shop.alien.util.encryption;
+
+public class EncryptTest {
+    public static void main(String[] args) {
+        String data = "{\"name\": \"测试商品\", \"price\": 99.9}";
+        String key = "alien67890982316";
+        String iv = "1234560405060708";
+        String encrypted = StandardAesUtil.encrypt(data, key, iv);
+        System.out.println("ENCRYPTED_RESULT:" + encrypted);
+    }
+}

+ 52 - 0
alien-util/src/main/java/shop/alien/util/encryption/StandardAesUtil.java

@@ -0,0 +1,52 @@
+package shop.alien.util.encryption;
+
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 标准 AES 加解密工具类
+ * 模式:AES/CBC/PKCS5Padding
+ * 编码:UTF-8
+ */
+public class StandardAesUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(StandardAesUtil.class);
+    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
+
+    public static String encrypt(String data, String key, String iv) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
+            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+            
+            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
+            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
+            return Base64.encodeBase64String(encrypted);
+        } catch (Exception e) {
+            log.error("AES加密失败: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    public static String decrypt(String base64Data, String key, String iv) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
+            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+            
+            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+            byte[] decoded = Base64.decodeBase64(base64Data);
+            byte[] original = cipher.doFinal(decoded);
+            return new String(original, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            log.error("AES解密失败: {}", e.getMessage());
+            return null;
+        }
+    }
+}

+ 32 - 8
alien-util/src/main/java/shop/alien/util/file/FileUtil.java

@@ -489,10 +489,22 @@ public class FileUtil {
     public static Map<String, String> getFileNameAndType(MultipartFile multipartFile) {
         String originalFilename = multipartFile.getOriginalFilename();
         Map<String, String> map = new HashMap<>();
-        String fileName = originalFilename.substring(0, originalFilename.lastIndexOf('.'));
-        String fileType = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
-        map.put("name", fileName);
-        map.put("type", fileType);
+        if (originalFilename == null || originalFilename.isEmpty()) {
+            map.put("name", "");
+            map.put("type", "");
+            return map;
+        }
+        int lastDotIndex = originalFilename.lastIndexOf('.');
+        if (lastDotIndex == -1 || lastDotIndex == originalFilename.length() - 1) {
+            // 没有扩展名或点号在最后
+            map.put("name", originalFilename);
+            map.put("type", "");
+        } else {
+            String fileName = originalFilename.substring(0, lastDotIndex);
+            String fileType = originalFilename.substring(lastDotIndex + 1);
+            map.put("name", fileName);
+            map.put("type", fileType);
+        }
         return map;
     }
 
@@ -505,10 +517,22 @@ public class FileUtil {
     public static Map<String, String> getFileNameAndType(File file) {
         String originalFilename = file.getName();
         Map<String, String> map = new HashMap<>();
-        String fileName = originalFilename.substring(0, originalFilename.lastIndexOf('.'));
-        String fileType = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
-        map.put("name", fileName);
-        map.put("type", fileType);
+        if (originalFilename == null || originalFilename.isEmpty()) {
+            map.put("name", "");
+            map.put("type", "");
+            return map;
+        }
+        int lastDotIndex = originalFilename.lastIndexOf('.');
+        if (lastDotIndex == -1 || lastDotIndex == originalFilename.length() - 1) {
+            // 没有扩展名或点号在最后
+            map.put("name", originalFilename);
+            map.put("type", "");
+        } else {
+            String fileName = originalFilename.substring(0, lastDotIndex);
+            String fileType = originalFilename.substring(lastDotIndex + 1);
+            map.put("name", fileName);
+            map.put("type", fileType);
+        }
         return map;
     }
 }