13
0

81 Revīzijas aacfb98076 ... db51787fc9

Autors SHA1 Ziņojums Datums
  liyafei db51787fc9 添加redisson依赖 1 dienu atpakaļ
  liyafei 586aa081f6 运营管理-新增AI审核 1 dienu atpakaļ
  qrs fa926eeab6 feat(config): 引入配置刷新与动态URL配置支持 1 dienu atpakaļ
  ldz 5b1c18d352 下掉休闲娱乐 健身的娱乐许可证校验 1 dienu atpakaļ
  lutong 8fdb7ed9d0 暂时关闭身份证检验 3 dienas atpakaļ
  ldz e7a858fdce 修改bug 3 dienas atpakaļ
  panzhilin 792b163c59 Merge remote-tracking branch 'origin/sit' into sit 4 dienas atpakaļ
  panzhilin 6f56dad31e Merge remote-tracking branch 'origin/sit-shenzhen' into sit-shenzhen 4 dienas atpakaļ
  panzhilin e7e28eb8f3 Ai语音识别(文件上传)解决URL硬编码 4 dienas atpakaļ
  liyafei 48d688738f Merge branch 'sit-shenzhen' into sit 4 dienas atpakaļ
  liyafei e4fad2d38a Merge remote-tracking branch 'origin/sit' into sit 4 dienas atpakaļ
  liyafei 385c5628a5 运营管理-优惠价服务 4 dienas atpakaļ
  panzhilin dc8dd52253 Ai语音识别(文件上传) 4 dienas atpakaļ
  penghao 64a0e8c7df feat:新增身份证图片存入及到期时间存入 4 dienas atpakaļ
  qrs 32b6edee1d feat(store): 更新差评申诉状态处理逻辑 5 dienas atpakaļ
  liyafei 19737a90b4 Merge remote-tracking branch 'origin/sit-shenzhen' into sit 5 dienas atpakaļ
  penghao a1bc6c866e Merge remote-tracking branch 'origin/sit-shenzhen' into sit-shenzhen 5 dienas atpakaļ
  penghao 03e7d78955 fix:修改中台意见反馈绑定userid 5 dienas atpakaļ
  liyafei 0b15cf163d 运营管理模块,添加通过用户输入文字描述,调用AI生成营销海报 5 dienas atpakaļ
  penghao 39501dfd34 feat:中台意见反馈回复及通知 5 dienas atpakaļ
  lyx 08cd1924b6 需求:店铺列表只展示打卡记录有图片且设为可见的 5 dienas atpakaļ
  qrs 793f71d9da fix(controller): 更新AI审核字段并优化优惠券规则查询接口 5 dienas atpakaļ
  penghao c62eda9643 feat:中台意见反馈相关接口开发 6 dienas atpakaļ
  panzhilin 7179713e10 意见反馈模块功能开发(用商家端)1.0 6 dienas atpakaļ
  panzhilin 79d6265271 意见反馈模块功能开发(未完成)1.2 1 nedēļu atpakaļ
  panzhilin 22e2cbed3d 意见反馈模块功能开发(未完成) 1 nedēļu atpakaļ
  qrs e40cecd515 feat(ai): 新增AI文本审核功能 6 dienas atpakaļ
  zjy a29e02d930 feat(job): 添加违规处理时间字段并优化导入包 6 dienas atpakaļ
  lyx 7fd4210b89 add:随机查询一个 1 nedēļu atpakaļ
  penghao a6ab8c9809 feat:添加入驻营业执照相关信息存入 1 nedēļu atpakaļ
  zjy c4eabadc1b fix(job): 修复AI任务ID为空时的处理逻辑 1 nedēļu atpakaļ
  zjy f4bcf27d22 fix(job): 优化AI审核任务状态判断逻辑 1 nedēļu atpakaļ
  zjy 3b351395c1 fix(job): 修复消息内容设置错误问题 1 nedēļu atpakaļ
  zjy f6e3cea511 fix(job): 修复消息内容设置错误问题 1 nedēļu atpakaļ
  zjy 031a7250ba feat(job): 优化举报结果通知消息结构 1 nedēļu atpakaļ
  zjy 1c7ca76e74 feat(job): 优化AI自动审核任务处理逻辑 1 nedēļu atpakaļ
  lutong d450ba813f test 1 nedēļu atpakaļ
  wxd cfde5b3a61 Merge branch 'sit-aiAudit' of alien/alien_cloud into sit 1 nedēļu atpakaļ
  wxd 2a42480fac fix(video): correct task status update on moderation failure 1 nedēļu atpakaļ
  lyx 95ae3bd7d5 bugfix:上传文件,支持xml,隐私协议 1 nedēļu atpakaļ
  qrs b3ca457125 二手用户举报ai接口完善 1 nedēļu atpakaļ
  zjy 528e35062e feat(job): 引入第三方接口配置并替换硬编码URL 1 nedēļu atpakaļ
  zjy 99c227f5d9 feat(job): 实现AI自动审核任务功能 1 nedēļu atpakaļ
  zhangchen 1dd4b46e07 上传文件,新增支持语音功能 1 nedēļu atpakaļ
  wxd 522a858e22 Merge branch 'sit-aiAudit' of alien/alien_cloud into sit 1 nedēļu atpakaļ
  wxd 3c9b2754ea feat(second-goods): implement video moderation result processing and approval handling 1 nedēļu atpakaļ
  wxd f914bd83f0 Merge branch 'sit-aiAudit' of alien/alien_cloud into sit 1 nedēļu atpakaļ
  wxd 97a9af171a feat(second): implement AI goods audit result processing 1 nedēļu atpakaļ
  panzhilin 1c3c5ae084 BUG:修改类型字段数值不一致问题 1 nedēļu atpakaļ
  Lhaibo ace57488e5 feat(job): 实现AI内容审核任务及结果处理功能 1 nedēļu atpakaļ
  penghao 9d38c0cb3f 门店入驻添加门店过期时间 1 nedēļu atpakaļ
  panzhilin 539bfefb11 酒单优化餐食和酒水 1 nedēļu atpakaļ
  qrs d4112a83c2 商家pc端-酒水管理 1 nedēļu atpakaļ
  zjy df89061f42 feat(violation): 移除AI审核任务创建逻辑 1 nedēļu atpakaļ
  ldz fb8c8e065d 八大类放开校验,增加模糊查询,注释合并一个接口 1 nedēļu atpakaļ
  panzhilin 50025348a7 Merge remote-tracking branch 'origin/sit' into sit 1 nedēļu atpakaļ
  panzhilin 34fa822d75 酒水餐食管理curd(删除原来的酒水表现在是基于store_menu的表,沿用原接口改原来代码的逻辑) 1 nedēļu atpakaļ
  penghao dc927befdf feat:新增运动设施保存图片接口 1 nedēļu atpakaļ
  lyx 8cc5b4b504 bugfix:运营活动表修改,直接自关联三级结构 1 nedēļu atpakaļ
  jyc fd8f2cb98c feat(coupon): 添加优惠券ID字段以支持查询 1 nedēļu atpakaļ
  ldz 0358efdcfe 修改用户端的营销活动部分逻辑,只有已审核通过的才显示的问题 1 nedēļu atpakaļ
  fcw d3bb673411 fix(second-goods): 调整商品审核流程逻辑 1 nedēļu atpakaļ
  fcw 2b5ea01d6b Merge remote-tracking branch 'origin/sit' into sit 1 nedēļu atpakaļ
  ldz 04c8c0a981 临时协助测试提出的问题,帮忙解决代金券列表的问题 1 nedēļu atpakaļ
  lyx 44c11deffa Merge remote-tracking branch 'origin/sit' into sit 1 nedēļu atpakaļ
  lyx 536c9f1e2b bugfix:首页增加字段展示 1 nedēļu atpakaļ
  qrs 3a8b46ba85 商家pc端-酒水管理 1 nedēļu atpakaļ
  fcw 91c10010ca Merge remote-tracking branch 'origin/sit' into sit 1 nedēļu atpakaļ
  penghao 6b7fefc47b 合并解决 1 nedēļu atpakaļ
  penghao 87cd39686d Merge remote-tracking branch 'origin/sit' into sit 1 nedēļu atpakaļ
  penghao 09a6f8b516 优化入驻保存-许可证存入相应信息 1 nedēļu atpakaļ
  zhangchen 5853bfe85d 解决退款空指针 1 nedēļu atpakaļ
  zhangchen eb2123b05f 解决退款空指针 1 nedēļu atpakaļ
  panzhilin c298bd419e 酒水餐食管理curd 1 nedēļu atpakaļ
  zhangchen a76717de87 审批状态去掉 1 nedēļu atpakaļ
  ldz 5d33e9151a 协助修改用户端bug,特色美食部分显示其他店铺问题 1 nedēļu atpakaļ
  zhangchen bb8a6386d0 通知标题修改 1 nedēļu atpakaļ
  LuTong b76b322f69 开发 根据活动ID获取详情 1 nedēļu atpakaļ
  fcw 77c5b11339 feat(store): 新增洗浴设施及服务的商户端查询功能 1 nedēļu atpakaļ
  fcw 6dee6d7b67 Merge remote-tracking branch 'origin/sit' into sit 1 nedēļu atpakaļ
  fcw 28d3935780 feat(sms): 发送短信时永久存储验证码 1 nedēļu atpakaļ
100 mainītis faili ar 5616 papildinājumiem un 467 dzēšanām
  1. 81 0
      alien-entity/src/main/java/shop/alien/entity/store/DrinkInfo.java
  2. 74 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeFeedback.java
  3. 49 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeFeedbackReply.java
  4. 66 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeImg.java
  5. 18 3
      alien-entity/src/main/java/shop/alien/entity/store/LifeLog.java
  6. 0 4
      alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java
  7. 1 1
      alien-entity/src/main/java/shop/alien/entity/store/StoreCommentAppeal.java
  8. 1 1
      alien-entity/src/main/java/shop/alien/entity/store/StoreImg.java
  9. 49 4
      alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
  10. 16 12
      alien-entity/src/main/java/shop/alien/entity/store/UserActionRewardRule.java
  11. 26 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/FeedbackReplyDto.java
  12. 26 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackAssignDto.java
  13. 39 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackDto.java
  14. 35 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackQueryDto.java
  15. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackReplyWebDto.java
  16. 26 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackStatusDto.java
  17. 12 3
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java
  18. 33 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/UserReplyDto.java
  19. 82 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/DrinkInfoVo.java
  20. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackAttachmentVo.java
  21. 41 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackLogVo.java
  22. 45 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackReplyVo.java
  23. 23 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackTypeVo.java
  24. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeDiscountCouponFriendRuleVo.java
  25. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeDiscountCouponVo.java
  26. 78 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeFeedbackDetailVo.java
  27. 62 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeFeedbackListVo.java
  28. 66 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeFeedbackVo.java
  29. 26 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LifeStaffListVo.java
  30. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategoryVo.java
  31. 1 1
      alien-entity/src/main/java/shop/alien/entity/storePlatform/StoreOperationalActivity.java
  32. 9 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityDTO.java
  33. 2 0
      alien-entity/src/main/java/shop/alien/mapper/LifeDiscountCouponStoreFriendMapper.java
  34. 80 0
      alien-entity/src/main/java/shop/alien/mapper/LifeFeedbackMapper.java
  35. 23 0
      alien-entity/src/main/java/shop/alien/mapper/LifeFeedbackReplyMapper.java
  36. 51 0
      alien-entity/src/main/java/shop/alien/mapper/LifeImgMapper.java
  37. 24 0
      alien-entity/src/main/java/shop/alien/mapper/LifeLogMapper.java
  38. 2 2
      alien-entity/src/main/java/shop/alien/mapper/StoreCommentAppealMapper.java
  39. 207 0
      alien-entity/src/main/resources/mapper/LifeFeedbackMapper.xml
  40. 32 0
      alien-entity/src/main/resources/mapper/LifeFeedbackReplyMapper.xml
  41. 90 0
      alien-entity/src/main/resources/mapper/LifeImgMapper.xml
  42. 14 0
      alien-job/src/main/java/shop/alien/job/feign/SecondGoodsFeign.java
  43. 5 41
      alien-job/src/main/java/shop/alien/job/jobhandler/VideoModerationJobHandler.java
  44. 178 21
      alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java
  45. 31 5
      alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java
  46. 13 167
      alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java
  47. 25 9
      alien-job/src/main/java/shop/alien/job/store/BadReviewAppealJob.java
  48. 1 0
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java
  49. 1 1
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java
  50. 2 1
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderExpirationServiceImpl.java
  51. 16 0
      alien-lawyer/src/main/java/shop/alien/lawyer/util/AliSms.java
  52. 40 0
      alien-second/src/main/java/shop/alien/second/controller/SecondGoodsController.java
  53. 13 1
      alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java
  54. 6 0
      alien-second/src/main/java/shop/alien/second/service/SecondGoodsService.java
  55. 6 0
      alien-second/src/main/java/shop/alien/second/service/VideoModerationService.java
  56. 224 1
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java
  57. 18 0
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java
  58. 79 41
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java
  59. 72 1
      alien-second/src/main/java/shop/alien/second/service/impl/VideoModerationServiceImpl.java
  60. 9 2
      alien-second/src/main/java/shop/alien/second/util/AiTaskUtils.java
  61. 272 0
      alien-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java
  62. 57 0
      alien-second/src/main/java/shop/alien/second/util/JsonUtils.java
  63. 5 0
      alien-store-platform/pom.xml
  64. 14 2
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/OperationalActivityController.java
  65. 59 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/feign/AlienAIFeign.java
  66. 167 33
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java
  67. 12 10
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java
  68. 2 2
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformLoginServiceImpl.java
  69. 6 6
      alien-store/src/main/java/shop/alien/store/config/WebSocketProcess.java
  70. 189 0
      alien-store/src/main/java/shop/alien/store/controller/AiAuditController.java
  71. 63 0
      alien-store/src/main/java/shop/alien/store/controller/AiUploadController.java
  72. 3 3
      alien-store/src/main/java/shop/alien/store/controller/AliController.java
  73. 46 0
      alien-store/src/main/java/shop/alien/store/controller/BathFacilityServiceController.java
  74. 191 0
      alien-store/src/main/java/shop/alien/store/controller/DrinkInfoController.java
  75. 3 3
      alien-store/src/main/java/shop/alien/store/controller/LifeDiscountCouponStoreFriendController.java
  76. 117 0
      alien-store/src/main/java/shop/alien/store/controller/LifeFeedbackController.java
  77. 99 0
      alien-store/src/main/java/shop/alien/store/controller/OperationalActivityController.java
  78. 18 0
      alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java
  79. 18 9
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  80. 35 0
      alien-store/src/main/java/shop/alien/store/feign/AlienAIFeign.java
  81. 6 0
      alien-store/src/main/java/shop/alien/store/service/BathFacilityServiceService.java
  82. 116 0
      alien-store/src/main/java/shop/alien/store/service/DrinkInfoService.java
  83. 1 1
      alien-store/src/main/java/shop/alien/store/service/LifeDiscountCouponStoreFriendService.java
  84. 20 0
      alien-store/src/main/java/shop/alien/store/service/LifeFeedbackReplyService.java
  85. 78 0
      alien-store/src/main/java/shop/alien/store/service/LifeFeedbackService.java
  86. 47 0
      alien-store/src/main/java/shop/alien/store/service/LifeImgService.java
  87. 6 0
      alien-store/src/main/java/shop/alien/store/service/LifeUserStoreService.java
  88. 44 0
      alien-store/src/main/java/shop/alien/store/service/OperationalActivityService.java
  89. 11 0
      alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java
  90. 84 59
      alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
  91. 75 0
      alien-store/src/main/java/shop/alien/store/service/impl/BathFacilityServiceServiceImpl.java
  92. 367 0
      alien-store/src/main/java/shop/alien/store/service/impl/DrinkInfoServiceImpl.java
  93. 7 7
      alien-store/src/main/java/shop/alien/store/service/impl/LifeBlacklistServiceImpl.java
  94. 1 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java
  95. 7 1
      alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponStoreFriendServiceImpl.java
  96. 28 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackReplyServiceImpl.java
  97. 705 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackServiceImpl.java
  98. 56 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeImgServiceImpl.java
  99. 9 9
      alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java
  100. 256 0
      alien-store/src/main/java/shop/alien/store/service/impl/OperationalActivityServiceImpl.java

+ 81 - 0
alien-entity/src/main/java/shop/alien/entity/store/DrinkInfo.java

@@ -0,0 +1,81 @@
+package shop.alien.entity.store;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 酒水餐食信息 DTO
+ * 用于接口参数传输,实际数据存储在 store_menu 表
+ * type: 1=餐食,2=酒水
+ *
+ * @author ssk
+ * @since 2025-05-14
+ */
+@Data
+@ApiModel(value = "DrinkInfo", description = "酒水餐食信息DTO")
+public class DrinkInfo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "类型(1:餐食,2:酒水)")
+    private Integer type;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    @ApiModelProperty(value = "售价")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "成本价")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "酒精度(%vol)")
+    private BigDecimal alcoholVolume;
+
+    @ApiModelProperty(value = "图片URL")
+    private String picUrl;
+
+    @ApiModelProperty(value = "分类(如:白酒、啤酒、小吃等)")
+    private String category;
+
+    @ApiModelProperty(value = "风味特征")
+    private String flavor;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "排序(数值越小越靠前)")
+    private Integer sort;
+
+    @ApiModelProperty(value = "是否推荐(0:否,1:是)")
+    private Integer isRecommended;
+
+    @ApiModelProperty(value = "状态(1:上架,2:下架)")
+    private Integer status;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updateUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}

+ 74 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeFeedback.java

@@ -0,0 +1,74 @@
+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;
+
+/**
+ * 意见反馈表
+ */
+@Data
+@JsonInclude
+@TableName("life_feedback")
+@ApiModel(value = "LifeFeedback对象", description = "意见反馈")
+public class LifeFeedback implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "用户ID")
+    @TableField("user_id")
+    private Integer userId;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
+    @TableField("feedback_source")
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "反馈方式:0-用户反馈,1-AI识别")
+    @TableField("feedback_way")
+    private Integer feedbackWay;
+
+    @ApiModelProperty(value = "反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    @TableField("feedback_type")
+    private Integer feedbackType;
+
+    @ApiModelProperty(value = "反馈内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty(value = "联系方式(手机号或邮箱)")
+    @TableField("contact_way")
+    private String contactWay;
+
+    @ApiModelProperty(value = "反馈时间")
+    @TableField("feedback_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date feedbackTime;
+
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决")
+    @TableField("handle_status")
+    private Integer handleStatus;
+
+    @ApiModelProperty(value = "跟进人员ID(关联life_sys表的id)")
+    @TableField("staff_id")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "update_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}
+

+ 49 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeFeedbackReply.java

@@ -0,0 +1,49 @@
+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;
+
+/**
+ * 反馈回复表
+ */
+@Data
+@JsonInclude
+@TableName("life_feedback_reply")
+@ApiModel(value = "LifeFeedbackReply对象", description = "反馈回复")
+public class LifeFeedbackReply implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "回复ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "关联的反馈ID")
+    @TableField("feedback_id")
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "回复类型:0-平台回复,1-用户回复")
+    @TableField("reply_type")
+    private Integer replyType;
+
+    @ApiModelProperty(value = "回复内容")
+    @TableField("reply_content")
+    private String replyContent;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "update_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}
+

+ 66 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeImg.java

@@ -0,0 +1,66 @@
+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;
+
+/**
+ * 反馈附件表(图片和视频)
+ */
+@Data
+@JsonInclude
+@TableName("life_img")
+@ApiModel(value = "LifeImg对象", description = "反馈附件(图片和视频)")
+public class LifeImg implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "反馈ID(关联life_feedback)")
+    @TableField("feedback_id")
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "文件URL(图片或视频)")
+    @TableField("img_url")
+    private String imgUrl;
+
+    @ApiModelProperty(value = "缩略图URL(视频的封面图)")
+    @TableField("thumbnail_url")
+    private String thumbnailUrl;
+
+    @ApiModelProperty(value = "文件类型:1-图片,2-视频")
+    @TableField("file_type")
+    private Integer fileType;
+
+    @ApiModelProperty(value = "上传时间")
+    @TableField("upload_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date uploadTime;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "update_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

+ 18 - 3
alien-entity/src/main/java/shop/alien/entity/store/LifeLog.java

@@ -3,24 +3,39 @@ 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;
 
 /**
- * 日志
+ * 意见反馈日志
  */
 @Data
 @JsonInclude
 @TableName("life_log")
-public class LifeLog {
+@ApiModel(value = "LifeLog对象", description = "意见反馈日志")
+public class LifeLog implements Serializable {
+    private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "主键ID")
     @TableId(value = "id", type = IdType.AUTO)
-    private String id;
+    private Integer id;
 
+    @ApiModelProperty(value = "意见反馈主表ID")
+    @TableField("feedback_id")
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "日志内容")
+    @TableField("context")
     private String context;
 
+    @ApiModelProperty(value = "操作类型:0-创建反馈工单,1-分配跟踪人员,2-回复用户,3-问题解决状态")
+    @TableField("type")
+    private String type;
+
     @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
     @TableField("delete_flag")
     @TableLogic

+ 0 - 4
alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java

@@ -78,9 +78,5 @@ public class SportsEquipmentFacility implements Serializable {
     @ApiModelProperty(value = "修改人ID")
     @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
     private Integer updatedUserId;
-
-    @ApiModelProperty(value = "收费类型0-免费,1-收费")
-    @TableField("billing_type")
-    private Integer billingType;
 }
 

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

@@ -43,7 +43,7 @@ public class StoreCommentAppeal extends Model<StoreCommentAppeal> {
     @TableField("img_id")
     private String imgId;
 
-    @ApiModelProperty(value = "申诉状态: 0:待处理, 1:已驳回, 2:已同意")
+    @ApiModelProperty(value = "申诉状态: 0:待处理, 1:已驳回, 2:已同意, 3:处理中")
     @TableField("appeal_status")
     private Integer appealStatus;
 

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

@@ -30,7 +30,7 @@ public class StoreImg extends Model<StoreImg> {
     @TableField("store_id")
     private Integer storeId;
 
-    @ApiModelProperty(value = "图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉,10:商家头像,11:店铺轮播图,12:联名卡图片,13:动态折扣, 14:套餐图片,15:合同照片,17:打卡广场小人图片 18: 商品发布图片 19:二手商品与用户举报图片,20头图单图模式,21头图多图模式 , 22续签合同,23,二手商品记录图片类型, 24 食品经营许可证审核前状态 25.食品经营许可证审核后状态, 28:运动器材设施图片, 29:洗浴设施及服务图片 30 酒水")
+    @ApiModelProperty(value = "图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉,10:商家头像,11:店铺轮播图,12:联名卡图片,13:动态折扣, 14:营业执照,15:合同照片,17:打卡广场小人图片 18: 二手商品发布图片 19:二手商品与用户举报图片,20头图单图模式,21头图多图模式 , 22续签合同, 23 二手商品记录图片类型, 24 食品经营许可证审核前类型 25.食品经营许可证审核后类型 26.运营活动活动标题图 27.运营活动活动详情图 28.运动设施 29.洗浴设施及服务 30.酒水 31.娱乐经营许可证 32.娱乐经营许可证审核前, 33:身份证正面, 34:身份证反面")
     @TableField("img_type")
     private Integer imgType;
 

+ 49 - 4
alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java

@@ -86,6 +86,7 @@ public class StoreInfo {
 
     @ApiModelProperty(value = "到期时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("expiration_time")
     private Date expirationTime;
 
     @ApiModelProperty(value = "门店坐标")
@@ -220,12 +221,12 @@ public class StoreInfo {
     @TableField("food_licence_reason")
     private String foodLicenceReason;
 
-    @ApiModelProperty(value = "经营许可证到期时间")
+    @ApiModelProperty(value = "食品经营许可证到期时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @TableField("food_licence_expiration_time")
     private Date  foodLicenceExpirationTime;
 
-    @ApiModelProperty(value = "变更经营许可证提交时间")
+    @ApiModelProperty(value = "变更食品经营许可证提交时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @TableField("update_food_licence_time")
     private Date  updateFoodLicenceTime;
@@ -243,8 +244,6 @@ public class StoreInfo {
     @TableField("business_classify_name")
     private String businessClassifyName;
 
-
-
     @ApiModelProperty(value = "是否提供餐食")
     @TableField("meals_flag")
     private Integer  mealsFlag;
@@ -266,4 +265,50 @@ public class StoreInfo {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @TableField("update_entertainment_licence_time")
     private Date updateEntertainmentLicenceTime;
+
+    @ApiModelProperty(value = "续签合同到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("renew_contract_expiration_time")
+    private Date renewContractExpirationTime;
+
+    @ApiModelProperty(value = "营业执照状态 字典 foodLicenceStatus")
+    @TableField("business_license_status")
+    private Integer businessLicenseStatus;
+
+    @ApiModelProperty(value = "营业执照失败原因")
+    @TableField("business_license_reason")
+    private String businessLicenseReason;
+
+    @ApiModelProperty(value = "营业执照到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("business_license_expiration_time")
+    private Date businessLicenseExpirationTime;
+
+    @ApiModelProperty(value = "变更营业执照提交时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("update_business_license_time")
+    private Date updateBusinessLicenseTime;
+
+    @ApiModelProperty(value = "身份证状态 字典 foodLicenceStatus")
+    @TableField("id_card_status")
+    private Integer idCardStatus;
+
+    @ApiModelProperty(value = "身份证审核失败原因")
+    @TableField("id_card_reason")
+    private String idCardReason;
+
+    @ApiModelProperty(value = "身份证到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("id_card_expiration_time")
+    private Date idCardExpirationTime;
+
+    @ApiModelProperty(value = "变更身份证提交时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("update_id_card_time")
+    private Date updateIdCardTime;
+
+    @ApiModelProperty(value = "审核时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("review_date")
+    private Date reviewDate;
 }

+ 16 - 12
alien-entity/src/main/java/shop/alien/entity/store/UserActionRewardRule.java

@@ -18,30 +18,34 @@ import java.util.Date;
 @Data
 @JsonInclude
 @TableName("user_action_reward_rule")
-@ApiModel(value = "UserActionRewardRule", description = "用户行为奖励规则配置表")
+@ApiModel(value = "UserActionRewardRule", description = "用户行为奖励规则配置表(3级树形结构)")
 public class UserActionRewardRule {
 
-    @ApiModelProperty(value = "主键")
+    @ApiModelProperty(value = "主键ID")
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
-    @ApiModelProperty(value = "角色(如:当用户)")
-    @TableField("role")
-    private String role;
+    @ApiModelProperty(value = "父节点ID,0表示根节点(第一级:角色)")
+    @TableField("parent_id")
+    private Integer parentId;
 
-    @ApiModelProperty(value = "行为(如:核销并评论、核销、打卡)")
-    @TableField("action")
-    private String action;
+    @ApiModelProperty(value = "层级:1-角色,2-行为,3-奖励")
+    @TableField("level")
+    private Integer level;
 
-    @ApiModelProperty(value = "奖励(如:优惠券、红包)")
-    @TableField("reward")
-    private String reward;
+    @ApiModelProperty(value = "名称:根据level区分,1-角色名,2-行为名,3-奖励名")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "奖励数量:如1优惠券、2红包(仅第三级使用)")
+    @TableField("reward_amount")
+    private Integer rewardAmount;
 
     @ApiModelProperty(value = "状态:0-禁用,1-启用")
     @TableField("status")
     private Integer status;
 
-    @ApiModelProperty(value = "排序")
+    @ApiModelProperty(value = "排序:数值越大越靠前")
     @TableField("sort_order")
     private Integer sortOrder;
 

+ 26 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/FeedbackReplyDto.java

@@ -0,0 +1,26 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 平台回复反馈DTO
+ */
+@Data
+@ApiModel(value = "FeedbackReplyDto对象", description = "平台回复反馈DTO")
+public class FeedbackReplyDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "原始反馈ID")
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "平台工作人员ID")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "回复内容")
+    private String content;
+}
+

+ 26 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackAssignDto.java

@@ -0,0 +1,26 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 中台分配跟踪人员DTO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackAssignDto对象", description = "中台分配跟踪人员DTO")
+public class LifeFeedbackAssignDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈ID", required = true)
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "跟踪人员ID(关联life_sys表的id)", required = true)
+    private Integer staffId;
+
+    @ApiModelProperty(value = "操作人员ID")
+    private Integer operatorId;
+}
+

+ 39 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackDto.java

@@ -0,0 +1,39 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 意见反馈提交DTO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackDto对象", description = "意见反馈提交DTO")
+public class LifeFeedbackDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "反馈方式:0-用户反馈,1-AI识别")
+    private Integer feedbackWay;
+
+    @ApiModelProperty(value = "反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer feedbackType;
+
+    @ApiModelProperty(value = "反馈内容")
+    private String content;
+
+    @ApiModelProperty(value = "联系方式(手机号或邮箱)")
+    private String contactWay;
+
+    @ApiModelProperty(value = "文件URL列表(图片和视频,系统会自动识别类型。视频会自动匹配封面图)")
+    private List<String> fileUrlList;
+}
+

+ 35 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackQueryDto.java

@@ -0,0 +1,35 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 中台意见反馈查询DTO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackQueryDto对象", description = "中台意见反馈查询DTO")
+public class LifeFeedbackQueryDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer feedbackType;
+
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决,2-未分配,3-无需解决")
+    private Integer handleStatus;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "反馈方式:0-用户反馈,1-AI识别")
+    private Integer feedbackWay;
+
+    @ApiModelProperty(value = "页码", required = true)
+    private Integer page = 1;
+
+    @ApiModelProperty(value = "每页数量", required = true)
+    private Integer size = 10;
+}
+

+ 29 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackReplyWebDto.java

@@ -0,0 +1,29 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 中台回复用户DTO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackReplyWebDto对象", description = "中台回复用户DTO")
+public class LifeFeedbackReplyWebDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈ID", required = true)
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "回复内容", required = true)
+    private String content;
+
+    @ApiModelProperty(value = "操作人员ID")
+    private Integer operatorId;
+
+    @ApiModelProperty(value = "用户回复内容")
+    private String userReply;
+}
+

+ 26 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LifeFeedbackStatusDto.java

@@ -0,0 +1,26 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 中台更新反馈处理状态DTO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackStatusDto对象", description = "中台更新反馈处理状态DTO")
+public class LifeFeedbackStatusDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈ID", required = true)
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决", required = true)
+    private Integer handleStatus;
+
+    // @ApiModelProperty(value = "操作人员ID")
+    // private Integer operatorId;
+}
+

+ 12 - 3
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java

@@ -213,8 +213,17 @@ public class StoreInfoDto {
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     private Date entertainmentLicenceExpirationTime;
 
-//    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)(多个ID用逗号拼接)")
-//    @JsonDeserialize(using = StringToListDeserializer.class)
-//    private List<String> businessClassify;
+    @ApiModelProperty(value = "营业执照图片URL")
+    private String businessLicenseUrl;
 
+    @ApiModelProperty(value = "营业执照状态 字典 foodLicenceStatus")
+    private Integer businessLicenseStatus;
+
+    @ApiModelProperty(value = "营业执照失败原因")
+    private String businessLicenseReason;
+
+    @ApiModelProperty(value = "营业执照到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date businessLicenseExpirationTime;
 }

+ 33 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/UserReplyDto.java

@@ -0,0 +1,33 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 用户回复DTO
+ */
+@Data
+@ApiModel(value = "UserReplyDto对象", description = "用户回复DTO")
+public class UserReplyDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "用户ID", required = true)
+    private Integer userId;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端", required = true)
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "原始反馈ID(反馈详情页的反馈ID)", required = true)
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "回复内容", required = true)
+    private String content;
+
+    @ApiModelProperty(value = "文件URL列表(图片和视频,系统会自动识别类型。视频会自动匹配封面图)")
+    private List<String> fileUrlList;
+}
+

+ 82 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/DrinkInfoVo.java

@@ -0,0 +1,82 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 酒水餐食信息表 VO
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+@Data
+@ApiModel(value = "DrinkInfoVo", description = "酒水餐食信息表视图对象")
+public class DrinkInfoVo {
+
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "类型(1:餐食,2:酒水)")
+    private Integer type;
+
+    @ApiModelProperty(value = "商品名称")
+    private String name;
+
+    @ApiModelProperty(value = "商品价格")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "商品成本价")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "商品分类")
+    private String category;
+
+    @ApiModelProperty(value = "酒精度数")
+    private BigDecimal alcoholVolume;
+
+    @ApiModelProperty(value = "商品图片")
+    private String picUrl;
+
+    @ApiModelProperty(value = "商品描述")
+    private String description;
+
+    @ApiModelProperty(value = "商品口味")
+    private String flavor;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "门店名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "是否推荐(0:否, 1:是)")
+    private Integer isRecommended;
+
+    @ApiModelProperty(value = "状态(0:下架, 1:上架)")
+    private Integer status;
+
+    @ApiModelProperty(value = "排序值")
+    private Integer sort;
+
+    @ApiModelProperty(value = "是否删除(0:否, 1:是)")
+    private Integer isDeleted;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "更新人ID")
+    private Integer updateUserId;
+
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}

+ 29 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackAttachmentVo.java

@@ -0,0 +1,29 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 反馈附件VO
+ */
+@Data
+@ApiModel(value = "FeedbackAttachmentVo对象", description = "反馈附件VO")
+public class FeedbackAttachmentVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "附件ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "文件类型:1-图片,2-视频")
+    private Integer fileType;
+
+    @ApiModelProperty(value = "文件URL")
+    private String fileUrl;
+
+    @ApiModelProperty(value = "缩略图URL(视频的封面图)")
+    private String thumbnailUrl;
+}
+

+ 41 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackLogVo.java

@@ -0,0 +1,41 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 反馈操作日志VO
+ */
+@Data
+@ApiModel(value = "FeedbackLogVo对象", description = "反馈操作日志VO")
+public class FeedbackLogVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "日志ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "操作类型:0-问题解决状态,1-分配跟踪人员,3-回复用户")
+    private Integer type;
+
+    @ApiModelProperty(value = "操作类型名称")
+    private String typeName;
+
+    @ApiModelProperty(value = "日志内容")
+    private String context;
+
+    @ApiModelProperty(value = "子内容(用于回复用户时显示用户回复)")
+    private String subContext;
+
+    @ApiModelProperty(value = "操作时间")
+    @JsonFormat(pattern = "yyyy/MM/dd HH:mm", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "操作人姓名")
+    private String operatorName;
+}
+

+ 45 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackReplyVo.java

@@ -0,0 +1,45 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 反馈回复VO
+ */
+@Data
+@ApiModel(value = "FeedbackReplyVo对象", description = "反馈回复VO")
+public class FeedbackReplyVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "回复ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "反馈ID")
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "回复类型:0-平台回复,1-用户回复")
+    private Integer replyType;
+
+    @ApiModelProperty(value = "回复类型名称")
+    private String replyTypeName;
+
+    @ApiModelProperty(value = "回复内容")
+    private String replyContent;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy/MM/dd HH:mm", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "图片列表")
+    private List<String> imgUrlList;
+
+    @ApiModelProperty(value = "视频列表")
+    private List<String> videoUrlList;
+}
+

+ 23 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackTypeVo.java

@@ -0,0 +1,23 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 反馈类型VO
+ */
+@Data
+@ApiModel(value = "FeedbackTypeVo对象", description = "反馈类型VO")
+public class FeedbackTypeVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈类型值:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer value;
+
+    @ApiModelProperty(value = "反馈类型名称")
+    private String label;
+}
+

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

@@ -39,6 +39,9 @@ public class LifeDiscountCouponFriendRuleVo extends LifeDiscountCouponFriendRule
     @ApiModelProperty(value = "优惠券数量")
     private Integer couponNum;
 
+    @ApiModelProperty(value = "优惠券id")
+    private Integer couponId;
+
     @ApiModelProperty(value = "面值")
     private BigDecimal nominalValue;
 

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

@@ -120,6 +120,9 @@ public class LifeDiscountCouponVo {
     @ApiModelProperty(value = "经营板块id(词典表 键为 business_section)")
     private Integer businessSection;
 
+    @ApiModelProperty(value = "经营板块名称")
+    private String businessSectionName;
+
     @ApiModelProperty(value = "自定义领取规则数量")
     private Integer claimRuleCustomizeValue;
 

+ 78 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeFeedbackDetailVo.java

@@ -0,0 +1,78 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 中台意见反馈详情VO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackDetailVo对象", description = "中台意见反馈详情VO")
+public class LifeFeedbackDetailVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "用户昵称")
+    private String nickName;
+
+    @ApiModelProperty(value = "账号(手机号)")
+    private String phone;
+
+    @ApiModelProperty(value = "跟踪人员姓名+联系方式")
+    private String staffInfo;
+
+    @ApiModelProperty(value = "跟踪人员ID")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "反馈来源名称")
+    private String feedbackSourceName;
+
+    @ApiModelProperty(value = "反馈方式:0-用户反馈,1-AI识别")
+    private Integer feedbackWay;
+
+    @ApiModelProperty(value = "反馈方式名称")
+    private String feedbackWayName;
+
+    @ApiModelProperty(value = "反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer feedbackType;
+
+    @ApiModelProperty(value = "反馈类型名称")
+    private String feedbackTypeName;
+
+    @ApiModelProperty(value = "反馈时间")
+    @JsonFormat(pattern = "yyyy/MM/dd HH:mm", timezone = "GMT+8")
+    private Date feedbackTime;
+
+    @ApiModelProperty(value = "问题描述")
+    private String content;
+
+    @ApiModelProperty(value = "联系方式")
+    private String contactWay;
+
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决,2-未分配,3-无需解决")
+    private Integer handleStatus;
+
+    @ApiModelProperty(value = "处理状态名称")
+    private String handleStatusName;
+
+    @ApiModelProperty(value = "附件列表")
+    private List<FeedbackAttachmentVo> attachments;
+
+    @ApiModelProperty(value = "操作日志列表")
+    private List<FeedbackLogVo> logs;
+
+    @ApiModelProperty(value = "回复列表(平台回复和用户回复)")
+    private List<FeedbackReplyVo> replies;
+}
+

+ 62 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeFeedbackListVo.java

@@ -0,0 +1,62 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 中台意见反馈列表VO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackListVo对象", description = "中台意见反馈列表VO")
+public class LifeFeedbackListVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "用户昵称")
+    private String nickName;
+
+    @ApiModelProperty(value = "账号(手机号)")
+    private String phone;
+
+    @ApiModelProperty(value = "反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer feedbackType;
+
+    @ApiModelProperty(value = "反馈类型名称")
+    private String feedbackTypeName;
+
+    @ApiModelProperty(value = "反馈方式:0-用户反馈,1-AI识别")
+    private Integer feedbackWay;
+
+    @ApiModelProperty(value = "反馈方式名称")
+    private String feedbackWayName;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "反馈来源名称")
+    private String feedbackSourceName;
+
+    @ApiModelProperty(value = "反馈时间")
+    @JsonFormat(pattern = "yyyy/MM/dd HH:mm", timezone = "GMT+8")
+    private Date feedbackTime;
+
+    @ApiModelProperty(value = "跟踪人员姓名+部门")
+    private String staffInfo;
+
+    @ApiModelProperty(value = "跟踪人员ID")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决,2-未分配,3-无需解决")
+    private Integer handleStatus;
+
+    @ApiModelProperty(value = "处理状态名称")
+    private String handleStatusName;
+}
+

+ 66 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeFeedbackVo.java

@@ -0,0 +1,66 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 意见反馈展示VO
+ */
+@Data
+@ApiModel(value = "LifeFeedbackVo对象", description = "意见反馈展示VO")
+public class LifeFeedbackVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "反馈方式:0-用户反馈,1-AI识别")
+    private Integer feedbackWay;
+
+    @ApiModelProperty(value = "反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer feedbackType;
+
+    @ApiModelProperty(value = "反馈类型名称")
+    private String feedbackTypeName;
+
+    @ApiModelProperty(value = "反馈内容")
+    private String content;
+
+    @ApiModelProperty(value = "联系方式")
+    private String contactWay;
+
+    @ApiModelProperty(value = "反馈时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date feedbackTime;
+
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决")
+    private Integer handleStatus;
+
+    @ApiModelProperty(value = "跟进工作人员ID")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "跟进工作人员姓名")
+    private String staffName;
+
+    @ApiModelProperty(value = "附件图片列表")
+    private List<String> imgUrlList;
+
+    @ApiModelProperty(value = "附件视频列表")
+    private List<String> videoUrlList;
+
+    @ApiModelProperty(value = "平台反馈建议列表")
+    private List<LifeFeedbackVo> platformReplies;
+}
+

+ 26 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeStaffListVo.java

@@ -0,0 +1,26 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 中台跟踪人员列表VO
+ */
+@Data
+@ApiModel(value = "LifeStaffListVo对象", description = "中台跟踪人员列表VO")
+public class LifeStaffListVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "人员ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "人员信息(姓名)")
+    private String staffInfo;
+
+    @ApiModelProperty(value = "姓名")
+    private String realName;
+}
+

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

@@ -21,6 +21,9 @@ public class SportsEquipmentFacilityCategoryVo implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
     @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
     private Integer facilityCategory;
 

+ 1 - 1
alien-entity/src/main/java/shop/alien/entity/storePlatform/StoreOperationalActivity.java

@@ -67,7 +67,7 @@ public class StoreOperationalActivity {
     @TableField("coupon_quantity")
     private Integer couponQuantity;
 
-    @ApiModelProperty(value = "状态:0-禁用, 1-启用")
+    @ApiModelProperty(value = "状态:1-待审核, 2-未开始, 3-审核拒绝, 4-已售罄, 5-进行中, 6-已下架, 7-已结束, 8-审核成功")
     @TableField("status")
     private Integer status;
 

+ 9 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityDTO.java

@@ -2,6 +2,7 @@ package shop.alien.entity.storePlatform.vo;
 
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.JsonNode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -73,5 +74,13 @@ public class StoreOperationalActivityDTO {
     @ApiModelProperty(value = "页数")
     private Integer pageSize;
 
+    @ApiModelProperty(value = "上传图片的方式: 1-用户本地上传,2-使用用户输入的描述AI生成图片")
+    private Integer uploadImgType;
+
+    @ApiModelProperty(value = "用户输入的AI描述")
+    private JsonNode imgDescribe;
+
+    @ApiModelProperty(value = "AI审核的输入参数")
+    private JsonNode auditParam;
 }
 

+ 2 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeDiscountCouponStoreFriendMapper.java

@@ -38,6 +38,7 @@ public interface LifeDiscountCouponStoreFriendMapper extends BaseMapper<LifeDisc
     @Select("select ldcsf.created_time endDate,ldc.nominal_value nominalValue,ldc.minimum_spending_amount minimumSpendingAmount,img.img_url imgUrl,\n" +
             "si.store_name storeName,\n" +
             "ldc.name couponName,\n" +
+            "ldc.id couponId,\n" +
             "ldcsf.single_qty couponNum\n" +
             "from  life_discount_coupon_store_friend ldcsf\n" +
             "left join life_discount_coupon ldc\n" +
@@ -53,6 +54,7 @@ public interface LifeDiscountCouponStoreFriendMapper extends BaseMapper<LifeDisc
     @Select("select ldcsf.created_time endDate,ldc.nominal_value nominalValue,ldc.minimum_spending_amount minimumSpendingAmount,img.img_url imgUrl,\n" +
             "si.store_name storeName,\n" +
             "ldc.name couponName,\n" +
+            "ldc.id couponId,\n" +
             "ldcsf.single_qty couponNum\n" +
             "from  life_discount_coupon_store_friend ldcsf\n" +
             "left join life_discount_coupon ldc\n" +

+ 80 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeFeedbackMapper.java

@@ -0,0 +1,80 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import shop.alien.entity.store.LifeFeedback;
+import shop.alien.entity.store.vo.LifeFeedbackDetailVo;
+import shop.alien.entity.store.vo.LifeFeedbackListVo;
+import shop.alien.entity.store.vo.LifeFeedbackVo;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 意见反馈 Mapper 接口
+ */
+@Mapper
+public interface LifeFeedbackMapper extends BaseMapper<LifeFeedback> {
+
+    /**
+     * 查询用户反馈列表(带工作人员名称)
+     * @param page 分页对象
+     * @param userId 用户ID
+     * @param feedbackSource 反馈来源
+     * @param feedbackWay 反馈方式
+     * @param handleStatus 处理状态
+     * @return 反馈列表
+     */
+    IPage<LifeFeedbackVo> selectFeedbackListWithStaff(
+            Page<LifeFeedbackVo> page,
+            @Param("userId") Integer userId,
+            @Param("feedbackSource") Integer feedbackSource,
+            @Param("feedbackWay") Integer feedbackWay,
+            @Param("handleStatus") Integer handleStatus
+    );
+
+    /**
+     * 查询反馈详情(带工作人员名称)
+     * @param feedbackId 反馈ID
+     * @return 反馈详情
+     */
+    LifeFeedbackVo selectFeedbackDetail(@Param("feedbackId") Integer feedbackId);
+
+    /**
+     * 统计待处理反馈数量
+     * @param feedbackSource 反馈来源
+     * @return 待处理数量
+     */
+    Integer countPendingFeedback(@Param("feedbackSource") Integer feedbackSource);
+
+
+    // ==================== Web中台接口 ====================
+
+    /**
+     * 中台-查询意见反馈列表
+     * @param page 分页对象
+     * @param feedbackType 反馈类型
+     * @param handleStatus 处理状态
+     * @param feedbackSource 反馈来源
+     * @param feedbackWay 反馈方式
+     * @return 反馈列表
+     */
+    IPage<LifeFeedbackListVo> selectWebFeedbackList(
+            Page<LifeFeedbackListVo> page,
+            @Param("feedbackType") Integer feedbackType,
+            @Param("handleStatus") Integer handleStatus,
+            @Param("feedbackSource") Integer feedbackSource,
+            @Param("feedbackWay") Integer feedbackWay
+    );
+
+    /**
+     * 中台-查询反馈详情
+     * @param feedbackId 反馈ID
+     * @return 反馈详情
+     */
+    LifeFeedbackDetailVo selectWebFeedbackDetail(@Param("feedbackId") Integer feedbackId);
+}
+

+ 23 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeFeedbackReplyMapper.java

@@ -0,0 +1,23 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import shop.alien.entity.store.LifeFeedbackReply;
+
+import java.util.List;
+
+/**
+ * 反馈回复 Mapper 接口
+ */
+@Mapper
+public interface LifeFeedbackReplyMapper extends BaseMapper<LifeFeedbackReply> {
+
+    /**
+     * 根据反馈ID查询回复列表
+     * @param feedbackId 反馈ID
+     * @return 回复列表
+     */
+    List<LifeFeedbackReply> selectByFeedbackId(@Param("feedbackId") Integer feedbackId);
+}
+

+ 51 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeImgMapper.java

@@ -0,0 +1,51 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import shop.alien.entity.store.LifeImg;
+
+import java.util.List;
+
+/**
+ * 反馈图片 Mapper 接口
+ */
+@Mapper
+public interface LifeImgMapper extends BaseMapper<LifeImg> {
+
+    /**
+     * 根据反馈ID查询图片列表
+     * @param feedbackId 反馈ID
+     * @return 图片列表
+     */
+    List<LifeImg> selectByFeedbackId(@Param("feedbackId") Integer feedbackId);
+
+    /**
+     * 批量插入图片
+     * @param list 图片列表
+     * @return 插入数量
+     */
+    Integer batchInsert(@Param("list") List<LifeImg> list);
+
+    /**
+     * 根据反馈ID删除图片(逻辑删除)
+     * @param feedbackId 反馈ID
+     * @return 删除数量
+     */
+    Integer deleteByFeedbackId(@Param("feedbackId") Integer feedbackId);
+
+    /**
+     * 查询反馈的图片URL列表
+     * @param feedbackId 反馈ID
+     * @return 图片URL列表
+     */
+    List<String> selectImgUrlsByFeedbackId(@Param("feedbackId") Integer feedbackId);
+
+    /**
+     * 查询反馈的视频URL列表
+     * @param feedbackId 反馈ID
+     * @return 视频URL列表
+     */
+    List<String> selectVideoUrlsByFeedbackId(@Param("feedbackId") Integer feedbackId);
+}
+

+ 24 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeLogMapper.java

@@ -2,11 +2,35 @@ package shop.alien.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.store.LifeLog;
+import shop.alien.entity.store.vo.FeedbackLogVo;
+
+import java.util.List;
 
 /**
  * 日志
  */
 @Mapper
 public interface LifeLogMapper extends BaseMapper<LifeLog> {
+
+    /**
+     * 根据反馈ID查询操作日志列表
+     * @param feedbackId 反馈ID
+     * @return 日志列表
+     */
+    @Select("SELECT l.id, l.type, l.context, l.created_time AS createdTime, " +
+            "CASE l.type " +
+            "   WHEN 0 THEN '问题解决状态' " +
+            "   WHEN 1 THEN '分配跟踪人员' " +
+            "   WHEN 2 THEN '创建反馈工单' " +
+            "   WHEN 3 THEN '回复用户' " +
+            "END AS typeName, " +
+            "s.user_name AS operatorName " +
+            "FROM life_log l " +
+            "LEFT JOIN life_sys s ON l.created_user_id = s.id " +
+            "WHERE l.feedback_id = #{feedbackId} AND l.delete_flag = 0 " +
+            "ORDER BY l.created_time DESC")
+    List<FeedbackLogVo> selectLogsByFeedbackId(@Param("feedbackId") Integer feedbackId);
 }

+ 2 - 2
alien-entity/src/main/java/shop/alien/mapper/StoreCommentAppealMapper.java

@@ -76,13 +76,13 @@ public interface StoreCommentAppealMapper extends BaseMapper<StoreCommentAppeal>
             "LEFT JOIN store_comment sc ON sca.comment_id = sc.id " +
             "LEFT JOIN store_img si ON sca.img_id = si.id " +
             "LEFT JOIN (SELECT sc.id id, si.img_url user_img_url FROM store_comment sc LEFT JOIN store_img si ON sc.img_id = si.id AND sc.delete_flag = 0) sci ON sci.id = sca.comment_id " +
-            "WHERE sca.appeal_status = 0 AND sca.delete_flag = 0")
+            "WHERE sca.appeal_status = 0 and record_id is null AND sca.delete_flag = 0")
     List<Map<String, Object>> getAppealList();
 
 
     @Select("SELECT sca.id, sca.appeal_status, sca.final_result, sca.comment_id, sca.record_id " +
             "FROM store_comment_appeal sca " +
-            "WHERE sca.appeal_status = 0")
+            "WHERE sca.appeal_status = 3")
     List<StoreCommentAppeal> getPendingAppeals();
 
     @Update("UPDATE store_comment_appeal " +

+ 207 - 0
alien-entity/src/main/resources/mapper/LifeFeedbackMapper.xml

@@ -0,0 +1,207 @@
+<?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.LifeFeedbackMapper">
+
+    <!-- 通用结果映射 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeFeedback">
+        <id column="id" property="id" />
+        <result column="user_id" property="userId" />
+        <result column="feedback_source" property="feedbackSource" />
+        <result column="feedback_way" property="feedbackWay" />
+        <result column="feedback_type" property="feedbackType" />
+        <result column="content" property="content" />
+        <result column="contact_way" property="contactWay" />
+        <result column="feedback_time" property="feedbackTime" />
+        <result column="staff_id" property="staffId" />
+        <result column="handle_status" property="handleStatus" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 基础字段 -->
+    <sql id="Base_Column_List">
+        id, user_id, feedback_source, feedback_way, feedback_type, 
+        content, contact_way, feedback_time, staff_id, handle_status,
+        create_time, update_time
+    </sql>
+
+    <!-- 查询用户反馈列表(带工作人员名称) -->
+    <!-- 只查询原始反馈,排除用户回复(用户回复的feedback_time晚于原始反馈) -->
+    <select id="selectFeedbackListWithStaff" resultType="shop.alien.entity.store.vo.LifeFeedbackVo">
+        SELECT 
+            f.id,
+            f.user_id AS userId,
+            f.feedback_source AS feedbackSource,
+            f.feedback_way AS feedbackWay,
+            f.feedback_type AS feedbackType,
+            f.content,
+            f.contact_way AS contactWay,
+            f.feedback_time AS feedbackTime,
+            f.staff_id AS staffId,
+            f.handle_status AS handleStatus,
+            s.user_name AS staffName
+        FROM life_feedback f
+        LEFT JOIN life_sys s ON f.staff_id = s.id
+        WHERE 1=1
+        <if test="userId != null">
+            AND f.user_id = #{userId}
+        </if>
+        <if test="feedbackSource != null">
+            AND f.feedback_source = #{feedbackSource}
+        </if>
+        <if test="feedbackWay != null">
+            AND f.feedback_way = #{feedbackWay}
+        </if>
+        <if test="handleStatus != null">
+            AND f.handle_status = #{handleStatus}
+        </if>
+        <!-- 只查询原始反馈:feedback_way=0(用户反馈)或feedback_way=1(AI识别) -->
+        <!-- 回复记录已存储在life_feedback_reply表中,life_feedback表中只包含原始反馈 -->
+        AND (f.feedback_way = 0 OR f.feedback_way = 1)
+        ORDER BY f.feedback_time DESC
+    </select>
+
+    <!-- 查询反馈详情(带工作人员名称) -->
+    <select id="selectFeedbackDetail" resultType="shop.alien.entity.store.vo.LifeFeedbackVo">
+        SELECT 
+            f.id,
+            f.user_id AS userId,
+            f.feedback_source AS feedbackSource,
+            f.feedback_way AS feedbackWay,
+            f.feedback_type AS feedbackType,
+            f.content,
+            f.contact_way AS contactWay,
+            f.feedback_time AS feedbackTime,
+            f.staff_id AS staffId,
+            f.handle_status AS handleStatus,
+            s.user_name AS staffName
+        FROM life_feedback f
+        LEFT JOIN life_sys s ON f.staff_id = s.id
+        WHERE f.id = #{feedbackId}
+    </select>
+
+    <!-- 统计处理中反馈数量 -->
+    <select id="countPendingFeedback" resultType="java.lang.Integer">
+        SELECT COUNT(1)
+        FROM life_feedback
+        WHERE handle_status = 0
+        <if test="feedbackSource != null">
+            AND feedback_source = #{feedbackSource}
+        </if>
+    </select>
+
+    <!-- 查询平台回复列表 -->
+    <select id="selectPlatformReplies" resultType="shop.alien.entity.store.vo.LifeFeedbackVo">
+        SELECT
+            f.id,
+            f.user_id AS userId,
+            f.feedback_source AS feedbackSource,
+            f.feedback_way AS feedbackWay,
+            f.feedback_type AS feedbackType,
+            f.content,
+            f.feedback_time AS feedbackTime,
+            f.staff_id AS staffId,
+            f.handle_status AS handleStatus,
+            s.user_name AS staffName
+        FROM life_feedback f
+        LEFT JOIN life_sys s ON f.staff_id = s.id
+        WHERE f.user_id = #{userId}
+        AND f.feedback_source = #{feedbackSource}
+        AND f.feedback_way = 1
+        AND f.feedback_time >= #{startTime}
+        ORDER BY f.feedback_time ASC
+    </select>
+
+    <!-- ==================== Web中台接口 ==================== -->
+
+    <!-- 中台-查询意见反馈列表 -->
+    <select id="selectWebFeedbackList" resultType="shop.alien.entity.store.vo.LifeFeedbackListVo">
+        SELECT
+            f.id,
+            u.nick_name AS nickName,
+            u.phone AS phone,
+            f.feedback_type AS feedbackType,
+            CASE f.feedback_type
+                WHEN 0 THEN 'bug反馈'
+                WHEN 1 THEN '优化反馈'
+                WHEN 2 THEN '新增功能反馈'
+            END AS feedbackTypeName,
+            f.feedback_way AS feedbackWay,
+            CASE f.feedback_way
+                WHEN 0 THEN '用户反馈'
+                WHEN 1 THEN 'AI识别'
+            END AS feedbackWayName,
+            f.feedback_source AS feedbackSource,
+            CASE f.feedback_source
+                WHEN 0 THEN '用户端'
+                WHEN 1 THEN '商家端'
+            END AS feedbackSourceName,
+            f.feedback_time AS feedbackTime,
+            f.staff_id AS staffId,
+            CONCAT(IFNULL(s.user_name, '')) AS staffInfo,
+            f.handle_status AS handleStatus,
+            CASE f.handle_status
+                WHEN 0 THEN '处理中'
+                WHEN 1 THEN '已解决'
+            END AS handleStatusName
+        FROM life_feedback f
+        LEFT JOIN store_user u ON f.user_id = u.id
+        LEFT JOIN life_sys s ON f.staff_id = s.id
+        WHERE 1=1
+        <if test="feedbackType != null">
+            AND f.feedback_type = #{feedbackType}
+        </if>
+        <if test="handleStatus != null">
+            AND f.handle_status = #{handleStatus}
+        </if>
+        <if test="feedbackSource != null">
+            AND f.feedback_source = #{feedbackSource}
+        </if>
+        <if test="feedbackWay != null">
+            AND f.feedback_way = #{feedbackWay}
+        </if>
+        ORDER BY f.feedback_time DESC
+    </select>
+
+    <!-- 中台-查询反馈详情 -->
+    <select id="selectWebFeedbackDetail" resultType="shop.alien.entity.store.vo.LifeFeedbackDetailVo">
+        SELECT
+            f.id,
+            u.nick_name AS nickName,
+            u.phone AS phone,
+            f.staff_id AS staffId,
+            CONCAT(IFNULL(s.user_name, '')) AS staffInfo,
+            f.feedback_source AS feedbackSource,
+            CASE f.feedback_source
+                WHEN 0 THEN '用户端'
+                WHEN 1 THEN '商家端'
+            END AS feedbackSourceName,
+            f.feedback_way AS feedbackWay,
+            CASE f.feedback_way
+                WHEN 0 THEN '用户反馈'
+                WHEN 1 THEN 'AI识别'
+            END AS feedbackWayName,
+            f.feedback_type AS feedbackType,
+            CASE f.feedback_type
+                WHEN 0 THEN 'bug反馈'
+                WHEN 1 THEN '优化反馈'
+                WHEN 2 THEN '新增功能反馈'
+            END AS feedbackTypeName,
+            f.feedback_time AS feedbackTime,
+            f.content,
+            f.contact_way AS contactWay,
+            f.handle_status AS handleStatus,
+            CASE f.handle_status
+                WHEN 0 THEN '处理中'
+                WHEN 1 THEN '已解决'
+            END AS handleStatusName
+        FROM life_feedback f
+        LEFT JOIN store_user u ON f.user_id = u.id
+        LEFT JOIN life_sys s ON f.staff_id = s.id
+        WHERE f.id = #{feedbackId}
+    </select>
+
+</mapper>
+

+ 32 - 0
alien-entity/src/main/resources/mapper/LifeFeedbackReplyMapper.xml

@@ -0,0 +1,32 @@
+<?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.LifeFeedbackReplyMapper">
+
+    <!-- 通用结果映射 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeFeedbackReply">
+        <id column="id" property="id" />
+        <result column="feedback_id" property="feedbackId" />
+        <result column="reply_type" property="replyType" />
+        <result column="reply_content" property="replyContent" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 基础字段 -->
+    <sql id="Base_Column_List">
+        id, feedback_id, reply_type, reply_content, create_time, update_time
+    </sql>
+
+    <!-- 根据反馈ID查询回复列表 -->
+    <select id="selectByFeedbackId" resultMap="BaseResultMap">
+        SELECT 
+            <include refid="Base_Column_List" />
+        FROM life_feedback_reply
+        WHERE feedback_id = #{feedbackId}
+        ORDER BY create_time ASC
+    </select>
+
+</mapper>
+

+ 90 - 0
alien-entity/src/main/resources/mapper/LifeImgMapper.xml

@@ -0,0 +1,90 @@
+<?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.LifeImgMapper">
+
+    <!-- 通用结果映射 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeImg">
+        <id column="id" property="id" />
+        <result column="feedback_id" property="feedbackId" />
+        <result column="img_url" property="imgUrl" />
+        <result column="thumbnail_url" property="thumbnailUrl" />
+        <result column="file_type" property="fileType" />
+        <result column="upload_time" property="uploadTime" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_user_id" property="updatedUserId" />
+    </resultMap>
+
+    <!-- 基础字段 -->
+    <sql id="Base_Column_List">
+        id, feedback_id, img_url, thumbnail_url, file_type, upload_time,
+        create_time, update_time, created_user_id, updated_user_id
+    </sql>
+
+    <!-- 根据反馈ID查询图片列表 -->
+    <select id="selectByFeedbackId" resultMap="BaseResultMap">
+        SELECT 
+            <include refid="Base_Column_List" />
+        FROM life_img
+        WHERE feedback_id = #{feedbackId}
+    </select>
+
+    <!-- 批量插入附件(图片和视频) -->
+    <insert id="batchInsert" parameterType="java.util.List">
+        INSERT INTO life_img (
+            feedback_id, img_url, thumbnail_url, file_type, upload_time,
+            create_time, update_time, created_user_id, updated_user_id
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+                #{item.feedbackId},
+                #{item.imgUrl},
+                #{item.thumbnailUrl},
+                #{item.fileType},
+                #{item.uploadTime},
+                NOW(),
+                NOW(),
+                #{item.createdUserId},
+                #{item.updatedUserId}
+            )
+        </foreach>
+    </insert>
+
+    <!-- 根据反馈ID删除图片 -->
+    <delete id="deleteByFeedbackId">
+        DELETE FROM life_img
+        WHERE feedback_id = #{feedbackId}
+    </delete>
+
+    <!-- 查询反馈的图片URL列表 -->
+    <select id="selectImgUrlsByFeedbackId" resultType="java.lang.String">
+        SELECT img_url
+        FROM life_img
+        WHERE feedback_id = #{feedbackId}
+        AND file_type = 1
+        ORDER BY upload_time ASC
+    </select>
+
+    <!-- 查询反馈的视频URL列表 -->
+    <select id="selectVideoUrlsByFeedbackId" resultType="java.lang.String">
+        SELECT img_url
+        FROM life_img
+        WHERE feedback_id = #{feedbackId}
+        AND file_type = 2
+        ORDER BY upload_time ASC
+    </select>
+
+    <!-- 查询反馈的视频信息(包含视频URL和缩略图URL) -->
+    <select id="selectVideoInfoByFeedbackId" resultMap="BaseResultMap">
+        SELECT
+            <include refid="Base_Column_List" />
+        FROM life_img
+        WHERE feedback_id = #{feedbackId}
+        AND file_type = 2
+        ORDER BY upload_time ASC
+    </select>
+
+</mapper>

+ 14 - 0
alien-job/src/main/java/shop/alien/job/feign/SecondGoodsFeign.java

@@ -28,4 +28,18 @@ public interface SecondGoodsFeign {
 
     @GetMapping("/secondGoods/approveAndListGoods")
     boolean approveAndListGoods(SecondGoods goods);
+
+    /**
+     * 获取AI商品审核结果
+     * @return 处理结果
+     */
+    @GetMapping("/secondGoods/getAiGoodsCheckResult")
+    shop.alien.entity.result.R<String> getAiGoodsCheckResult();
+
+    /**
+     * 处理所有待处理的视频审核任务
+     * @return 处理结果
+     */
+    @GetMapping("/secondGoods/processVideoModerationResult")
+    shop.alien.entity.result.R<String> processVideoModerationResult();
 }

+ 5 - 41
alien-job/src/main/java/shop/alien/job/jobhandler/VideoModerationJobHandler.java

@@ -5,11 +5,8 @@ import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
-import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.result.R;
 import shop.alien.job.feign.SecondGoodsFeign;
-import shop.alien.mapper.system.SecondVideoTaskMapper;
-
-import java.util.List;
 
 /**
  * 视频审核任务XXL-JOB处理器
@@ -20,57 +17,24 @@ import java.util.List;
 public class VideoModerationJobHandler {
 
     /**
-     * 视频审核任务Mapper
-     */
-    private final SecondVideoTaskMapper videoModerationTaskMapper;
-
-    /**
      * 二级商品服务Feign客户端
      */
     private final SecondGoodsFeign secondGoodsFeign;
     
     /**
      * 视频审核结果拉取任务
-     * 每30秒执行一次
+     * 通过Feign调用alien-second服务处理视频审核结果
      */
     @XxlJob("videoModerationResultJobHandler")
     public ReturnT<String> videoModerationResultJobHandler(String param) {
         try {
             log.info("开始执行视频审核结果拉取任务");
-            
-            // 查询待处理的审核任务(状态为SUBMITTED或PROCESSING)
-            List<SecondVideoTask> pendingTasks = videoModerationTaskMapper.selectPendingTasks();
-            
-            if (pendingTasks.isEmpty()) {
-                log.info("没有待处理的视频审核任务");
-                return ReturnT.SUCCESS;
-            }
-            
-            log.info("共找到 {} 个待处理的视频审核任务", pendingTasks.size());
-            
-            // 处理每个任务
-            int successCount = 0;
-            for (SecondVideoTask task : pendingTasks) {
-                try {
-                    boolean success = secondGoodsFeign.processTask(task.getTaskId());
-                    if (success) {
-                        successCount++;
-                        // 处理视频审核结果,更新商品状态
-                        log.info("处理第 {} 个task", successCount);
-                        log.info("TaskId:开始 {} ", task.getTaskId());
-                        secondGoodsFeign.processVideoResult(task.getTaskId());
-                        log.info("TaskId:结束 {} ", task.getTaskId());
-                    }
-                } catch (Exception e) {
-                    log.error("处理视频审核任务时发生异常,任务ID: {}", task.getTaskId(), e);
-                }
-            }
-            
-            log.info("视频审核结果拉取任务执行完成,成功处理 {} 个任务", successCount);
+            R<String> result = secondGoodsFeign.processVideoModerationResult();
+            log.info("视频审核结果拉取任务执行完成: {}", result.getData());
             return ReturnT.SUCCESS;
         } catch (Exception e) {
             log.error("执行视频审核结果拉取任务时发生异常", e);
             return ReturnT.FAIL;
         }
     }
-}
+}

+ 178 - 21
alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java

@@ -3,30 +3,34 @@ package shop.alien.job.second;
 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.toolkit.StringUtils;
+import com.xxl.job.core.context.XxlJobHelper;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
 import org.springframework.stereotype.Component;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.ResourceAccessException;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.http.*;
+import shop.alien.entity.result.R;
 import shop.alien.entity.second.SecondGoodsRecord;
 import shop.alien.entity.second.SecondRiskControlRecord;
-import shop.alien.entity.store.LifeUser;
-import shop.alien.entity.store.LifeUserViolation;
-import shop.alien.entity.store.StoreDictionary;
-import shop.alien.mapper.LifeUserMapper;
-import shop.alien.mapper.LifeUserViolationMapper;
-import shop.alien.mapper.StoreDictionaryMapper;
+import shop.alien.entity.store.*;
+import shop.alien.mapper.*;
 import shop.alien.mapper.second.SecondGoodsRecordMapper;
 import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+import shop.alien.util.common.Constants;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.net.SocketException;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -39,6 +43,9 @@ import java.util.stream.Collectors;
 @Slf4j
 @Component
 @RequiredArgsConstructor
+//@RestController
+//@RequestMapping("/job")
+//@RequiredArgsConstructor
 public class AiCheckXxlJob {
 
     // 添加RestTemplate用于HTTP调用
@@ -54,6 +61,9 @@ public class AiCheckXxlJob {
 
     private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;
 
+    @Autowired
+    private final StoreImgMapper storeImgMapper;
+
     // 第三方接口地址 登录接口URL
     @Value("${third-party-login.base-url}")
     private String loginUrl;
@@ -66,6 +76,16 @@ public class AiCheckXxlJob {
     @Value("${third-party-pass-word.base-url}")
     private String passWord;
 
+    // 第三方接口地址 登录接口URL
+    @Value("${third-party-goods.base-url}")
+    private String goodsUrl;
+
+    // 第三方接口地址 登录接口URL
+    @Value("${third-party-goodsresult.base-url}")
+    private String goodsResultUrl;
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
     /**
      * AI自动审核任务处理器
      * <p>
@@ -81,6 +101,7 @@ public class AiCheckXxlJob {
      * @since 1.0.0
      */
     @XxlJob("aiCheckJobHandler")
+//    @PostMapping("/aiCheckJobHandler")
     public void aiCheckJobHandler() {
         log.info("开始执行AI自动审核任务");
 
@@ -88,8 +109,8 @@ public class AiCheckXxlJob {
             log.info("登录Ai服务获取token..." + loginUrl);
             //构建请求参数
             MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
-            formData.add("username", "admin");    // 表单字段 1:用户名
-            formData.add("password", "123456");    // 表单字段 2:密码
+            formData.add("username", userName);    // 表单字段 1:用户名
+            formData.add("password", passWord);    // 表单字段 2:密码
 
             //设置请求头
             HttpHeaders headers = new HttpHeaders();
@@ -97,7 +118,7 @@ public class AiCheckXxlJob {
             HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
             ResponseEntity<String> postForEntity = null;
             try {
-                postForEntity = restTemplate.postForEntity("http://192.168.2.78:9000/ai/user-auth-core/api/v1/auth/login", requestEntity, String.class);
+                postForEntity = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
             } catch (Exception e) {
                 log.error("类:PostMethod 方法:post", e);
             }
@@ -110,7 +131,9 @@ public class AiCheckXxlJob {
                     JSONObject dataJson = jsonObject.getJSONObject("data");
                     String accessToken = dataJson.getString("access_token");
                     // 查询所有待处理的用户违规记录
-                    List<LifeUserViolation> lifeUserViolations = lifeUserViolationMapper.selectList(new LambdaQueryWrapper<LifeUserViolation>().eq(LifeUserViolation::getProcessingStatus, "5"));
+                    List<LifeUserViolation> lifeUserViolations = lifeUserViolationMapper.selectList(new LambdaQueryWrapper<LifeUserViolation>()
+                            .eq(LifeUserViolation::getReportContextType, "4")
+                            .eq(LifeUserViolation::getProcessingStatus, "0"));
 
                     // 遍历每条违规记录,组装AI审核请求数据
                     for (LifeUserViolation violation : lifeUserViolations) {
@@ -118,11 +141,11 @@ public class AiCheckXxlJob {
                         Map<String, Object> requestBody = new HashMap<>();
 
                         // 设置投诉记录ID
-                        requestBody.put("complaint_id", violation.getId());
+                        requestBody.put("complaint_id", violation.getId().toString());
 
                         // 查询投诉类型字典信息
                         StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>()
-                                .eq(StoreDictionary::getTypeName, violation.getDictId()).eq(StoreDictionary::getTypeName, violation.getDictType()));
+                                .eq(StoreDictionary::getDictId, violation.getDictId()).eq(StoreDictionary::getTypeName, violation.getDictType()));
                         String complaint_type = "";
                         if (storeDictionary != null) {
                             // 设置投诉类型
@@ -138,20 +161,30 @@ public class AiCheckXxlJob {
                         SecondGoodsRecord secondGoodsRecord = secondGoodsRecordMapper.selectById(violation.getBusinessId());
                         if (secondGoodsRecord != null) {
                             // 设置被举报人信息
-                            requestBody.put("reported_user_id", secondGoodsRecord.getUserId());
+                            requestBody.put("reported_user_id", secondGoodsRecord.getUserId().toString());
                             requestBody.put("reported_user_type", "");
                             requestBody.put("reported_info", lifeUserMapper.selectById(secondGoodsRecord.getUserId()));
                             requestBody.put("product_name", secondGoodsRecord.getTitle());
                         }
                         // 设置商品相关信息
                         requestBody.put("product_info", secondGoodsRecord);
-                        requestBody.put("product_id", violation.getBusinessId());
+                        requestBody.put("product_id", violation.getGoodsId().toString());
 
                         // 设置投诉文本内容
                         requestBody.put("complaint_text", violation.getOtherReasonContent());
 
-                        // 设置证据图片数组(将逗号分隔的字符串转换为数组)
-                        requestBody.put("evidence_images", violation.getReportEvidenceImg() != null ? violation.getReportEvidenceImg().split(",") : new String[0]);
+                        LambdaQueryWrapper<StoreImg> wrapper = new LambdaQueryWrapper<>();
+                        wrapper.eq(StoreImg::getStoreId, violation.getId());
+                        wrapper.eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_REPORT);
+                        List<StoreImg> imgList = storeImgMapper.selectList(wrapper);
+
+                        // 将 imgList 中的 ImgUrl 提取为 String[] 数组
+                        String[] imgUrls = imgList.stream()
+                                .map(StoreImg::getImgUrl)
+                                .toArray(String[]::new);
+
+                        // 设置证据图片数组
+                        requestBody.put("evidence_images", imgUrls);
 
                         HttpHeaders aiHeaders = new HttpHeaders();
                         aiHeaders.setContentType(MediaType.APPLICATION_JSON);
@@ -161,8 +194,28 @@ public class AiCheckXxlJob {
                         HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
                         ResponseEntity<String> response = null;
                         try {
-                            response = restTemplate.postForEntity("http://192.168.2.78:9000/ai/auto-review/api/v1/product_complaint_record/submit", request, String.class);
+                            response = restTemplate.postForEntity(goodsUrl, request, String.class);
                             log.info("AI自动审核结果:{}", response.getBody());
+                            if (response.getStatusCodeValue() != 200) {
+                                throw new RuntimeException("AI门店审核接口调用失败 http状态:" + response.getStatusCode());
+                            }
+                            if (StringUtils.isNotEmpty(response.getBody())) {
+                                com.alibaba.fastjson.JSONObject taskObject = com.alibaba.fastjson.JSONObject.parseObject(response.getBody());
+                                if (taskObject.getInteger("code") == 200) {
+                                    com.alibaba.fastjson.JSONObject data = taskObject.getJSONObject("data");
+                                    if (data != null) {
+                                        String taskId = data.getString("task_id");
+                                        violation.setAiTaskId(taskId);
+                                        lifeUserViolationMapper.updateById(violation);
+                                    }
+                                }
+                            }
+                        } catch (ResourceAccessException e) {
+                            if (e.getCause() instanceof SocketException) {
+                                log.error("AI自动审核请求网络异常,连接被中断: {}", e.getMessage());
+                            } else {
+                                log.error("AI自动审核请求资源访问异常", e);
+                            }
                         } catch (Exception e) {
                             log.error("AI自动审核请求异常", e);
                         }
@@ -176,6 +229,110 @@ public class AiCheckXxlJob {
         log.info("AI自动审核任务执行完成");
     }
 
+    @XxlJob("aiCheckJobHandlerResult")
+    public void aiCheckJobHandlerResult() {
+        log.info("登录Ai服务获取token..." + loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+        ResponseEntity<String> postForEntity = null;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            postForEntity = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+        }
+
+        if (postForEntity != null) {
+            if (postForEntity.getStatusCodeValue() == 200) {
+                log.info("请求Ai服务登录成功 postForEntity.getBody()\t" + postForEntity.getBody());
+                String responseBody = postForEntity.getBody();
+                JSONObject jsonObject = JSONObject.parseObject(responseBody);
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                String accessToken = dataJson.getString("access_token");
+
+                // 查询所有待处理的用户违规记录
+                List<LifeUserViolation> lifeUserViolations = lifeUserViolationMapper.selectList(new LambdaQueryWrapper<LifeUserViolation>()
+                        .eq(LifeUserViolation::getReportContextType, "4")
+                        .eq(LifeUserViolation::getProcessingStatus, "0"));
+
+                for (LifeUserViolation violation : lifeUserViolations) {
+                    // 初始化请求体Map
+                    Map<String, Object> requestBody = new HashMap<>();
+
+
+
+                    if (violation.getAiTaskId() == null || violation.getAiTaskId() == "") {
+                        continue;
+                    }
+
+                    // 设置投诉记录ID
+                    requestBody.put("task_id", violation.getAiTaskId());
+                    HttpHeaders aiHeaders = new HttpHeaders();
+                    aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+                    aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+                    System.out.println(requestBody);
+                    HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+                    ResponseEntity<String> response = null;
+                    try {
+                        response = restTemplate.postForEntity(goodsResultUrl, request, String.class);
+                        if (response.getStatusCodeValue() != 200) {
+                            throw new RuntimeException("AI门店审核接口调用失败 http状态:" + response.getStatusCode());
+                        }
+                        if (StringUtils.isNotEmpty(response.getBody())) {
+                            com.alibaba.fastjson.JSONObject taskObject = com.alibaba.fastjson.JSONObject.parseObject(response.getBody());
+                            if (taskObject.getInteger("code") == 200) {
+                                com.alibaba.fastjson.JSONObject data = taskObject.getJSONObject("data");
+                                if (data != null) {
+                                    String status = data.getString("status");
+                                    if (!"processing".equals(status)) {
+                                        boolean isValid = data.getBoolean("is_valid");
+                                        violation.setReportResult(data.getString("matched_type"));
+                                        violation.setProcessingStatus(isValid ? "1" : "2");
+                                        violation.setProcessingTime(new Date());
+                                        lifeUserViolationMapper.updateById(violation);
+
+                                        // 发送通知
+                                        LifeNotice lifeMessage = new LifeNotice();
+                                        LifeUser lifeUser = lifeUserMapper.selectById(violation.getReportingUserId());
+                                        lifeMessage.setReceiverId("user_" + lifeUser.getUserPhone());
+                                        String text = "您的举报商品结果为" + (isValid ? "违规" : "未违规");
+                                        com.alibaba.fastjson.JSONObject lifeMessagejson = new com.alibaba.fastjson.JSONObject();
+                                        lifeMessagejson.put("title", "平台已受理");
+                                        lifeMessagejson.put("message", text);
+                                        lifeMessage.setContext(lifeMessagejson.toJSONString());
+
+                                        lifeMessage.setTitle("举报通知");
+                                        lifeMessage.setBusinessId(violation.getId());
+                                        lifeMessage.setSenderId("system");
+                                        lifeMessage.setIsRead(0);
+                                        lifeMessage.setNoticeType(1);
+                                        lifeNoticeMapper.insert(lifeMessage);
+                                    }
+                                }
+                            }
+                        }
+                    } catch (ResourceAccessException e) {
+                        if (e.getCause() instanceof SocketException) {
+                            log.error("AI自动审核结果查询网络异常,连接被中断: {}", e.getMessage());
+                        } else {
+                            log.error("AI自动审核结果查询资源访问异常", e);
+                        }
+                    } catch (Exception e) {
+                        log.error("AI自动审核结果查询异常", e);
+                    }
+
+                }
+            }
+        }
+
+    }
+
     /**
      * 二手商品风控记录审核任务
      * <p>

+ 31 - 5
alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java

@@ -18,11 +18,13 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
 import shop.alien.entity.second.SecondGoods;
-import shop.alien.entity.store.LifeUserViolation;
-import shop.alien.entity.store.SecondAiTask;
+import shop.alien.entity.store.*;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.LifeUserViolationMapper;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 @Slf4j
@@ -33,15 +35,21 @@ public class AiUserViolationJob {
     private final RestTemplate restTemplate;
     private final LifeUserViolationMapper lifeUserViolationMapper;
 
+    private final LifeUserMapper lifeUserMapper;
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
     @Value("${third-party-user-name.base-url}")
     private String userName;
 
     @Value("${third-party-pass-word.base-url}")
     private String passWord;
 
-    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
+    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl;
 
-    private String aiUserViolationCheckUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/user_complaint_record/result";
+    @Value("${third-party-aiUserViolationCheckUrl.base-url:http://192.168.2.250:9000/ai/auto-review/api/v1/user_complaint_record/result}")
+    private String aiUserViolationCheckUrl;
 
     @XxlJob("getAiUserViolationResult")
     public R<String> getAiUserViolationResult() {
@@ -141,11 +149,29 @@ public class AiUserViolationJob {
                             aiTask.setProcessingStatus("1");
                             aiTask.setReportResult(dataJsonObj.getString("decision_reason"));
                         } else {
-                            aiTask.setProcessingStatus("0");
+                            aiTask.setProcessingStatus("2");
                             aiTask.setReportResult(dataJsonObj.getString("decision_reason"));
                         }
+                        aiTask.setProcessingTime(new Date());
                     }
 
+                    // 发送通知
+                    LifeNotice lifeMessage = new LifeNotice();
+                    LifeUser lifeUser = lifeUserMapper.selectById(task.getReportingUserId());
+                    lifeMessage.setReceiverId("user_" + lifeUser.getUserPhone());
+                    String text = "您的举报用户结果为" + (aiTask.getProcessingStatus().equals("1") ? "违规" : "未违规");
+
+                    com.alibaba.fastjson.JSONObject lifeMessagejson = new com.alibaba.fastjson.JSONObject();
+                    lifeMessagejson.put("title", "平台已受理");
+                    lifeMessagejson.put("message", text);
+                    lifeMessage.setContext(lifeMessagejson.toJSONString());
+                    lifeMessage.setTitle("举报通知");
+                    lifeMessage.setBusinessId(task.getId());
+                    lifeMessage.setSenderId("system");
+                    lifeMessage.setIsRead(0);
+                    lifeMessage.setNoticeType(1);
+                    lifeNoticeMapper.insert(lifeMessage);
+
                     QueryWrapper<LifeUserViolation> queryWrapper = new QueryWrapper<>();
                     queryWrapper.eq("ai_task_id", taskId);
                     LifeUserViolation lifeUserViolation = lifeUserViolationMapper.selectOne(queryWrapper);

+ 13 - 167
alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java

@@ -1,190 +1,36 @@
 package shop.alien.job.second;
 
-import com.alibaba.fastjson2.JSONObject;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
 import org.springframework.stereotype.Component;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
-import shop.alien.entity.second.SecondGoods;
-import shop.alien.entity.store.SecondAiTask;
-import shop.alien.entity.store.StoreComment;
-import shop.alien.entity.store.StoreCommentAppeal;
 import shop.alien.job.feign.SecondGoodsFeign;
-import shop.alien.mapper.SecondAiTaskMapper;
-import shop.alien.mapper.second.SecondGoodsMapper;
-
-import javax.annotation.Resource;
-import java.util.ArrayList;
-import java.util.List;
 
+/**
+ * 商品审核定时任务
+ */
 @Slf4j
 @Component
 @RequiredArgsConstructor
 public class goodsCheckJob {
 
-    private final RestTemplate restTemplate;
-
-    private final SecondAiTaskMapper secondAiTaskMapper;
-    private final SecondGoodsMapper secondGoodsMapper;
-
     private final SecondGoodsFeign secondGoodsFeign;
 
-    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
-
-    private String goodsCheckUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/audit_task/getResult";
-
-    @Value("${third-party-user-name.base-url}")
-    private String userName;
-
-    @Value("${third-party-pass-word.base-url}")
-    private String passWord;
-
+    /**
+     * 获取AI商品审核结果
+     * 通过Feign调用alien-second服务处理AI审核结果
+     */
     @XxlJob("getAiGoodsCheckResult")
     public R<String> getAiGoodsCheckResult() {
-        String accessToken = fetchAiServiceToken();
-        if (!StringUtils.hasText(accessToken)) {
-            return R.fail("调用差评申诉辅助系统 登录接口失败");
-        }
-        return getGoodsCheck(accessToken);
-    }
-
-
-    private String fetchAiServiceToken() {
-        log.info("登录Ai服务获取token...{}", loginUrl);
-        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
-        formData.add("username", userName);
-        formData.add("password", passWord);
-
-        HttpHeaders headers = new HttpHeaders();
-        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
-
+        log.info("开始执行AI商品审核结果获取任务");
         try {
-            ResponseEntity<String> response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
-            if (response != null && response.getStatusCodeValue() == 200 && response.getBody() != null) {
-                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
-                JSONObject dataJson = jsonObject.getJSONObject("data");
-                return dataJson != null ? dataJson.getString("access_token") : null;
-            }
-            log.error("请求差评申诉辅助系统 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+            R<String> result = secondGoodsFeign.getAiGoodsCheckResult();
+            log.info("AI商品审核结果获取任务执行完成: {}", result.getData());
+            return result;
         } catch (Exception e) {
-            log.error("调用差评申诉辅助系统登录接口异常", e);
-        }
-        return null;
-    }
-
-
-    private R<String> getGoodsCheck(String accessToken) {
-
-
-        HttpHeaders analyzeHeaders = new HttpHeaders();
-        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
-        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
-        // 查询所有状态为处理中的申诉
-        List<SecondAiTask> pendingTasks = secondAiTaskMapper.selectList(
-                new QueryWrapper<SecondAiTask>()
-                        .eq("status", "PROCESSING")
-        );
-
-        // 循环调用查询结果接口
-        for (SecondAiTask task : pendingTasks) {
-            String completedUrl = buildCompletedUrl(task.getTaskId());
-
-            ResponseEntity<String> analyzeResp;
-
-            RestTemplate restTemplateWithAuth = new RestTemplate();
-            List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
-            interceptors.add((request, body, execution) -> {
-                request.getHeaders().set("Authorization", "Bearer " + accessToken);
-                return execution.execute(request, body);
-            });
-            restTemplateWithAuth.setInterceptors(interceptors);
-
-            ResponseEntity<String> response = null;
-
-            try {
-                analyzeResp = restTemplateWithAuth.getForEntity(completedUrl, String.class);
-
-                if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
-                    String analyzeBody = analyzeResp.getBody();
-                    log.info("商品审核提交成功, 返回: {}", analyzeBody);
-
-                    JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
-                    JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
-
-                    if (dataJsonObj == null) {
-                        log.error("商品审核返回数据为空");
-                        R.fail("商品审核返回数据为空");
-                        continue;
-                    }
-
-                    // 获取task_id用于后续查询
-                    String taskId = dataJsonObj.getString("task_id");
-                    if (taskId == null) {
-                        log.error("商品审核返回task_id为空");
-                        R.fail("商品审核返回task_id为空");
-                        continue;
-                    }
-
-                    SecondAiTask aiTask = new SecondAiTask();
-                    aiTask.setTaskId(taskId);
-                    if (dataJsonObj.getString("status").equals("pending")) {
-                        R.fail("审核未结束");
-                        continue;
-                    }
-
-                    if (dataJsonObj.getString("status").equals("done")) {
-                        aiTask.setStatus("SUCCESS");
-                    }
-
-                    aiTask.setResult(dataJsonObj.toJSONString());
-
-                    QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
-                    queryWrapper.eq("ai_task_id", taskId);
-                    SecondGoods goods = secondGoodsMapper.selectOne(queryWrapper);
-                    if (goods == null) {
-                        log.error("商品不存在");
-                        R.fail("商品不存在");
-                        continue;
-                    }
-                    secondGoodsFeign.approveAndListGoods(goods);
-
-                } else {
-                    if (analyzeResp != null) {
-                        log.error("调用商品审核接口失败, http状态: {}", analyzeResp.getStatusCode());
-                        R.fail("调用商品审核接口失败, http状态: " + analyzeResp.getStatusCode());
-                    }
-                }
-            } catch (Exception e) {
-                log.error("调用差评申述查询结果接口异常", e);
-            }
-        }
-        return R.success("调用商品审核结果接口完成");
-    }
-
-
-    private String buildCompletedUrl(String recordId) {
-        String baseUrl = goodsCheckUrl;
-        if (!StringUtils.hasText(baseUrl)) {
-            throw new IllegalStateException("差评申述分析接口地址未配置");
-        }
-        if (baseUrl.endsWith("/")) {
-            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+            log.error("AI商品审核结果获取任务执行异常", e);
+            return R.fail("任务执行异常: " + e.getMessage());
         }
-        // 构建新的URL格式: /api/v1/audit_task/getResult?task_id={recordId}
-        return baseUrl + "?task_id=" + recordId;
     }
 }

+ 25 - 9
alien-job/src/main/java/shop/alien/job/store/BadReviewAppealJob.java

@@ -2,6 +2,7 @@ package shop.alien.job.store;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.xxl.job.core.context.XxlJobHelper;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.RequiredArgsConstructor;
@@ -18,11 +19,12 @@ import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
-import shop.alien.entity.store.StoreComment;
-import shop.alien.entity.store.StoreCommentAppeal;
+import shop.alien.entity.store.*;
 import shop.alien.entity.result.R;
+import shop.alien.mapper.LifeNoticeMapper;
 import shop.alien.mapper.StoreCommentAppealMapper;
 import shop.alien.mapper.StoreCommentMapper;
+import shop.alien.mapper.StoreUserMapper;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -48,24 +50,26 @@ public class BadReviewAppealJob {
     private final StoreCommentAppealMapper storeCommentAppealMapper;
 
     private final StoreCommentMapper storeCommentMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
 
 //    @Value("${third-party-login.base-url}")
 //    private String loginUrl;
 
-    private String loginUrl = "http://192.168.2.78:9000/ai/user-auth-core/api/v1/auth/login";
-
-
     @Value("${third-party-user-name.base-url}")
     private String userName;
 
     @Value("${third-party-pass-word.base-url}")
     private String passWord;
 
-    private String analyzeUrl = "http://192.168.2.78:9000/ai/auto-review/api/v1/analyze";
-
-    private String resultUrl = "http://192.168.2.78:9000/ai/auto-review";
+    @Value("${third-party-login.base-url}")
+    private String loginUrl;
 
+    @Value("${third-party-analyzeUrl.base-url}")
+    private String analyzeUrl;
 
+    @Value("${third-party-resultUrl.base-url}")
+    private String resultUrl;
 
     /**
      * 差评申述置信度分析接口地址
@@ -172,6 +176,18 @@ public class BadReviewAppealJob {
                     storeCommentAppealMapper.updateByRecordId(appeal.getRecordId(),
                             sCommentAppeal.getAppealStatus(),
                             sCommentAppeal.getFinalResult());
+
+                    // 发送通知
+                    LifeNotice lifeMessage = new LifeNotice();
+                    StoreUser storeUser = storeUserMapper.selectOne(new QueryWrapper<StoreUser>().eq("store_id", appeal.getStoreId()));
+                    lifeMessage.setReceiverId("store_" + storeUser.getPhone());
+                    String text = "您的评论申诉结果为" + sCommentAppeal.getFinalResult();
+                    lifeMessage.setContext(text);
+                    lifeMessage.setSenderId("system");
+                    lifeMessage.setIsRead(0);
+                    lifeMessage.setNoticeType(1);
+                    lifeNoticeMapper.insert(lifeMessage);
+
                 } else {
                     if (analyzeResp != null) {
                         log.error("调用差评申述置信度分析接口失败, http状态: {}", analyzeResp.getStatusCode());
@@ -283,7 +299,7 @@ public class BadReviewAppealJob {
                 //修改评论状态为"处理中"
                 StoreCommentAppeal sCommentAppeal = new StoreCommentAppeal();
                 sCommentAppeal.setId((Integer) storeCommentAppeal.get("id"));
-                sCommentAppeal.setAppealStatus(0);
+                sCommentAppeal.setAppealStatus(3);
                 sCommentAppeal.setRecordId(recordId);
                 storeCommentAppealMapper.updateById(sCommentAppeal);
 

+ 1 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java

@@ -37,6 +37,7 @@ public class LawyerUserController {
     @ApiOperationSupport(order = 1)
     @PostMapping("/addLawyerUser")
     public R<LawyerUser> addLawyerUser(@RequestBody LawyerUser lawyerUser) {
+
         log.info("LawyerUserController.addLawyerUser?lawyerUser={}", lawyerUser);
         return lawyerUserService.addLawyerUser(lawyerUser);
     }

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

@@ -1715,7 +1715,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
             LifeNotice lifeNotice = new LifeNotice();
             lifeNotice.setSenderId(SYSTEM_SENDER_ID);
             lifeNotice.setBusinessId(order.getId());
-            lifeNotice.setTitle("退款申请通知");
+            lifeNotice.setTitle("申请退款通知");
 
             // 获取申请人接收ID
             String receiverId = getClientReceiverId(clientUserId);

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

@@ -177,7 +177,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                     //退款到账通知
                     LifeNotice lifeNotice2 = buildUserLifeNotice(order, "退款到账通知", "您的编号为" + orderNo + "的订单,订单金额已原路返还至您的支付渠道,请查收。");
                     WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice2);
-                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(lifeNotice2.getCreatedTime(), 2));
+                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(new Date(), 2));
 
                     lifeNoticeMapper.insert(lifeNotice2);
                     webSocketProcess.sendMessage(lifeNotice2.getReceiverId(), JSONObject.from(webSocketVo2).toJSONString());
@@ -187,6 +187,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                     LawyerConsultationOrder update = new LawyerConsultationOrder();
                     update.setId(order.getId());
                     update.setOrderStatus(5);
+                    update.setApplyRefundTime(new Date());
                     orderMapper.updateById( update);
                 }
             }

+ 16 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/util/AliSms.java

@@ -9,10 +9,13 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
+import shop.alien.entity.store.StoreVerificationCode;
+import shop.alien.mapper.StoreVerificationCodeMapper;
 import shop.alien.util.common.RandomCreateUtil;
 import shop.alien.lawyer.config.BaseRedisService;
 
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -28,6 +31,8 @@ import java.util.List;
 public class AliSms {
     private final BaseRedisService baseRedisService;
 
+    private final StoreVerificationCodeMapper storeVerificationCodeMapper;
+
     @Value("${ali.sms.accessKeyId}")
     private String accessKeyId;
 
@@ -88,6 +93,17 @@ public class AliSms {
             }
             // 验证码发送成功,将验证码保存到redis中 设置60秒过期
             baseRedisService.setString("verification_lawyer_"+phone,code.toString(),Long.valueOf(300));
+            // 永久存储验证码
+            StoreVerificationCode entity = new StoreVerificationCode();
+            entity.setAppType(2);
+            entity.setBusinessType(7);
+            entity.setPhone(phone);
+            entity.setCode(String.valueOf(code));
+            Date now = new Date();
+            entity.setCreatedTime(now);
+            entity.setUpdatedTime(now);
+            entity.setDeleteFlag(0);
+            storeVerificationCodeMapper.insert(entity);
             return code;
         } catch (Exception e) {
             log.error("AliSmsConfig.sendSms ERROR Msg={}", e.getMessage());

+ 40 - 0
alien-second/src/main/java/shop/alien/second/controller/SecondGoodsController.java

@@ -12,6 +12,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.second.SecondGoods;
 import shop.alien.entity.second.vo.SecondGoodsVo;
 import shop.alien.mapper.second.SecondGoodsAuditMapper;
+import shop.alien.second.service.SecondGoodsAuditService;
 import shop.alien.second.service.SecondGoodsService;
 import shop.alien.second.service.VideoModerationService;
 import shop.alien.util.common.JwtUtil;
@@ -56,6 +57,9 @@ public class SecondGoodsController {
     // 注入图片审核工具
     private final ImageModerationUtil imageModerationUtil;
 
+    // 注入二手商品审核服务
+    private final SecondGoodsAuditService secondGoodsAuditService;
+
     /**
      * 根据ID获取二手商品
      */
@@ -318,4 +322,40 @@ public class SecondGoodsController {
             return false;
         }
     }
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    @ApiOperation("获取AI商品审核结果")
+    @GetMapping("/getAiGoodsCheckResult")
+    public R<String> getAiGoodsCheckResult() {
+        log.info("SecondGoodsController.getAiGoodsCheckResult");
+        try {
+            String result = secondGoodsAuditService.getAiGoodsCheckResult();
+            return R.data(result, "获取AI审核结果完成");
+        } catch (Exception e) {
+            log.error("获取AI商品审核结果异常", e);
+            return R.fail("获取AI商品审核结果异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理视频审核结果
+     * 查询所有待处理的视频审核任务,拉取审核结果并更新商品状态
+     * @return 处理结果
+     */
+    @ApiOperation("处理所有待处理的视频审核任务")
+    @GetMapping("/processVideoModerationResult")
+    public R<String> processVideoModerationResult() {
+        log.info("SecondGoodsController.processVideoModerationResult");
+        try {
+            String result = videoModerationService.processAllPendingVideoTasks();
+            return R.data(result, "视频审核结果处理完成");
+        } catch (Exception e) {
+            log.error("处理视频审核结果异常", e);
+            return R.fail("处理视频审核结果异常: " + e.getMessage());
+        }
+    }
 }

+ 13 - 1
alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java

@@ -1,6 +1,5 @@
 package shop.alien.second.service;
 
-import org.springframework.stereotype.Service;
 import shop.alien.entity.SecondVideoTask;
 import shop.alien.entity.second.SecondGoods;
 import shop.alien.entity.second.vo.SecondGoodsVo;
@@ -82,5 +81,18 @@ public interface SecondGoodsAuditService {
     boolean isVideoUrl(String url);
 
     boolean performSecondRoundReview(SecondGoods goods, SecondGoodsVo goodsDTO);
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    String getAiGoodsCheckResult();
+
+    /**
+     * 商品审核通过后的上架处理(包含风控检查)
+     * @param goods 商品信息
+     */
+    void handleGoodsApprovalSuccess(SecondGoods goods);
 }
 

+ 6 - 0
alien-second/src/main/java/shop/alien/second/service/SecondGoodsService.java

@@ -217,4 +217,10 @@ public interface SecondGoodsService extends IService<SecondGoods> {
      * @return 是否处理成功
      */
     boolean batchShelveGoodsByRiskControlRecord(Integer ruleType, String businessId);
+
+    /**
+     * 执行商品发布风控检查
+     * @param goods 商品信息
+     */
+    void performPublishRiskCheck(SecondGoods goods);
 }

+ 6 - 0
alien-second/src/main/java/shop/alien/second/service/VideoModerationService.java

@@ -32,4 +32,10 @@ public interface VideoModerationService  extends IService<SecondVideoTask> {
      * @return 视频审核任务
      */
     SecondVideoTask getTaskByTaskId(String taskId);
+
+    /**
+     * 处理所有待处理的视频审核任务
+     * @return 处理结果
+     */
+    String processAllPendingVideoTasks();
 }

+ 224 - 1
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java

@@ -8,7 +8,9 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import shop.alien.entity.SecondVideoTask;
@@ -64,6 +66,12 @@ public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
     private boolean videoModerationBlockOnFailure;
 
     /**
+     * AI审核结果查询接口地址
+     */
+    @Value("${audit.result-url:http://192.168.2.250:9100/ai/auto-review/api/v1/audit_task/getResult}")
+    private String aiAuditResultUrl;
+
+    /**
      * 视频审核服务,用于处理商品中视频内容的审核
      */
     private final VideoModerationService videoModerationService;
@@ -105,6 +113,12 @@ public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
 
     private final AiTaskUtils aiTaskUtil;
 
+    @Lazy
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+
+
     /**
      * 执行内容审核
      * @param goodsDTO 商品信息
@@ -178,7 +192,8 @@ public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
             }
 
             // 创建AI任务
-            String taskId = aiTaskUtil.createTask(accessToken, goods.getDescription(), goodsDTO.getImgUrl());
+            String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+            String taskId = aiTaskUtil.createTask(accessToken, test, goodsDTO.getImgUrl());
             if (StringUtils.isEmpty(taskId)) {
                 log.warn("Failed to create AI task for second round review, goods id={}", goods.getId());
                 return false;
@@ -406,6 +421,43 @@ public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
                 operationName = "首次发布";
             }
             operationRecordService.recordGoodsOperation(goods, operationName);
+
+            // 审核通过
+//                goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
+//                goods.setFailedReason("");
+//                goods.setReleaseTime(new Date());
+//                updateById(goods);
+//
+//                // 更新审核记录
+//                createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+//
+//                // 发送审核成功消息
+//                sendMessage(goods);
+//                // 审核成功,记录操作历史
+//                // 审核成功,记录操作历史
+//                String operationName = "";
+//                QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
+//                queryRecordWrapper.eq("goods_id", goods.getId());
+//                log.info("查询操作记录开始 goods_id: {}", goods.getId());
+//                List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
+//                log.info("查询操作记录结束 recordList: {}", recordList);
+//                if (CollectionUtil.isNotEmpty(recordList)){
+//                    operationName = "重新发布";
+//                }else {
+//                    operationName = "首次发布";
+//                }
+//                recordGoodsOperation(goods, operationName);
+//
+//                // 检查用户是否在24小时内发布同类商品超过阈值
+//                if (!checkUserPublishSameCategoryLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
+//                }
+//
+//                // 检查用户是否在24小时内发布商品超过阈值
+//                if (!checkUserPublishLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
+//
+//                }
         } catch (Exception e) {
             log.error("商品上架过程中发生异常,执行回滚", e);
             // 如果通知已发送但后续操作失败,需要补偿
@@ -542,5 +594,176 @@ public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
         
         return reasonBuilder.toString();
     }
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    @Override
+    public String getAiGoodsCheckResult() {
+        // 获取AI服务Token
+        String accessToken = aiTaskUtil.getAccessToken();
+        if (!StringUtils.hasText(accessToken)) {
+            log.error("调用AI服务登录接口失败,无法获取token");
+            return "调用AI服务登录接口失败";
+        }
+
+        // 查询所有状态为处理中的任务
+        List<SecondAiTask> pendingTasks = secondAiTaskMapper.selectList(
+                new QueryWrapper<SecondAiTask>().eq("status", "PROCESSING")
+        );
+
+        if (CollectionUtil.isEmpty(pendingTasks)) {
+            log.info("没有处理中的AI审核任务");
+            return "没有处理中的AI审核任务";
+        }
+
+        int passCount = 0;
+        int rejectCount = 0;
+        int pendingCount = 0;
+        int failCount = 0;
+
+        for (SecondAiTask task : pendingTasks) {
+            String result = processAiTaskResult(task, accessToken);
+            switch (result) {
+                case "success": passCount++; break;
+                case "reject": rejectCount++; break;
+                case "pending": pendingCount++; break;
+                default: failCount++;
+            }
+        }
+
+        return String.format("AI审核结果处理完成,通过:%d,拒绝:%d,处理中:%d,异常:%d", passCount, rejectCount, pendingCount, failCount);
+    }
+
+    /**
+     * 处理单个AI任务结果
+     * @param task AI任务
+     * @param accessToken 访问令牌
+     * @return success-审核通过, reject-审核拒绝, pending-处理中, fail-处理失败
+     */
+    private String processAiTaskResult(SecondAiTask task, String accessToken) {
+        String resultUrl = aiAuditResultUrl + "?task_id=" + task.getTaskId();
+        try {
+            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
+            headers.set("Authorization", "Bearer " + accessToken);
+
+            org.springframework.web.client.RestTemplate restTemplate = new org.springframework.web.client.RestTemplate();
+            org.springframework.http.ResponseEntity<String> response = restTemplate.exchange(
+                    resultUrl, org.springframework.http.HttpMethod.GET,
+                    new org.springframework.http.HttpEntity<>(headers), String.class);
+
+            if (response.getStatusCodeValue() != 200 || response.getBody() == null) {
+                log.error("调用AI审核结果接口失败,taskId: {}, http状态: {}", task.getTaskId(), response.getStatusCode());
+                return "fail";
+            }
+
+            JSONObject dataJson = JSONObject.parseObject(response.getBody()).getJSONObject("data");
+            if (dataJson == null) {
+                log.error("AI审核返回数据为空,taskId: {}", task.getTaskId());
+                return "fail";
+            }
+
+            String status = dataJson.getString("status");
+            // 任务仍在处理中
+            if ("pending".equals(status)) {
+                return "pending";
+            }
+
+            // 任务已完成
+            if ("done".equals(status)) {
+                // 更新任务状态和结果
+                task.setStatus("SUCCESS");
+                task.setResult(dataJson.toJSONString());
+                task.setUpdateTime(new Date());
+                secondAiTaskMapper.updateById(task);
+
+                // 查询关联商品
+                SecondGoods goods = secondGoodsMapper.selectOne(
+                        new QueryWrapper<SecondGoods>().eq("ai_task_id", task.getTaskId()));
+                if (goods == null) {
+                    log.error("商品不存在,taskId: {}", task.getTaskId());
+                    return "fail";
+                }
+
+                String result = dataJson.getString("result");
+                if ("pass".equals(result)) {
+                    // 审核通过,上架商品
+                    log.info("AI审核通过,商品ID: {}, taskId: {}", goods.getId(), task.getTaskId());
+                    handleGoodsApprovalSuccess(goods);
+                    return "success";
+                } else if ("reject".equals(result) || "suspect".equals(result)) {
+                    // 审核拒绝,处理失败流程
+                    String reason = dataJson.getString("reason");
+                    String violation = dataJson.getString("violation");
+                    String ruleHit = dataJson.getString("rule_hit");
+
+                    // 构建失败原因
+                    StringBuilder failReason = new StringBuilder("AI审核不通过");
+                    if (StringUtils.hasText(reason)) {
+                        failReason.append(":").append(reason);
+                    }
+                    if (StringUtils.hasText(violation)) {
+                        failReason.append(",违规类型:").append(violation);
+                    }
+                    if (StringUtils.hasText(ruleHit)) {
+                        failReason.append(",命中规则:").append(ruleHit);
+                    }
+
+                    log.info("AI审核拒绝,商品ID: {}, taskId: {}, 原因: {}", goods.getId(), task.getTaskId(), failReason);
+
+                    // 更新商品状态为审核失败
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                    goods.setFailedReason(failReason.toString());
+                    secondGoodsMapper.updateById(goods);
+
+                    // 创建审核失败记录
+                    createGoodsAudit(goods, failReason.toString(), Constants.AuditStatus.FAILED);
+
+                    // 记录操作历史
+                    operationRecordService.recordGoodsOperation(goods, "AI审核失败");
+
+                    // 发送审核失败消息通知
+                    notificationService.sendFailedMsg(goods);
+
+                    return "reject";
+                }
+            }
+            return "fail";
+        } catch (Exception e) {
+            log.error("处理AI审核任务结果异常,taskId: {}", task.getTaskId(), e);
+            return "fail";
+        }
+    }
+
+    /**
+     * 商品审核通过后的上架处理(包含风控检查)
+     * @param goods 商品信息
+     */
+    @Override
+    public void handleGoodsApprovalSuccess(SecondGoods goods) {
+        // 审核通过,设置上架状态
+        goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
+        goods.setFailedReason("");
+        goods.setReleaseTime(new Date());
+        secondGoodsMapper.updateById(goods);
+
+        // 更新审核记录
+        createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+
+        // 发送审核成功消息
+        notificationService.sendMessage(goods);
+
+        // 记录操作历史
+        QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
+        queryRecordWrapper.eq("goods_id", goods.getId());
+        List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
+        String operationName = CollectionUtil.isNotEmpty(recordList) ? "重新发布" : "首次发布";
+        operationRecordService.recordGoodsOperation(goods, operationName);
+
+        // 执行风控检查
+        secondGoodsService.performPublishRiskCheck(goods);
+    }
 }
 

+ 18 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java

@@ -21,6 +21,7 @@ import shop.alien.mapper.*;
 import shop.alien.mapper.second.SecondGoodsRecordMapper;
 import shop.alien.second.feign.AlienStoreFeign;
 import shop.alien.second.service.SecondGoodsReportingService;
+import shop.alien.second.util.AiUserViolationUtils;
 import shop.alien.second.util.JsonUtils;
 import shop.alien.util.common.Constants;
 import shop.alien.util.common.EnumUtil;
@@ -63,6 +64,8 @@ public class SecondGoodsReportingServiceImpl implements SecondGoodsReportingServ
     @Autowired
     private final StoreImgMapper storeImgMapper;
 
+    private final AiUserViolationUtils aiUserViolationUtils;
+
     @Override
     public SecondReportingVo queryReportingDetail (Integer id) {
         SecondReportingVo secondReportingVo = new SecondReportingVo();
@@ -194,6 +197,21 @@ public class SecondGoodsReportingServiceImpl implements SecondGoodsReportingServ
             }
             int result = lifeUserViolationMapper.insert(lifeuserViolation);
             if (result > 0) {
+
+                if ("5".equals(lifeuserViolation.getReportContextType())) {
+                    // AI审核
+                    //登录获取token
+                    String token = aiUserViolationUtils.getAccessToken();
+                    //调用AI接口
+                    String taskId = aiUserViolationUtils.createTask(token, lifeuserViolation);
+                    if (org.springframework.util.StringUtils.isEmpty(taskId)) {
+                        log.warn("Failed to create AI task for second round review, lifeuserViolation id={}", lifeuserViolation.getId());
+                        return 0;
+                    }
+                    lifeuserViolation.setAiTaskId(taskId);
+                    lifeUserViolationMapper.updateById(lifeuserViolation);
+                }
+
                 if (lifeuserViolation.getReportContextType().equals("4") || lifeuserViolation.getReportContextType().equals("5")) {
                     String phoneId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
                     // 举报通知

+ 79 - 41
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
+import com.alipay.api.domain.GoodsVO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -750,7 +751,22 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
         return true;
     }
 
+    /**
+     * 执行商品发布风控检查
+     * @param goods 商品信息
+     */
+    @Override
+    public void performPublishRiskCheck(SecondGoods goods) {
+        // 检查用户是否在24小时内发布同类商品超过阈值
+        if (!checkUserPublishSameCategoryLimit(goods)) {
+            log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
+        }
 
+        // 检查用户是否在24小时内发布商品超过阈值
+        if (!checkUserPublishLimit(goods)) {
+            log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
+        }
+    }
 
     /**
      * 执行内容审核
@@ -777,6 +793,7 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
             recordGoodsOperation(goods,"文本审核失败");
             return;
         }
+
         // 视频审核
         List<String> taskIds = performVideoReviews(goods, goodsDTO);
 
@@ -792,10 +809,16 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
 //            recordGoodsOperation(goods);
             return;
         }
+
+        List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+        if (videoUrls.isEmpty()) {
+            // ai 审核
+            secondGoodsAuditService.performSecondRoundReview(goods, goodsDTO);
+            return;
+        }
+
         // 审核通过后上架商品
-//        approveAndListGoods(goods);
-        // 开始第二轮审核
-        boolean b = secondGoodsAuditService.performSecondRoundReview(goods, goodsDTO);
+        approveAndListGoods(goods);
 
         // 检查用户是否在24小时内发布同类商品超过阈值
         if (!checkUserPublishSameCategoryLimit(goods)) {
@@ -1709,51 +1732,66 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
             
             // 根据审核结果更新商品状态
             if ("none".equals(task.getRiskLevel())) {
-                // 审核通过
-                goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
-                goods.setFailedReason("");
-                goods.setReleaseTime(new Date());
-                updateById(goods);
-                
-                // 更新审核记录
-                createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
-                
-                // 发送审核成功消息
-                sendMessage(goods);
-                // 审核成功,记录操作历史
-                // 审核成功,记录操作历史
-                String operationName = "";
-                QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
-                queryRecordWrapper.eq("goods_id", goods.getId());
-                log.info("查询操作记录开始 goods_id: {}", goods.getId());
-                List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
-                log.info("查询操作记录结束 recordList: {}", recordList);
-                if (CollectionUtil.isNotEmpty(recordList)){
-                    operationName = "重新发布";
-                }else {
-                    operationName = "首次发布";
-                }
-                recordGoodsOperation(goods, operationName);
-
-                // 检查用户是否在24小时内发布同类商品超过阈值
-                if (!checkUserPublishSameCategoryLimit(goods)) {
-                    log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
-                }
-
-                // 检查用户是否在24小时内发布商品超过阈值
-                if (!checkUserPublishLimit(goods)) {
-                    log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
-
-                }
+                log.warn("视频审核通过,任务ID: {}", task.getTaskId());
+                SecondGoodsVo secondGoodsVo = new SecondGoodsVo();
+                BeanUtils.copyProperties(goods, secondGoodsVo);
+                QueryWrapper<StoreImg> imgQueryWrapper = new QueryWrapper<>();
+                imgQueryWrapper.eq("store_id", goods.getId());
+                imgQueryWrapper.eq("img_type", 18);
+                List<StoreImg> storeImgs = storeImgMapper.selectList(imgQueryWrapper);
+                List<String> imgUrls = storeImgs.stream()
+                        .map(StoreImg::getImgUrl)
+                        .filter(imgUrl -> StringUtils.hasText(imgUrl))
+                        .collect(Collectors.toList());
+                secondGoodsVo.setImgUrl(imgUrls);
+
+                secondGoodsAuditService.performSecondRoundReview(goods, secondGoodsVo);
+//
+//                // 审核通过
+//                goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
+//                goods.setFailedReason("");
+//                goods.setReleaseTime(new Date());
+//                updateById(goods);
+//
+//                // 更新审核记录
+//                createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+//
+//                // 发送审核成功消息
+//                sendMessage(goods);
+//                // 审核成功,记录操作历史
+//                // 审核成功,记录操作历史
+//                String operationName = "";
+//                QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
+//                queryRecordWrapper.eq("goods_id", goods.getId());
+//                log.info("查询操作记录开始 goods_id: {}", goods.getId());
+//                List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
+//                log.info("查询操作记录结束 recordList: {}", recordList);
+//                if (CollectionUtil.isNotEmpty(recordList)){
+//                    operationName = "重新发布";
+//                }else {
+//                    operationName = "首次发布";
+//                }
+//                recordGoodsOperation(goods, operationName);
+//
+//                // 检查用户是否在24小时内发布同类商品超过阈值
+//                if (!checkUserPublishSameCategoryLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
+//                }
+//
+//                // 检查用户是否在24小时内发布商品超过阈值
+//                if (!checkUserPublishLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
+//
+//                }
             } else {
                 // 审核不通过
                 goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
-                
+
                 // 解析审核结果,生成具体的失败原因
                 String failedReason = parseVideoModerationFailureReason(task);
                 goods.setFailedReason(failedReason);
                 updateById(goods);
-                
+
                 // 更新审核记录
                 createGoodsAudit(goods, failedReason, Constants.AuditStatus.FAILED);
 

+ 72 - 1
alien-second/src/main/java/shop/alien/second/service/impl/VideoModerationServiceImpl.java

@@ -6,13 +6,17 @@ import com.aliyun.green20220302.models.VideoModerationResultResponse;
 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.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import shop.alien.entity.SecondVideoTask;
 import shop.alien.mapper.system.SecondVideoTaskMapper;
+import shop.alien.second.service.SecondGoodsService;
 import shop.alien.second.service.VideoModerationService;
 import shop.alien.util.common.safe.video.VideoModerationUtil;
 
 import java.util.Date;
+import java.util.List;
 import java.util.UUID;
 
 /**
@@ -34,6 +38,13 @@ public class VideoModerationServiceImpl extends ServiceImpl<SecondVideoTaskMappe
     private final SecondVideoTaskMapper videoModerationTaskMapper;
 
     /**
+     * 二手商品服务(使用@Lazy避免循环依赖)
+     */
+    @Lazy
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+    /**
      * 提交视频审核任务
      *
      * @param videoUrl 视频URL
@@ -178,7 +189,7 @@ public class VideoModerationServiceImpl extends ServiceImpl<SecondVideoTaskMappe
                     return false;
                 } else {
                     // 审核失败
-                    videoModerationTaskMapper.updateTaskStatus(task.getTaskId(), "FAILED");
+                    videoModerationTaskMapper.updateTaskStatus(task.getTaskId(), "PROCESSING");
                     log.error("视频审核任务处理失败,任务ID: {},错误信息: {}", task.getTaskId(), response.getBody().getMessage());
                     return false;
                 }
@@ -201,4 +212,64 @@ public class VideoModerationServiceImpl extends ServiceImpl<SecondVideoTaskMappe
             return false;
         }
     }
+
+    /**
+     * 处理所有待处理的视频审核任务
+     * @return 处理结果
+     */
+    @Override
+    public String processAllPendingVideoTasks() {
+        try {
+            log.info("开始执行视频审核结果处理");
+
+            // 查询待处理的审核任务(状态为SUBMITTED或PROCESSING)
+            List<SecondVideoTask> pendingTasks = videoModerationTaskMapper.selectPendingTasks();
+
+            if (pendingTasks.isEmpty()) {
+                log.info("没有待处理的视频审核任务");
+                return "没有待处理的视频审核任务";
+            }
+
+            log.info("共找到 {} 个待处理的视频审核任务", pendingTasks.size());
+
+            int successCount = 0;
+            int processingCount = 0;
+            int failCount = 0;
+
+            for (SecondVideoTask task : pendingTasks) {
+                try {
+                    // 1. 拉取视频审核结果
+                    boolean success = processTask(task);
+                    if (success) {
+                        successCount++;
+                        // 2. 视频审核完成后,更新商品业务状态
+                        log.info("TaskId:开始处理商品状态 {}", task.getTaskId());
+                        SecondVideoTask updatedTask = videoModerationTaskMapper.selectByTaskId(task.getTaskId());
+                        if (updatedTask != null) {
+                            secondGoodsService.processVideoModerationResult(updatedTask);
+                        }
+                        log.info("TaskId:商品状态处理完成 {}", task.getTaskId());
+                    } else {
+                        // 检查任务状态判断是处理中还是失败
+                        SecondVideoTask updatedTask = videoModerationTaskMapper.selectByTaskId(task.getTaskId());
+                        if (updatedTask != null && "PROCESSING".equals(updatedTask.getStatus())) {
+                            processingCount++;
+                        } else {
+                            failCount++;
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("处理视频审核任务时发生异常,任务ID: {}", task.getTaskId(), e);
+                    failCount++;
+                }
+            }
+
+            String result = String.format("视频审核结果处理完成,成功:%d,处理中:%d,失败:%d", successCount, processingCount, failCount);
+            log.info(result);
+            return result;
+        } catch (Exception e) {
+            log.error("执行视频审核结果处理任务时发生异常", e);
+            return "执行异常: " + e.getMessage();
+        }
+    }
 }

+ 9 - 2
alien-second/src/main/java/shop/alien/second/util/AiTaskUtils.java

@@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.map.HashedMap;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.http.*;
 import org.springframework.stereotype.Component;
 import org.springframework.util.LinkedMultiValueMap;
@@ -20,12 +21,13 @@ import java.util.Map;
 @Slf4j
 @Component
 @RequiredArgsConstructor
+@RefreshScope
 public class AiTaskUtils {
 
     private final RestTemplate restTemplate;
 
 //    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
-    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
+//    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
 
     @Value("${third-party-user-name.base-url:UdUser}")
     private String userName;
@@ -33,8 +35,13 @@ public class AiTaskUtils {
     @Value("${third-party-pass-word.base-url:123456}")
     private String passWord;
 
-    private String auditTaskUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/audit_task/product";
 
+    @Value("${third-party-login.base-url:http://192.168.2.250:9100/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl;
+
+    @Value("${audit.auditTaskUrl:http://192.168.2.250:9100/ai/auto-review/api/v1/audit_task/product}")
+    private String auditTaskUrl;
+//    private String auditTaskUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/audit_task/product";
 
 
     private String auditTaskResultUrl = "http://192.168.2.250:9000/ai/task-core/api/v1/audit_task/getResult";

+ 272 - 0
alien-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java

@@ -0,0 +1,272 @@
+package shop.alien.second.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeMessage;
+import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.entity.store.vo.LifeUserVo;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.mapper.LifeUserMapper;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiUserViolationUtils {
+
+    private final RestTemplate restTemplate;
+    private final LifeUserMapper lifeUserMapper;
+    private final LifeMessageMapper lifeMessageMapper;
+
+    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl;
+
+    @Value("${third-party-user-name.base-url:UdUser}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url:123456}")
+    private String passWord;
+
+    @Value("${third-party-userComplaintRecordUrl.base-url:http://192.168.2.250:9000/ai/auto-review/api/v1/user_complaint_record/submit}")
+    private String userComplaintRecordUrl;
+
+
+    /**
+     * 登录 AI 服务,获取 token
+     *
+     * @return accessToken
+     */
+    public String getAccessToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        ResponseEntity<String> response;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+            return null;
+        }
+
+        if (response != null && response.getStatusCode() == HttpStatus.OK) {
+            String body = response.getBody();
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t{}", body);
+            if (StringUtils.hasText(body)) {
+                JSONObject jsonObject = JSONObject.parseObject(body);
+                if (jsonObject != null) {
+                    JSONObject dataJson = jsonObject.getJSONObject("data");
+                    if (dataJson != null) {
+                        return dataJson.getString("access_token");
+                    }
+                }
+            }
+            log.warn("AI服务登录响应解析失败 body: {}", body);
+            return null;
+        }
+
+        log.error("请求AI服务 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        return null;
+    }
+
+
+    /**
+     * 调用AI服务创建任务
+     *
+     * @return accessToken
+     */
+    public String createTask(String accessToken, LifeUserViolation lifeuserViolation) {
+        log.info("创建Ai服务任务...{}", userComplaintRecordUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+
+        Map<String, Object> analyzeRequest = new HashedMap<>();
+        // 对应参数
+        if (1 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "用户违规");
+        } else if (2 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "色情低俗");
+        } else if (3 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "违法违规");
+        } else if (4 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "谩骂嘲讽、煽动对立");
+        } else if (5 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "涉嫌诈骗");
+        } else if (6 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "人身攻击");
+        } else if (7 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "种族歧视");
+        } else if (8 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "政治敏感");
+        } else if (9 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "虚假、不实内容");
+        } else if (10 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "违反公德秩序");
+        } else if (11 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "危害人身安全");
+        } else if (12 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "网络暴力");
+        } else {
+            analyzeRequest.put("complaint_type", "其他");
+        }
+
+        analyzeRequest.put("reporter_user_id", lifeuserViolation.getReportingUserId());
+        analyzeRequest.put("reported_user_id", lifeuserViolation.getReportedUserId());
+
+        //举报人
+        String reporterUserId = lifeuserViolation.getReportingUserId();
+        //被举报人
+        String reportedUserId = lifeuserViolation.getReportedUserId();
+
+        //通过双方userId查询用户信息,查出用户电话,
+        QueryWrapper<LifeUserVo> reporterUserQueryWrapper = new QueryWrapper<>();
+        reporterUserQueryWrapper.eq("id", reporterUserId);
+        LifeUserVo reporterUserById = lifeUserMapper.getUserById(reporterUserQueryWrapper);
+        QueryWrapper<LifeUserVo> reportedUserQueryWrapper = new QueryWrapper<>();
+        reportedUserQueryWrapper.eq("id", reportedUserId);
+        LifeUserVo reportedUserById = lifeUserMapper.getUserById(reportedUserQueryWrapper);
+        // 用user_去拼接,查询life_message表,获取双方两天记录
+        String userReporterUserById = "user_" + reporterUserById.getUserPhone();
+        String userReportedUserById = "user_" + reportedUserById.getUserPhone();
+
+        //将双方两天记录拼接成text,传入analyzeRequest
+        // 获取双方最近的聊天记录
+        List<LifeMessage> chatMessages = getRecentChatMessages(userReporterUserById, userReportedUserById);
+
+        // 将聊天记录转换为文本格式
+        String text = convertMessagesToText(chatMessages);
+        analyzeRequest.put("conversation_context", StringUtils.hasText(text) ? text : "");
+
+//        analyzeRequest.put("text", StringUtils.hasText(text) ? text : "");
+//        analyzeRequest.put("img_urls", img_urls);
+
+        HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+        ResponseEntity<String> analyzeResp = null;
+        try {
+            analyzeResp = restTemplate.postForEntity(userComplaintRecordUrl, analyzeEntity, String.class);
+        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+            log.error("调用提交用户投诉审核任务接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+            return  null;
+        } catch (Exception e) {
+            log.error("调用提交用户投诉审核任务接口异常", e);
+            return  null;
+        }
+
+        if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+            String analyzeBody = analyzeResp.getBody();
+            log.info("提交用户投诉审核任务提交成功, 返回: {}", analyzeBody);
+
+            JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+            JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+            if (dataJsonObj == null) {
+                log.error("提交用户投诉审核任务返回数据为空");
+                R.fail("提交用户投诉审核任务返回数据为空");
+                return  null;
+            }
+
+            // 获取record_id用于后续查询
+            String taskId = dataJsonObj.getString("task_id");
+            if (taskId == null) {
+                log.error("提交用户投诉审核任务返回record_id为空");
+                R.fail("提交用户投诉审核任务返回record_id为空");
+                return  null;
+            }
+            return taskId;
+        } else {
+            if (analyzeResp != null) {
+                log.error("调用提交用户投诉审核任务接口失败, http状态: {}", analyzeResp.getStatusCode());
+                R.fail("调用提交用户投诉审核任务接口失败, http状态: " + analyzeResp.getStatusCode());
+                return  null;
+            }
+        }
+        return  null;
+    }
+
+    /**
+     * 获取双方用户的最近聊天记录
+     * @param userReporterUserById 举报人ID
+     * @param userReportedUserById 被举报人ID
+     * @return 聊天记录列表
+     */
+    public List<LifeMessage> getRecentChatMessages(String userReporterUserById, String userReportedUserById) {
+        // 构建查询条件
+        QueryWrapper<LifeMessage> queryWrapper = new QueryWrapper<>();
+
+        // 查询双方的聊天记录
+        queryWrapper.and(wrapper -> wrapper
+                        .eq("sender_id", userReporterUserById)
+                        .eq("receiver_id", userReportedUserById))
+                .or(wrapper -> wrapper
+                        .eq("sender_id", userReportedUserById)
+                        .eq("receiver_id", userReporterUserById));
+
+        // 按时间倒序排列
+        queryWrapper.orderByDesc("created_time");
+
+        // 限制50条记录
+        queryWrapper.last("LIMIT 50");
+
+        return lifeMessageMapper.selectList(queryWrapper);
+    }
+
+    /**
+     * 将聊天消息列表转换为文本格式
+     * @param messages 聊天消息列表
+     * @return 格式化后的文本字符串
+     */
+    private String convertMessagesToText(List<LifeMessage> messages) {
+        if (messages == null || messages.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        // 按时间顺序排列(因为数据库查询是倒序的,这里需要重新排序)
+        for (int i = messages.size() - 1; i >= 0; i--) {
+            LifeMessage message = messages.get(i);
+            // 格式:[时间] 发送者ID: 消息内容
+            sb.append("[").append(formatTime(message.getCreatedTime())).append("] ")
+                    .append(message.getSenderId()).append(": ")
+                    .append(message.getContent()).append("\n");
+        }
+
+        return sb.toString().trim();
+    }
+
+    /**
+     * 格式化时间显示
+     * @param date 时间
+     * @return 格式化后的时间字符串
+     */
+    private String formatTime(Date date) {
+        if (date == null) {
+            return "";
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        return sdf.format(date);
+    }
+}

+ 57 - 0
alien-second/src/main/java/shop/alien/second/util/JsonUtils.java

@@ -31,4 +31,61 @@ public class JsonUtils {
             throw new RuntimeException("解析JSON失败", e);
         }
     }
+
+
+    /**
+     * 从图片对象中获取URL(支持多种可能的字段名)
+     * 例如:从 banner_image 或 vertical_image 对象中获取 url 或 imgUrl
+     *
+     * @param jsonString JSON字符串
+     * @param imageObjectKey 图片对象的key,如 "banner_image" 或 "vertical_image"
+     * @return 图片URL字符串,如果不存在则返回null
+     */
+    public static String getImageUrlFromObject(String jsonString, String imageObjectKey) {
+        try {
+            JsonNode jsonNode = objectMapper.readTree(jsonString);
+            JsonNode dataNode = jsonNode.get("data");
+            if (dataNode == null) {
+                return null;
+            }
+            
+            JsonNode imageNode = dataNode.get(imageObjectKey);
+            if (imageNode == null || !imageNode.isObject()) {
+                return null;
+            }
+            
+            // 尝试常见的URL字段名
+            String[] possibleUrlFields = {"url", "imgUrl", "imageUrl", "img_url", "image_url"};
+            for (String field : possibleUrlFields) {
+                JsonNode urlNode = imageNode.get(field);
+                if (urlNode != null && urlNode.isTextual()) {
+                    return urlNode.asText();
+                }
+            }
+            
+            return null;
+        } catch (Exception e) {
+            throw new RuntimeException("解析图片URL失败,对象key: " + imageObjectKey, e);
+        }
+    }
+
+    /**
+     * 获取banner图片URL
+     *
+     * @param jsonString JSON字符串
+     * @return banner图片URL
+     */
+    public static String getBannerImageUrl(String jsonString) {
+        return getImageUrlFromObject(jsonString, "banner_image");
+    }
+
+    /**
+     * 获取竖版图片URL
+     *
+     * @param jsonString JSON字符串
+     * @return 竖版图片URL
+     */
+    public static String getVerticalImageUrl(String jsonString) {
+        return getImageUrlFromObject(jsonString, "vertical_image");
+    }
 }

+ 5 - 0
alien-store-platform/pom.xml

@@ -275,6 +275,11 @@
             <artifactId>hutool-captcha</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 14 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/OperationalActivityController.java

@@ -12,6 +12,9 @@ import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
 import shop.alien.storeplatform.service.OperationalActivityService;
 
 import java.util.List;
+import java.util.Optional;
+
+import static shop.alien.storeplatform.service.impl.OperationalActivityServiceImpl.failureReasonHolder;
 
 /**
  * 运营活动管理控制器
@@ -36,11 +39,20 @@ public class OperationalActivityController {
     public R<String> createActivity(@RequestBody StoreOperationalActivityDTO dto) {
         log.info("OperationalActivityController.createActivity: dto={}", dto);
         try {
+            log.warn("用户输入的图片描述{}",dto.getImgDescribe());
             int result = activityService.createActivity(dto);
-            if (result > 0) {
+            if (1 == result) {
                 return R.success("活动创建成功");
+            } else if (2 == result) {
+                try {
+                    return R.fail("活动创建失败,原因:审核未通过"+
+                            Optional.ofNullable(failureReasonHolder.get()));
+                }finally {
+                    failureReasonHolder.remove();
+                }
+            }else {
+                return R.fail("活动创建失败");
             }
-            return R.fail("活动创建失败");
         } catch (Exception e) {
             log.error("OperationalActivityController.createActivity ERROR: {}", e.getMessage(), e);
             return R.fail(e.getMessage());

+ 59 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/feign/AlienAIFeign.java

@@ -0,0 +1,59 @@
+package shop.alien.storeplatform.feign;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.http.MediaType;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.*;
+
+@FeignClient(url = "${feign.alienAI.url}", name = "alien-AI")
+public interface AlienAIFeign {
+
+    /**
+     * 登录接口 - 获取access_token
+     *
+     * @param username 用户名
+     * @param password 密码
+     * @return JsonNode 响应体,包含access_token
+     */
+    @PostMapping(value = "/ai/user-auth-core/api/v1/auth/login",
+            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode login(@RequestBody MultiValueMap<String, String> formData);
+
+    /**
+     * 使用 JsonNode 灵活调用接口 - 生成促销图片
+     *
+     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+     * @param requestBody JsonNode 请求体,可以灵活构建
+     * @return JsonNode 响应体,可以灵活解析
+     */
+    @PostMapping(value = "/ai/life-manager/api/v1/promotion_image/generate",
+                 consumes = MediaType.APPLICATION_JSON_VALUE,
+                 produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode generatePromotionImage(@RequestHeader("Authorization") String authorization, @RequestBody JsonNode requestBody);
+
+    /**
+     * 使用 JsonNode 灵活调用接口 - 发起AI审核(多模态)
+     *
+     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+     * @param requestBody JsonNode 请求体,可以灵活构建
+     * @return JsonNode 响应体,可以灵活解析
+     */
+    @PostMapping(value = "/ai/auto-review/api/v1/multimodal_audit_task/submit",
+            consumes = MediaType.APPLICATION_JSON_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode multiModelAudit(@RequestHeader("Authorization") String authorization, @RequestBody JsonNode requestBody);
+
+
+    /**
+     * 获取 AI审核(多模态) 结果
+     *
+     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+     * @param taskId 任务ID
+     * @return JsonNode 响应体,包含审核结果
+     */
+    @GetMapping(value = "/ai/auto-review/api/v1/multimodal_audit_task/getResult",
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode getMultiModelAuditResult(@RequestHeader("Authorization") String authorization, @RequestParam("task_id") String taskId);
+}

+ 167 - 33
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java

@@ -1,36 +1,39 @@
 package shop.alien.storeplatform.service.impl;
 
-import com.alibaba.excel.util.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RBucket;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
 import shop.alien.entity.store.LifeDiscountCoupon;
-import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.StoreImg;
-import shop.alien.entity.store.StoreUser;
 import shop.alien.entity.storePlatform.StoreOperationalActivity;
 import shop.alien.entity.storePlatform.vo.StoreOperationalActivityDTO;
 import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
 import shop.alien.mapper.LifeDiscountCouponMapper;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
+import shop.alien.storeplatform.feign.AlienAIFeign;
 import shop.alien.storeplatform.service.OperationalActivityService;
 
 import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.concurrent.TimeUnit;
+
 
 /**
  * 运营活动服务实现类
@@ -55,13 +58,41 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
     private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
 
+    private final AlienAIFeign alienAIFeign;
+
+    private final RedissonClient redissonClient;
+
+    @Value("${ai.aiAccount}")
+    private String aiAccount;
+
+    @Value("${ai.aiPassword}")
+    private String aiPassword;
+
+    @Value("${ai.token-timeout:3}")
+    private Integer aiTokenTimeout;
+
+    // AI平台token Redis key
+    private static final String AI_TOKEN_KEY = "ai:platform:token";
+
+    ObjectMapper objectMapper = new ObjectMapper();
+
+    public static final ThreadLocal<String> failureReasonHolder = new ThreadLocal<>();
+
+    // AI参数模板
+    String tpl = "活动名称:%s\n"
+            + "活动时间:%s - %s,格式化时间为yyyy-mm-dd类型\n"
+            + "用户可参与次数:%s\n"
+            + "活动规则:%s\n"
+            + "优惠券发放数量:%s\n"
+            + "图片描述:%s";
+
     @Override
     public int createActivity(StoreOperationalActivityDTO dto) {
         log.info("OperationalActivityServiceImpl.createActivity: dto={}", dto);
-        
+
         StoreOperationalActivity activity = new StoreOperationalActivity();
         BeanUtils.copyProperties(dto, activity);
-        
+
         // 设置默认值
         if (activity.getParticipationLimit() == null) {
             activity.setParticipationLimit(0);
@@ -69,8 +100,82 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         if (activity.getStatus() == null) {
             activity.setStatus(1);
         }
-        Integer result = activityMapper.insert(activity);
-        if (result > 0) {
+        Integer result =0;
+            try {
+                String accessToken = getToken();
+                if (accessToken == null || accessToken.isEmpty()) {
+                    log.error("获取AI服务access_token失败,无法生成促销图片");
+                } else {
+                    // AI登录成功
+                    // 先调用AI进行运营名称和图片的审核,同步调用
+                    String authorization = "Bearer " + accessToken;
+                    JsonNode audioResponse = alienAIFeign.multiModelAudit(authorization, dto.getAuditParam());
+                    // 如果审核失败,不进行文字生成海报图片,提前短路。
+                    if (audioResponse.has("data")) {
+                        String taskId = audioResponse.get("data").get("task_id").asText();
+                        JsonNode audioResResponse = alienAIFeign.getMultiModelAuditResult(authorization, taskId);
+                        String status=audioResResponse.get("data").get("status").asText();
+                        for (int i = 0; i < 60; i++) { // AI审核接口速度还在优化中 todo
+                            if (status.equals("completed")||status.equals("failed")) {
+                                break;
+                            }
+                            audioResResponse = alienAIFeign.getMultiModelAuditResult(authorization, taskId);
+                            status = audioResResponse.get("data").get("status").asText();
+                            Thread.sleep(1000);
+                        }
+                        String auditRes = audioResResponse.get("data").get("audit_result").asText();
+                        if (!status.equals("failed")&&auditRes != null && auditRes.equals("compliant")) {
+                            activity.setStatus(8);
+                            result = activityMapper.insert(activity);
+                        } else {
+                            String failureReason = audioResResponse.get("data").get("failure_reason").asText();
+                            failureReasonHolder.set(failureReason);
+                            // 审核不成功 不生成任何记录,返回原因
+                            return 2;
+                        }
+                    }
+                    // 使用用户描述和页面输入框其他信息,让AI生成海报图片。
+                    if (dto.getUploadImgType() == 2) {
+                        // 格式化输入AI参数
+                        ObjectNode requestBody = objectMapper.createObjectNode();
+                        String filled = String.format(
+                                tpl,
+                                dto.getActivityName(),
+                                dto.getStartTime(),
+                                dto.getEndTime(),
+                                dto.getParticipationLimit(),
+                                dto.getActivityRule(),
+                                dto.getCouponQuantity(),
+                                dto.getImgDescribe()
+                        );
+                        requestBody.put("text", filled);
+                        JsonNode imgResponse = alienAIFeign.generatePromotionImage(authorization, requestBody);
+                        // 解析响应
+                        if (imgResponse.has("data")) {
+                            JsonNode data = imgResponse.get("data");
+                            // 提取横向图(banner_image)的图片URL
+                            if (data.has("banner_image")) {
+                                JsonNode bannerImage = data.get("banner_image");
+                                if (bannerImage.has("image_url") && !bannerImage.get("image_url").isNull()) {
+                                    String bannerImageUrl = bannerImage.get("image_url").asText();
+                                    dto.getActivityTitleImg().setImgUrl(bannerImageUrl);
+                                }
+                            }
+                            // 提取竖向图(vertical_image)的图片URL
+                            if (data.has("vertical_image")) {
+                                JsonNode verticalImage = data.get("vertical_image");
+                                if (verticalImage.has("image_url") && !verticalImage.get("image_url").isNull()) {
+                                    String verticalImageUrl = verticalImage.get("image_url").asText();
+                                    dto.getActivityDetailImg().setImgUrl(verticalImageUrl);
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // AI调用失败,也可以添加数据
+                log.error("调用AI服务生成促销图片失败", e);
+            }
             dto.getActivityTitleImg().setBusinessId(activity.getId());
             dto.getActivityTitleImg().setImgType(26);
             imgMapper.insert(dto.getActivityTitleImg());
@@ -78,19 +183,49 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
             dto.getActivityDetailImg().setBusinessId(activity.getId());
             dto.getActivityDetailImg().setImgType(27);
             imgMapper.insert(dto.getActivityDetailImg());
-        }
-        
         return result;
     }
 
+    /**
+     * 登录AI平台,获取token
+     */
+    private String getToken() {
+        // 1. 先从缓存获取
+        RBucket<String> tokenBucket = redissonClient.getBucket(AI_TOKEN_KEY);
+        String cachedToken = tokenBucket.get();
+        if (cachedToken != null && !cachedToken.isEmpty()) {
+            log.debug("从缓存获取 AI token");
+            return cachedToken;
+        }
+        // 2. 缓存未命中,调用登录接口
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", aiAccount);
+        formData.add("password", aiPassword);
+        JsonNode loginResponse = alienAIFeign.login(formData);
+        String accessToken = null;
+        if (loginResponse != null && loginResponse.has("data")) {
+            JsonNode data = loginResponse.get("data");
+            if (data.has("access_token")) {
+                accessToken = data.get("access_token").asText();
+            }
+        }
+        // 3. 如果获取成功,存入缓存
+        if (accessToken != null && !accessToken.isEmpty()) {
+            tokenBucket.set(accessToken, aiTokenTimeout, TimeUnit.SECONDS);
+        } else {
+            log.error("获取 AI token 失败");
+        }
+        return accessToken;
+    }
+
     @Override
     public int updateActivity(StoreOperationalActivityDTO dto) {
         log.info("OperationalActivityServiceImpl.updateActivity: dto={}", dto);
-        
+
         if (dto.getId() == null) {
             throw new IllegalArgumentException("活动ID不能为空");
         }
-        
+
         StoreOperationalActivity activity = new StoreOperationalActivity();
         BeanUtils.copyProperties(dto, activity);
         Integer result = activityMapper.updateById(activity);
@@ -105,7 +240,6 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
             imgMapper.update(null, wrapper);
 
 
-
             // 插入新图片
             StoreImg activityTitleImg = new StoreImg();
             activityTitleImg.setStoreId(dto.getStoreId());
@@ -130,11 +264,11 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
     @Override
     public int deleteActivity(Integer id) {
         log.info("OperationalActivityServiceImpl.deleteActivity: id={}", id);
-        
+
         if (id == null) {
             throw new IllegalArgumentException("活动ID不能为空");
         }
-        
+
         // 逻辑删除
         return activityMapper.deleteById(id);
     }
@@ -142,11 +276,11 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
     @Override
     public StoreOperationalActivityVO queryActivityById(Integer id) {
         log.info("OperationalActivityServiceImpl.getActivityById: id={}", id);
-        
+
         if (id == null) {
             throw new IllegalArgumentException("活动ID不能为空");
         }
-        
+
         StoreOperationalActivity activity = activityMapper.selectById(id);
 
         if (activity == null) {
@@ -172,7 +306,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         } else if (activity.getStatus() == 7) {
             vo.setStatusName("已结束");
         }
-        
+
         // 设置优惠券名称(判空处理)
         if (activity.getCouponId() != null) {
             LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
@@ -206,19 +340,19 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
     @Override
     public IPage<StoreOperationalActivityVO> queryActivityList(Integer storeId, Integer status, String activityName, Integer pageNum, Integer pageSize) {
-        log.info("OperationalActivityServiceImpl.queryActivityList: storeId={}, status={}, activityName={}, pageNum={}, pageSize={}", 
+        log.info("OperationalActivityServiceImpl.queryActivityList: storeId={}, status={}, activityName={}, pageNum={}, pageSize={}",
                 storeId, status, activityName, pageNum, pageSize);
-        
+
         LambdaQueryWrapper<StoreOperationalActivity> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(storeId != null, StoreOperationalActivity::getStoreId, storeId);
         wrapper.like(activityName != null && activityName != "", StoreOperationalActivity::getActivityName, activityName);
         wrapper.eq(status != null, StoreOperationalActivity::getStatus, status);
 
         IPage<StoreOperationalActivity> list = activityMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);
-        
+
         // 将list复制到vo
         List<StoreOperationalActivityVO> voRecords = new ArrayList<>();
-        
+
         for (StoreOperationalActivity activity : list.getRecords()) {
             // 创建实体类
             StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
@@ -244,11 +378,11 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
             voRecords.add(vo);
         }
-        
+
         // 创建分页结果对象
         Page<StoreOperationalActivityVO> voList = new Page<>(list.getCurrent(), list.getSize(), list.getTotal());
         voList.setRecords(voRecords);
-        
+
         return voList;
     }
 
@@ -258,7 +392,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
         Date now = new Date();
         System.out.println("当前时间: " + sdf.format(now));
-        
+
         // 获取当天零点零分零秒时间
         Calendar todayStart = Calendar.getInstance();
         todayStart.setTime(now);
@@ -268,7 +402,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         todayStart.set(Calendar.MILLISECOND, 0);
         Date todayZero = todayStart.getTime();
         System.out.println("当天零点时间: " + sdf.format(todayZero));
-        
+
         // now + 1天(零点)
         Calendar calendarPlus = Calendar.getInstance();
         calendarPlus.setTime(now);
@@ -285,15 +419,15 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
     @Override
     public int updateActivityStatus(Integer id, Integer status) {
         log.info("OperationalActivityServiceImpl.updateActivityStatus: id={}, status={}", id, status);
-        
+
         if (id == null) {
             throw new IllegalArgumentException("活动ID不能为空");
         }
-        
+
         StoreOperationalActivity activity = new StoreOperationalActivity();
         activity.setId(id);
         activity.setStatus(status);
-        
+
         return activityMapper.updateById(activity);
     }
 }

+ 12 - 10
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java

@@ -96,16 +96,18 @@ public class StorePlatformBarMenuServiceImpl extends ServiceImpl<StorePlatformSt
 
         // 查询图片
         List<Integer> ids = list.stream().map(StorePlatformStoreMenu::getId).collect(Collectors.toList());
-        List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
-                .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 7).in(StoreImg::getBusinessId, ids));
-
-        if (CollectionUtil.isNotEmpty(imgList)) {
-            list.forEach(menu -> {
-                imgList.stream()
-                        .filter(img -> img.getBusinessId().equals(menu.getId()))
-                        .findFirst()
-                        .ifPresent(img -> menu.setImgUrl(img.getImgUrl()));
-            });
+        if (CollectionUtil.isNotEmpty(ids)) {
+            List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                    .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 7).in(StoreImg::getBusinessId, ids));
+
+            if (CollectionUtil.isNotEmpty(imgList)) {
+                list.forEach(menu -> {
+                    imgList.stream()
+                            .filter(img -> img.getBusinessId().equals(menu.getId()))
+                            .findFirst()
+                            .ifPresent(img -> menu.setImgUrl(img.getImgUrl()));
+                });
+            }
         }
 
 //        // 查询图片

+ 2 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformLoginServiceImpl.java

@@ -214,10 +214,10 @@ public class StorePlatformLoginServiceImpl extends ServiceImpl<StoreUserMapper,
 
             StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
             if (storeInfo != null) {
-                if (storeInfo.getExpirationTime().compareTo(new Date()) < 0) {
+                if (storeInfo.getExpirationTime() == null || storeInfo.getExpirationTime().compareTo(new Date()) < 0) {
                     jsonObject.put("expirationTime", 0);
                 }
-                if (storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) < 0) {
+                if (storeInfo.getFoodLicenceExpirationTime() == null || storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) < 0) {
                     jsonObject.put("foodLicenceExpirationTime", 0);
                 }
                 if (storeInfo.getEntertainmentLicenceExpirationTime() != null && storeInfo.getEntertainmentLicenceExpirationTime().compareTo(new Date()) < 0) {

+ 6 - 6
alien-store/src/main/java/shop/alien/store/config/WebSocketProcess.java

@@ -81,12 +81,12 @@ public class WebSocketProcess implements ApplicationContextAware {
             }
 
             // 检查消息合规性
-            if (!checkCompliance(webSocketVo)) {
-                webSocketVo.setType("7");
-                webSocketVo.setText("发送内容存在违规行为");
-                sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
-                return;
-            }
+//            if (!checkCompliance(webSocketVo)) {
+//                webSocketVo.setType("7");
+//                webSocketVo.setText("发送内容存在违规行为");
+//                sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
+//                return;
+//            }
 
             // 发送位置信息
             if ("position".equals(webSocketVo.getCategory())) {

+ 189 - 0
alien-store/src/main/java/shop/alien/store/controller/AiAuditController.java

@@ -0,0 +1,189 @@
+package shop.alien.store.controller;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+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 java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = {"ai审核"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/aiAudit")
+@RequiredArgsConstructor
+public class AiAuditController {
+
+    private final RestTemplate restTemplate;
+
+    @Value("${third-party-user-name.base-url}")
+    private String userName;
+
+    @Value("${third-party-user-name-admin.base-url}")
+    private String userNameAdmin;
+
+    @Value("${third-party-pass-word.base-url}")
+    private String passWord;
+
+    @Value("${third-party-login.base-url:http://192.168.2.250:9100/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl;
+
+    @Value("${third-party-text-check.base-url:http://192.168.2.250:9100/ai/auto-review/api/v1/trade_relevance/check}")
+    private String aiTextCheckUrl;
+
+    @Value("${third-party-content_compliance-check.base-url:http://192.168.2.250:9100/ai/auto-review/api/v1/content_compliance/check}")
+    private String aiContentCheckUrl;
+
+    @ApiOperation("ai文本审核")
+    @ApiOperationSupport(order = 1)
+    @GetMapping("/getAiAuditTextCheckResult")
+    public R<JSONObject> getAiAuditTextCheckResult(String text) {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用AI文字审核文本接口 登录接口失败");
+        }
+        return getAiAuditTextCheck(accessToken, text);
+    }
+
+    @ApiOperation("ai文本审核")
+    @ApiOperationSupport(order = 2)
+    @GetMapping("/getAiAuditContentCheckResult")
+    public R<JSONObject> getAiAuditContentCheckResult(String text) {
+        String accessToken = fetchAiServiceAdminToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用AI文字审核内容接口 登录接口失败");
+        }
+        return getAiAuditContentCheck(accessToken, text);
+    }
+
+    private R<JSONObject> getAiAuditTextCheck(String accessToken, String text) {
+        JSONObject data = new JSONObject();
+        data.put("is_trade_related", true);
+
+        // 初始化请求体Map
+        Map<String, Object> requestBody = new HashMap<>();
+        requestBody.put("text", text);
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+        aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(aiTextCheckUrl, request, String.class);
+            if (response.getStatusCodeValue() != 200) {
+                throw new RuntimeException("AI文字审核接口调用失败 http状态:" + response.getStatusCode());
+            }
+            if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotEmpty(response.getBody())) {
+                JSONObject taskObject = JSONObject.parseObject(response.getBody());
+                if (taskObject.getInteger("code") == 200) {
+                    data = taskObject.getJSONObject("data");
+                }
+            }
+        } catch (Exception e) {
+            log.error("调用AI文字审核接口 接口异常------", e);
+        }
+
+        return R.data(data);
+    }
+
+    private R<JSONObject> getAiAuditContentCheck(String accessToken, String text) {
+        JSONObject data = new JSONObject();
+        data.put("is_compliant", true);
+
+        // 初始化请求体Map
+        Map<String, Object> requestBody = new HashMap<>();
+        requestBody.put("text", text);
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+//        aiHeaders.set("Authorization", "Bearer " + accessToken);
+        aiHeaders.set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1cHN0b3JlQGFkbWluLmNvbSIsImlkIjo2LCJ0aW1lIjoxNzYyOTI1NDAzLjY1MTY5MjZ9.07lz8Ox2cGC28UCmqcKCt5R6Rfwtgs-Eiu0ttgWRxws");
+
+        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(aiContentCheckUrl, request, String.class);
+            if (response.getStatusCodeValue() != 200) {
+                throw new RuntimeException("AI文字审核文本接口调用失败 http状态:" + response.getStatusCode());
+            }
+            if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotEmpty(response.getBody())) {
+                JSONObject taskObject = JSONObject.parseObject(response.getBody());
+                if (taskObject.getInteger("code") == 200) {
+                    data = taskObject.getJSONObject("data");
+                }
+            }
+        } catch (Exception e) {
+            log.error("调用AI文本审核接口 接口异常------", e);
+        }
+
+        return R.data(data);
+    }
+
+    private String fetchAiServiceToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+            if (response != null && response.getStatusCodeValue() == 200 && response.getBody() != null) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                return dataJson != null ? dataJson.getString("access_token") : null;
+            }
+            log.error("请求差评申诉辅助系统 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        } catch (Exception e) {
+            log.error("调用差评申诉辅助系统登录接口异常", e);
+        }
+        return null;
+    }
+
+    private String fetchAiServiceAdminToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userNameAdmin);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+            if (response != null && response.getStatusCodeValue() == 200 && response.getBody() != null) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                return dataJson != null ? dataJson.getString("access_token") : null;
+            }
+            log.error("请求差评申诉辅助系统 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        } catch (Exception e) {
+            log.error("调用差评申诉辅助系统登录接口异常", e);
+        }
+        return null;
+    }
+}

+ 63 - 0
alien-store/src/main/java/shop/alien/store/controller/AiUploadController.java

@@ -0,0 +1,63 @@
+package shop.alien.store.controller;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.store.util.ai.AiFeedbackAssignUtils;
+
+@Slf4j
+@Api(tags = {"ai语音识别"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/aiUp")
+@RequiredArgsConstructor
+public class AiUploadController {
+    private final AiFeedbackAssignUtils aiFeedbackAssignUtils;
+    @ApiOperation("AI语音识别")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/GetAIUpload")
+    public R<JSONObject> getAiUpload(
+            @RequestParam("file") MultipartFile file,
+            @RequestParam(value = "language", required = false) String language,
+            @RequestParam(value = "use_itn", required = false) String use_itn,
+            @RequestParam(value = "merge_vad", required = false) String merge_vad) {
+        // 检查文件是否为空
+        if (file == null || file.isEmpty()) {
+            return R.fail("音频文件不能为空");
+        }
+        
+        String accessToken = aiFeedbackAssignUtils.getAccessToken();
+        if (!StringUtils.hasText(accessToken)) {
+          return  R.fail("调用AI语音识别接口 登录接口失败");
+        }
+        
+        try {
+            String speechRecognitionResult = aiFeedbackAssignUtils.getSpeechRecognition1(file, accessToken, language, use_itn, merge_vad);
+            if (speechRecognitionResult == null) {
+                return R.fail("语音识别失败");
+            }
+            // 将返回的JSON字符串解析为JSONObject
+            JSONObject result = JSONObject.parseObject(speechRecognitionResult);
+            return R.data(result);
+        } catch (RuntimeException e) {
+            log.error("语音识别失败", e);
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("语音识别异常", e);
+            return R.fail("语音识别失败: " + e.getMessage());
+        }
+    }
+}

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

@@ -108,9 +108,9 @@ public class AliController {
                         .size();
             }
         }
-        if (size > 0) {
-            return R.fail("该身份证已实名认证过");
-        }
+//        if (size > 0) {
+//            return R.fail("该身份证已实名认证过");
+//        }
         if (aliPayConfig.getIdInfo(name, idCard)) {
             return R.success("身份验证成功");
         }

+ 46 - 0
alien-store/src/main/java/shop/alien/store/controller/BathFacilityServiceController.java

@@ -164,5 +164,51 @@ public class BathFacilityServiceController {
         log.info("BathFacilityServiceController.getCategorySummary?storeId={}", storeId);
         return R.data(facilityServiceService.getCategorySummary(storeId));
     }
+
+
+    @ApiOperation("分页查询洗浴设施及服务列表(商户端)")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true, defaultValue = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "页大小", dataType = "int", paramType = "query", required = true, defaultValue = "10"),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/storePage")
+    public R<IPage<BathFacilityServiceVo>> getStorePageList(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize,
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer facilityCategory) {
+        log.info("BathFacilityServiceController.getStorePageList?pageNum={},pageSize={},storeId={},facilityCategory={}",
+                pageNum, pageSize, storeId, facilityCategory);
+        return R.data(facilityServiceService.getStorePageList(pageNum, pageSize, storeId, facilityCategory));
+    }
+
+    @ApiOperation("根据ID查询洗浴设施及服务详情(商户端)")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/storeDetail")
+    public R<BathFacilityServiceVo> getStoreDetail(@RequestParam Integer id) {
+        log.info("BathFacilityServiceController.getStoreDetail?id={}", id);
+        BathFacilityServiceVo vo = facilityServiceService.getStoreDetail(id);
+        if (vo == null) {
+            return R.fail("数据不存在");
+        }
+        return R.data(vo);
+    }
+
+    @ApiOperation("查询指定店铺按分类汇总的设备信息(包含设备数量、设备列表和图片)(商户端)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/storeCategorySummary")
+    public R<List<BathFacilityServiceCategoryVo>> getStoreCategorySummary(@RequestParam Integer storeId) {
+        log.info("BathFacilityServiceController.getStoreCategorySummary?storeId={}", storeId);
+        return R.data(facilityServiceService.getStoreCategorySummary(storeId));
+    }
 }
 

+ 191 - 0
alien-store/src/main/java/shop/alien/store/controller/DrinkInfoController.java

@@ -0,0 +1,191 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+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.DrinkInfo;
+import shop.alien.entity.store.vo.DrinkInfoVo;
+import shop.alien.store.service.DrinkInfoService;
+
+import java.util.List;
+
+/**
+ * 酒水餐食信息 Controller
+ * 基于 store_menu 表实现(dish_menu_type: 1=餐食,2=酒水)
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+@Slf4j
+@Api(tags = {"酒水餐食管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/drink")
+@RequiredArgsConstructor
+public class DrinkInfoController {
+
+    private final DrinkInfoService drinkInfoService;
+
+    @ApiOperation("分页查询酒水餐食列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "type", value = "类型(1:餐食,2:酒水)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "category", value = "分类", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名称关键词", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createUserId", value = "创建人ID(只查询自己创建的)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<DrinkInfoVo>> getDrinkInfoPage(
+            @RequestParam("page") int page,
+            @RequestParam("size") int size,
+            @RequestParam(value = "storeId", required = false) Integer storeId,
+            @RequestParam(value = "type", required = false) Integer type,
+            @RequestParam(value = "category", required = false) String category,
+            @RequestParam(value = "name", required = false) String name,
+            @RequestParam(value = "createUserId", required = false) Integer createUserId) {
+        log.info("DrinkInfoController.getDrinkInfoPage?page={}, size={}, storeId={}, type={}, category={}, name={}, createUserId={}", 
+                page, size, storeId, type, category, name, createUserId);
+        IPage<DrinkInfoVo> drinkInfoPage = drinkInfoService.getDrinkInfoPage(page, size, storeId, type, category, name, createUserId);
+        return R.data(drinkInfoPage);
+    }
+
+    @ApiOperation("根据ID查询酒水餐食详情")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<DrinkInfoVo> getDrinkInfoById(@PathVariable("id") Integer id) {
+        log.info("DrinkInfoController.getDrinkInfoById?id={}", id);
+        DrinkInfoVo drinkInfoVo = drinkInfoService.getDrinkInfoById(id);
+        if (drinkInfoVo == null) {
+            return R.fail("未找到该酒水餐食信息");
+        }
+        return R.data(drinkInfoVo);
+    }
+
+    @ApiOperation("新增酒水餐食")
+    @ApiOperationSupport(order = 3)
+    @PostMapping
+    public R<String> saveDrinkInfo(@RequestBody DrinkInfo drinkInfo) {
+        log.info("DrinkInfoController.saveDrinkInfo?drinkInfo={}", drinkInfo);
+        boolean result = drinkInfoService.saveDrinkInfo(drinkInfo);
+        if (result) {
+            return R.success("新增成功");
+        }
+        return R.fail("新增失败");
+    }
+
+    @ApiOperation("修改酒水餐食")
+    @ApiOperationSupport(order = 4)
+    @PutMapping
+    public R<String> updateDrinkInfo(@RequestBody DrinkInfo drinkInfo) {
+        log.info("DrinkInfoController.updateDrinkInfo?drinkInfo={}", drinkInfo);
+        boolean result = drinkInfoService.updateDrinkInfo(drinkInfo);
+        if (result) {
+            return R.success("修改成功");
+        }
+        return R.fail("修改失败");
+    }
+
+    @ApiOperation("删除酒水餐食")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteDrinkInfo(@PathVariable("id") Integer id) {
+        log.info("DrinkInfoController.deleteDrinkInfo?id={}", id);
+        boolean result = drinkInfoService.deleteDrinkInfo(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("设置酒水餐食推荐")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "isRecommended", value = "是否推荐(0:否, 1:是)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/recommended")
+    public R<String> updateIsRecommended(
+            @PathVariable("id") Integer id,
+            @RequestParam("isRecommended") Integer isRecommended) {
+        log.info("DrinkInfoController.updateIsRecommended?id={}, isRecommended={}", id, isRecommended);
+        boolean result = drinkInfoService.updateIsRecommended(id, isRecommended);
+        if (result) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("更新酒水餐食排序")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "sort", value = "排序值", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/sort")
+    public R<String> updateSort(
+            @PathVariable("id") Integer id,
+            @RequestParam("sort") Integer sort) {
+        log.info("DrinkInfoController.updateSort?id={}, sort={}", id, sort);
+        boolean result = drinkInfoService.updateSort(id, sort);
+        if (result) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("根据门店ID查询所有酒水餐食")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "类型(枚举值:酒水/餐食)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/listByStoreId")
+    public R<List<DrinkInfoVo>> getDrinkInfoListByStoreId(
+            @RequestParam("storeId") Integer storeId,
+            @RequestParam(value = "type", required = false) Integer type) {
+        log.info("DrinkInfoController.getDrinkInfoListByStoreId?storeId={}, type={}", storeId, type);
+        List<DrinkInfoVo> drinkInfoList = drinkInfoService.getDrinkInfoListByStoreId(storeId, type);
+        return R.data(drinkInfoList);
+    }
+
+    @ApiOperation("查询推荐酒水餐食列表")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createUserId", value = "创建人ID(只查询自己创建的)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/recommended")
+    public R<List<DrinkInfoVo>> getRecommendedList(
+            @RequestParam(value = "storeId", required = false) Integer storeId,
+            @RequestParam(value = "createUserId", required = false) Integer createUserId) {
+        log.info("DrinkInfoController.getRecommendedList?storeId={}, createUserId={}", storeId, createUserId);
+        List<DrinkInfoVo> drinkInfoList = drinkInfoService.getRecommendedList(storeId, createUserId);
+        return R.data(drinkInfoList);
+    }
+
+    @ApiOperation("批量更新酒水餐食排序")
+    @ApiOperationSupport(order = 11)
+    @PostMapping("/batchSort")
+    public R<String> batchUpdateSort(@RequestBody List<DrinkInfo> drinkInfoList) {
+        log.info("DrinkInfoController.batchUpdateSort?size={}", drinkInfoList.size());
+        boolean result = drinkInfoService.batchUpdateSort(drinkInfoList);
+        if (result) {
+            return R.success("批量更新排序成功");
+        }
+        return R.fail("批量更新排序失败");
+    }
+
+}

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

@@ -165,9 +165,9 @@ public class LifeDiscountCouponStoreFriendController {
     @ApiImplicitParams({@ApiImplicitParam(name = "storeId", value = "当前登录店铺id", dataType = "String", paramType = "query", required = true)
     })
     @GetMapping("/getRuleList")
-    private R<List<LifeDiscountCouponFriendRuleVo>> getRuleList(@RequestParam(value = "storeId") String storeId) {
-        log.info("LifeDiscountCouponStoreFriendController.getRuleList?storeId={}", storeId);
-        return R.data(lifeDiscountCouponStoreFriendService.getRuleList(storeId));
+    private R<List<LifeDiscountCouponFriendRuleVo>> getRuleList(@RequestParam(value = "storeId") String storeId, String acName, String status) {
+        log.info("LifeDiscountCouponStoreFriendController.getRuleList?storeId={},name={},status={}", storeId, acName, status);
+        return R.data(lifeDiscountCouponStoreFriendService.getRuleList(storeId, acName, status));
     }
 
     @ApiOperation("查询赠券记录")

+ 117 - 0
alien-store/src/main/java/shop/alien/store/controller/LifeFeedbackController.java

@@ -0,0 +1,117 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+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.dto.LifeFeedbackDto;
+import shop.alien.entity.store.dto.UserReplyDto;
+import shop.alien.entity.store.dto.*;
+import shop.alien.entity.store.vo.LifeFeedbackDetailVo;
+import shop.alien.entity.store.vo.LifeFeedbackListVo;
+import shop.alien.entity.store.vo.LifeFeedbackVo;
+import shop.alien.store.service.LifeFeedbackService;
+
+/**
+ * 意见反馈 Controller
+ */
+@Api(tags = {"意见反馈模块"})
+@Slf4j
+@CrossOrigin
+@RestController
+@RequestMapping("/feedback")
+@RequiredArgsConstructor
+public class LifeFeedbackController {
+
+    private final LifeFeedbackService lifeFeedbackService;
+
+    @ApiOperation(value = "提交反馈", httpMethod = "POST")
+    @PostMapping("/submit")
+    public R<String> submitFeedback(@RequestBody LifeFeedbackDto dto) {
+        log.info("LifeFeedbackController.submitFeedback, dto={}", dto);
+        return lifeFeedbackService.submitFeedback(dto);
+    }
+
+    @ApiOperation(value = "查询用户历史反馈列表", httpMethod = "GET")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "feedbackSource", value = "反馈来源:0-用户端,1-商家端", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "每页数量", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/list")
+    public R<IPage<LifeFeedbackVo>> getFeedbackList(
+            @RequestParam("userId") Integer userId,
+            @RequestParam("feedbackSource") Integer feedbackSource,
+            @RequestParam(value = "page", defaultValue = "1") int page,
+            @RequestParam(value = "size", defaultValue = "10") int size) {
+        log.info("LifeFeedbackController.getFeedbackList, userId={}, feedbackSource={}, page={}, size={}", 
+                userId, feedbackSource, page, size);
+        IPage<LifeFeedbackVo> result = lifeFeedbackService.getFeedbackList(userId, feedbackSource, page, size);
+        return R.data(result);
+    }
+
+    @ApiOperation(value = "查询反馈详情", httpMethod = "GET")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "feedbackId", value = "反馈ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<LifeFeedbackVo> getFeedbackDetail(@RequestParam("feedbackId") Integer feedbackId) {
+        log.info("LifeFeedbackController.getFeedbackDetail, feedbackId={}", feedbackId);
+        return lifeFeedbackService.getFeedbackDetail(feedbackId);
+    }
+
+    @ApiOperation(value = "用户回复", httpMethod = "POST")
+    @PostMapping("/userReply")
+    public R<String> userReply(@RequestBody UserReplyDto dto) {
+        log.info("LifeFeedbackController.userReply, dto={}", dto);
+        return lifeFeedbackService.userReply(dto);
+    }
+
+    // ==================== 中台接口 ====================
+
+    @ApiOperation(value = "中台-查询意见反馈列表", httpMethod = "GET")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "feedbackType", value = "反馈类型:0-优化建议,1-问题", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "handleStatus", value = "处理状态:0-处理中,1-已解决,2-未分配,3-无需解决", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "feedbackSource", value = "反馈来源:0-用户端,1-商家端", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "feedbackWay", value = "反馈方式:0-用户反馈,1-AI识别", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "每页数量", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/platform/list")
+    public R<IPage<LifeFeedbackListVo>> getWebFeedbackList(LifeFeedbackQueryDto queryDto) {
+        log.info("LifeFeedbackController.getWebFeedbackList, queryDto={}", queryDto);
+        return lifeFeedbackService.getWebFeedbackList(queryDto);
+    }
+
+    @ApiOperation(value = "中台-查询反馈详情", httpMethod = "GET")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "feedbackId", value = "反馈ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/platform/detail")
+    public R<LifeFeedbackDetailVo> getWebFeedbackDetail(@RequestParam("feedbackId") Integer feedbackId) {
+        log.info("LifeFeedbackController.getWebFeedbackDetail, feedbackId={}", feedbackId);
+        return lifeFeedbackService.getWebFeedbackDetail(feedbackId);
+    }
+
+    @ApiOperation(value = "中台-回复用户", httpMethod = "POST")
+    @PostMapping("/platform/reply")
+    public R<String> webReplyUser(@RequestBody LifeFeedbackReplyWebDto replyDto) {
+        log.info("LifeFeedbackController.webReplyUser, replyDto={}", replyDto);
+        return lifeFeedbackService.webReplyUser(replyDto);
+    }
+
+    @ApiOperation(value = "中台-更新处理状态", httpMethod = "POST")
+    @PostMapping("/platform/updateStatus")
+    public R<String> updateWebFeedbackStatus(@RequestBody LifeFeedbackStatusDto statusDto) {
+        log.info("LifeFeedbackController.updateWebFeedbackStatus, statusDto={}", statusDto);
+        return lifeFeedbackService.updateWebFeedbackStatus(statusDto);
+    }
+}
+

+ 99 - 0
alien-store/src/main/java/shop/alien/store/controller/OperationalActivityController.java

@@ -0,0 +1,99 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+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.storePlatform.vo.StoreOperationalActivityVO;
+import shop.alien.store.service.OperationalActivityService;
+
+/**
+ * 运营活动管理控制器
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Slf4j
+@Api(tags = {"商家端-运营活动管理"})
+@ApiSort(10)
+@CrossOrigin
+@RestController
+@RequestMapping("/operationalActivity")
+@RequiredArgsConstructor
+public class OperationalActivityController {
+
+    private final OperationalActivityService activityService;
+
+    @ApiOperation("根据活动ID获取详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParam(name = "id", value = "活动ID", dataTypeClass = Integer.class, paramType = "path", required = true)
+    @GetMapping("/detail/{id}")
+    public R<StoreOperationalActivityVO> getActivityDetailById(@PathVariable("id") Integer id) {
+        log.info("OperationalActivityController.getActivityDetailById: id={}", id);
+        try {
+            StoreOperationalActivityVO result = activityService.getActivityDetailById(id);
+            return R.data(result);
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("OperationalActivityController.getActivityDetailById ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("分页查看运营活动列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "商户ID", dataTypeClass = Integer.class, paramType = "query"),
+            @ApiImplicitParam(name = "storeName", value = "商户名称(模糊)", dataTypeClass = String.class, paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "当前页", dataTypeClass = Integer.class, paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量", dataTypeClass = Integer.class, paramType = "query")
+    })
+    @GetMapping("/detail")
+    public R<IPage<StoreOperationalActivityVO>> pageActivityDetail(
+            @RequestParam(value = "storeId", required = false) Integer storeId,
+            @RequestParam(value = "storeName", required = false) String storeName,
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize) {
+        log.info("OperationalActivityController.pageActivityDetail storeId={}, storeName={}, pageNum={}, pageSize={}", storeId, storeName, pageNum, pageSize);
+        try {
+            IPage<StoreOperationalActivityVO> result = activityService.pageActivityDetail(storeId, storeName, pageNum, pageSize);
+            return R.data(result);
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("OperationalActivityController.pageActivityDetail ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("运营活动审核处理")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/audit")
+    public R<String> auditActivity(
+            @RequestParam("id") Integer id,
+            @RequestParam("status") Integer status,
+            @RequestParam(value = "approvalComments", required = false) String approvalComments) {
+        log.info("OperationalActivityController.auditActivity: id={}, status={}, approvalComments={}", id, status, approvalComments);
+        if (id == null) {
+            return R.fail("活动ID不能为空");
+        }
+        if (status == null) {
+            return R.fail("审核状态不能为空");
+        }
+        try {
+            int result = activityService.auditActivity(id, status, approvalComments);
+            if (result > 0) {
+                return R.success("审核处理成功");
+            }
+            return R.fail("审核处理失败");
+        } catch (Exception e) {
+            log.error("OperationalActivityController.auditActivity ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+}
+

+ 18 - 0
alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java

@@ -190,5 +190,23 @@ public class SportsEquipmentFacilityController {
         log.info("SportsEquipmentFacilityController.getstoreCategorySummary?storeId={}", storeId);
         return R.data(facilityService.getstoreCategorySummary(storeId));
     }
+
+    @ApiOperation("保存分类下的实景图片(商户端)- 设施列表通过添加接口已保存,此接口仅保存图片")
+    @ApiOperationSupport(order = 10)
+    @PostMapping("/saveCategoryImages")
+    public R<Boolean> saveCategoryImages(@RequestBody SportsEquipmentFacilityCategoryVo vo) {
+        log.info("SportsEquipmentFacilityController.saveCategoryImages?storeId={},facilityCategory={},imageList={}", 
+                vo.getStoreId(), vo.getFacilityCategory(), vo.getImageList());
+        try {
+            boolean result = facilityService.saveCategoryImages(vo.getStoreId(), vo.getFacilityCategory(), vo.getImageList());
+            if (result) {
+                return R.success("保存成功");
+            }
+            return R.fail("保存失败");
+        } catch (Exception e) {
+            log.error("SportsEquipmentFacilityController.saveCategoryImages异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
 }
 

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

@@ -11,7 +11,6 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.result.R;
-import shop.alien.entity.second.SecondGoodsCategory;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.StoreInfoDto;
 import shop.alien.entity.store.vo.*;
@@ -824,7 +823,11 @@ public class StoreInfoController {
     })
     public R<List<StoreCommentSummaryInterest>> getStoreInterestInfo(int storeId) {
         log.info("StoreInfoController.getStoreInterestInfo?storeId={}", storeId);
-        List<StoreCommentSummaryInterest> list = storeCommentSummaryInterestMapper.selectList(new LambdaQueryWrapper<StoreCommentSummaryInterest>().eq(StoreCommentSummaryInterest :: getStoreId, storeId));
+        List<StoreCommentSummaryInterest> list = storeCommentSummaryInterestMapper.selectList(
+                new LambdaQueryWrapper<StoreCommentSummaryInterest>()
+                        .eq(StoreCommentSummaryInterest::getStoreId, storeId)
+                        .last("ORDER BY RAND() LIMIT 1") // 随机排序并只取第一条
+        );
         return R.data(list);
     }
 
@@ -912,6 +915,8 @@ public class StoreInfoController {
             @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeName", value = "店铺名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "storeType", value = "美食3 休闲娱乐1  健身2 ", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
     })
@@ -923,10 +928,12 @@ public class StoreInfoController {
             @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
             @RequestParam(value = "businessType", required = false) Integer businessType,
             @RequestParam(value = "categoryId", required = false) Integer categoryId,
+            @RequestParam(value = "storeName", required = false) String storeName,
+            @RequestParam(value = "storeType", required = false) Integer storeType,
             @RequestParam(defaultValue = "1") int pageNum,
             @RequestParam(defaultValue = "10") int pageSize) {
-        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
-                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},storeName={},storeType{},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, storeName,storeType, pageNum, pageSize);
 
         try {
             // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
@@ -947,7 +954,7 @@ public class StoreInfoController {
             }
 
             // 调用服务层获取筛选结果
-            IPage<StoreInfoVo> result = storeInfoService.getSpecialTypeStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+            IPage<StoreInfoVo> result = storeInfoService.getSpecialTypeStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, storeName,storeType, pageNum, pageSize);
 
             // 记录响应日志
             log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",
@@ -1108,7 +1115,7 @@ public class StoreInfoController {
      * @param pageSize    页容
      * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
      */
-    @ApiOperation("查询种类型店铺(丽人美发、运动健身)并按距离筛选")
+    @ApiOperation("查询种类型店铺(丽人美发、运动健身)并按距离筛选")
     @ApiOperationSupport(order = 16)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query", required = true),
@@ -1117,6 +1124,7 @@ public class StoreInfoController {
             @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeName", value = "商家名称(模糊查询)", dataType = "String", paramType = "query"),
             @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
     })
@@ -1128,10 +1136,11 @@ public class StoreInfoController {
             @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
             @RequestParam(value = "businessType", required = false) Integer businessType,
             @RequestParam(value = "categoryId", required = false) Integer categoryId,
+            @RequestParam(value = "storeName", required = false) String storeName,
             @RequestParam(defaultValue = "1") int pageNum,
             @RequestParam(defaultValue = "10") int pageSize) {
-        log.info("StoreInfoController.getLifeServicesByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
-                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+        log.info("StoreInfoController.getLifeServicesByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},storeName={},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, storeName, pageNum, pageSize);
 
         try {
             // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
@@ -1152,7 +1161,7 @@ public class StoreInfoController {
             }
 
             // 调用服务层获取筛选结果
-            IPage<StoreInfoVo> result = storeInfoService.getLifeServicesByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+            IPage<StoreInfoVo> result = storeInfoService.getLifeServicesByDistance(lon, lat, distance, sortType, businessType, categoryId, storeName, pageNum, pageSize);
 
             // 记录响应日志
             log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",

+ 35 - 0
alien-store/src/main/java/shop/alien/store/feign/AlienAIFeign.java

@@ -0,0 +1,35 @@
+package shop.alien.store.feign;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.http.MediaType;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.*;
+
+@FeignClient(url = "${feign.alienAI.url}", name = "alien-AI")
+public interface AlienAIFeign {
+
+    /**
+     * 登录接口 - 获取access_token
+     *
+     *
+     *
+     * @return JsonNode 响应体,包含access_token
+     */
+    @PostMapping(value = "/ai/user-auth-core/api/v1/auth/login",
+            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode login(@RequestBody MultiValueMap<String, String> formData);
+
+    /**
+     * 使用 JsonNode 灵活调用接口
+     *
+     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+     * @return JsonNode 响应体,可以灵活解析
+     */
+    @PostMapping(value = "/asr/upload",
+            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode getSpeechRecognition (@RequestHeader("Authorization") String authorization, @RequestBody MultiValueMap<String, Object> formData);
+}
+

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

@@ -78,5 +78,11 @@ public interface BathFacilityServiceService extends IService<BathFacilityService
      * @return List<BathFacilityServiceCategoryVo>
      */
     List<BathFacilityServiceCategoryVo> getCategorySummary(Integer storeId);
+
+    IPage<BathFacilityServiceVo> getStorePageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory);
+
+    BathFacilityServiceVo getStoreDetail(Integer id);
+
+    List<BathFacilityServiceCategoryVo> getStoreCategorySummary(Integer storeId);
 }
 

+ 116 - 0
alien-store/src/main/java/shop/alien/store/service/DrinkInfoService.java

@@ -0,0 +1,116 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.store.DrinkInfo;
+import shop.alien.entity.store.vo.DrinkInfoVo;
+
+import java.util.List;
+
+/**
+ * 酒水餐食信息表 服务类
+ * 注意:drink_info 表已废弃,现在基于 store_menu 表实现
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+public interface DrinkInfoService {
+
+    /**
+     * 分页查询酒水餐食列表
+     *
+     * @param page         页码
+     * @param size         页容
+     * @param storeId      门店ID
+     * @param type         类型(1:菜品,2:酒水)
+     * @param category     分类
+     * @param name         名称关键词
+     * @param createUserId 创建人ID(只查询指定用户创建的)
+     * @return IPage<DrinkInfoVo>
+     */
+    IPage<DrinkInfoVo> getDrinkInfoPage(int page, int size, Integer storeId, Integer type, String category, String name, Integer createUserId);
+
+    /**
+     * 根据ID查询酒水餐食详情
+     *
+     * @param id 主键ID
+     * @return DrinkInfoVo
+     */
+    DrinkInfoVo getDrinkInfoById(Integer id);
+
+    /**
+     * 新增酒水餐食
+     *
+     * @param drinkInfo 酒水餐食信息
+     * @return boolean
+     */
+    boolean saveDrinkInfo(DrinkInfo drinkInfo);
+
+    /**
+     * 修改酒水餐食
+     *
+     * @param drinkInfo 酒水餐食信息
+     * @return boolean
+     */
+    boolean updateDrinkInfo(DrinkInfo drinkInfo);
+
+    /**
+     * 删除酒水餐食(软删除)
+     *
+     * @param id 主键ID
+     * @return boolean
+     */
+    boolean deleteDrinkInfo(Integer id);
+
+    /**
+     * 上下架酒水餐食
+     *
+     * @param id     主键ID
+     * @param status 状态(0:下架, 1:上架)
+     * @return boolean
+     */
+    boolean updateStatus(Integer id, Integer status);
+
+    /**
+     * 设置酒水餐食推荐
+     *
+     * @param id            主键ID
+     * @param isRecommended 是否推荐(0:否, 1:是)
+     * @return boolean
+     */
+    boolean updateIsRecommended(Integer id, Integer isRecommended);
+
+    /**
+     * 更新酒水餐食排序
+     *
+     * @param id   主键ID
+     * @param sort 排序值
+     * @return boolean
+     */
+    boolean updateSort(Integer id, Integer sort);
+
+    /**
+     * 根据门店ID查询所有酒水餐食
+     *
+     * @param storeId 门店ID
+     * @param type    类型(1:菜品,2:酒水)
+     * @return List<DrinkInfoVo>
+     */
+    List<DrinkInfoVo> getDrinkInfoListByStoreId(Integer storeId, Integer type);
+
+    /**
+     * 查询推荐酒水餐食列表
+     *
+     * @param storeId      门店ID
+     * @param createUserId 创建人ID(只查询指定用户创建的)
+     * @return List<DrinkInfoVo>
+     */
+    List<DrinkInfoVo> getRecommendedList(Integer storeId, Integer createUserId);
+
+    /**
+     * 批量更新酒水餐食排序
+     *
+     * @param drinkInfoList 酒水餐食列表(包含id和sort字段)
+     * @return boolean
+     */
+    boolean batchUpdateSort(List<DrinkInfo> drinkInfoList);
+}

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

@@ -55,7 +55,7 @@ public interface LifeDiscountCouponStoreFriendService extends IService<LifeDisco
 
     List<LifeDiscountCouponFriendRuleDetailVo> getReceivedFriendCouponList(String storeId,String friendStoreUserId);
 
-    List<LifeDiscountCouponFriendRuleVo> getRuleList(String storeId);
+    List<LifeDiscountCouponFriendRuleVo> getRuleList(String storeId, String acName, String status);
 
     LifeDiscountCouponFriendRuleVo getRuleById(String id);
 

+ 20 - 0
alien-store/src/main/java/shop/alien/store/service/LifeFeedbackReplyService.java

@@ -0,0 +1,20 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LifeFeedbackReply;
+
+import java.util.List;
+
+/**
+ * 反馈回复 Service
+ */
+public interface LifeFeedbackReplyService extends IService<LifeFeedbackReply> {
+
+    /**
+     * 根据反馈ID查询回复列表
+     * @param feedbackId 反馈ID
+     * @return 回复列表
+     */
+    List<LifeFeedbackReply> getByFeedbackId(Integer feedbackId);
+}
+

+ 78 - 0
alien-store/src/main/java/shop/alien/store/service/LifeFeedbackService.java

@@ -0,0 +1,78 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeFeedback;
+import shop.alien.entity.store.dto.*;
+import shop.alien.entity.store.vo.LifeFeedbackDetailVo;
+import shop.alien.entity.store.vo.LifeFeedbackListVo;
+import shop.alien.entity.store.vo.LifeFeedbackVo;
+
+/**
+ * 意见反馈 Service
+ */
+public interface LifeFeedbackService extends IService<LifeFeedback> {
+
+    /**
+     * 提交反馈
+     * @param dto 反馈信息
+     * @return 反馈结果
+     */
+    R<String> submitFeedback(LifeFeedbackDto dto);
+
+    /**
+     * 查询用户历史反馈列表
+     * @param userId 用户ID
+     * @param feedbackSource 反馈来源
+     * @param page 页码
+     * @param size 每页数量
+     * @return 反馈列表
+     */
+    IPage<LifeFeedbackVo> getFeedbackList(Integer userId, Integer feedbackSource, int page, int size);
+
+    /**
+     * 查询反馈详情
+     * @param feedbackId 反馈ID
+     * @return 反馈详情
+     */
+    R<LifeFeedbackVo> getFeedbackDetail(Integer feedbackId);
+
+    /**
+     * 用户回复
+     * @param dto 用户回复信息
+     * @return 回复结果
+     */
+    R<String> userReply(UserReplyDto dto);
+
+    // ==================== 中台接口 ====================
+
+    /**
+     * 中台-查询意见反馈列表
+     * @param queryDto 查询条件
+     * @return 反馈列表
+     */
+    R<IPage<LifeFeedbackListVo>> getWebFeedbackList(LifeFeedbackQueryDto queryDto);
+
+    /**
+     * 中台-查询反馈详情
+     * @param feedbackId 反馈ID
+     * @return 反馈详情
+     */
+    R<LifeFeedbackDetailVo> getWebFeedbackDetail(Integer feedbackId);
+
+    /**
+     * 中台-回复用户
+     * @param replyDto 回复信息
+     * @return 回复结果
+     */
+    R<String> webReplyUser(LifeFeedbackReplyWebDto replyDto);
+
+    /**
+     * 中台-更新反馈处理状态
+     * @param statusDto 状态信息
+     * @return 更新结果
+     */
+    R<String> updateWebFeedbackStatus(LifeFeedbackStatusDto statusDto);
+}
+

+ 47 - 0
alien-store/src/main/java/shop/alien/store/service/LifeImgService.java

@@ -0,0 +1,47 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LifeImg;
+
+import java.util.List;
+
+/**
+ * 反馈图片 Service
+ */
+public interface LifeImgService extends IService<LifeImg> {
+
+    /**
+     * 根据反馈ID查询图片列表
+     * @param feedbackId 反馈ID
+     * @return 图片列表
+     */
+    List<LifeImg> getByFeedbackId(Integer feedbackId);
+
+    /**
+     * 批量保存图片
+     * @param imgList 图片列表
+     * @return 是否成功
+     */
+    boolean batchSave(List<LifeImg> imgList);
+
+    /**
+     * 根据反馈ID删除图片
+     * @param feedbackId 反馈ID
+     * @return 是否成功
+     */
+    boolean removeByFeedbackId(Integer feedbackId);
+
+    /**
+     * 查询反馈的图片URL列表
+     * @param feedbackId 反馈ID
+     * @return 图片URL列表
+     */
+    List<String> getImgUrlsByFeedbackId(Integer feedbackId);
+
+    /**
+     * 查询反馈的视频URL列表
+     * @param feedbackId 反馈ID
+     * @return 视频URL列表
+     */
+    List<String> getVideoUrlsByFeedbackId(Integer feedbackId);
+}

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

@@ -135,7 +135,12 @@ public class LifeUserStoreService {
                 storeMap.put("businessStatusStr", store.getBusinessStatusStr());
 
                 //474bug
+                storeMap.put("businessSection", store.getBusinessSection());
+                storeMap.put("businessSectionName", store.getBusinessSectionName());
+                storeMap.put("businessTypes", store.getBusinessTypes());
                 storeMap.put("businessTypesName", store.getBusinessTypesName());
+                storeMap.put("businessClassify", store.getBusinessClassify());
+                storeMap.put("businessClassifyName", store.getBusinessClassifyName());
                 storeMap.put("storeBusinessInfo", storeBusinessInfo);
                 // 添加门店ID
                 storeMap.put("storeId", store.getId());
@@ -221,6 +226,7 @@ public class LifeUserStoreService {
                 // 如果未找到打卡次数(有图片并且设置为可见的),则设置为0
                 if (null == storeMap.get("storeClockInCountWithCanLook")) {
                     storeMap.put("storeClockInCountWithCanLook", 0);
+                    continue;
                 }
                 // 将当前门店的信息添加到返回结果列表中
                 returnMaps.add(storeMap);

+ 44 - 0
alien-store/src/main/java/shop/alien/store/service/OperationalActivityService.java

@@ -0,0 +1,44 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
+
+/**
+ * 运营活动服务接口
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+public interface OperationalActivityService {
+
+    /**
+     * 审核运营活动
+     *
+     * @param id               活动ID
+     * @param status           审核状态(1通过/0驳回等)
+     * @param approvalComments 驳回原因
+     * @return 更新结果
+     */
+    int auditActivity(Integer id, Integer status, String approvalComments);
+
+    /**
+     * 根据门店条件分页查询运营活动
+     *
+     * @param storeId   门店ID
+     * @param storeName 门店名称(模糊)
+     * @param pageNum   页码
+     * @param pageSize  每页数量
+     * @return 活动分页结果
+     */
+    IPage<StoreOperationalActivityVO> pageActivityDetail(Integer storeId, String storeName, Integer pageNum, Integer pageSize);
+
+    /**
+     * 根据活动ID获取活动详情
+     *
+     * @param id 活动ID
+     * @return 活动详情
+     */
+    StoreOperationalActivityVO getActivityDetailById(Integer id);
+
+}
+

+ 11 - 0
alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java

@@ -82,5 +82,16 @@ public interface SportsEquipmentFacilityService extends IService<SportsEquipment
     SportsEquipmentFacilityVo getStoreDetail(Integer id);
 
     List<SportsEquipmentFacilityCategoryVo> getstoreCategorySummary(Integer storeId);
+
+    /**
+     * 保存分类下的图片
+     * 设施列表通过添加接口已保存,此接口仅保存该分类的实景图片
+     *
+     * @param storeId          门店ID
+     * @param facilityCategory 设施分类(1:有氧区, 2:力量区, 3:单功能机械区)
+     * @param imageList        图片列表
+     * @return boolean
+     */
+    boolean saveCategoryImages(Integer storeId, Integer facilityCategory, List<String> imageList);
 }
 

+ 84 - 59
alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java

@@ -138,6 +138,13 @@ public interface StoreInfoService extends IService<StoreInfo> {
     StoreInfoVo editStoreInfo(StoreInfoDto storeInfoDto);
 
     /**
+     * 新中台web端修改门店及门店用户
+     *
+     * @return ResponseEntity
+     */
+    StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto);
+
+    /**
      * web端删除门店及门店用户
      *
      * @return ResponseEntity
@@ -177,6 +184,11 @@ public interface StoreInfoService extends IService<StoreInfo> {
     StoreInfoVo getStoreDetail(String storeId, String userId, String jingdu, String weidu);
 
     /**
+     * 中台web端查询门店明细
+     */
+    StoreInfoVo getNewStoreDetail(String storeId);
+
+    /**
      * web端审批结果
      */
     void approveStoreInfo(String storeId, Integer approvalStatus, String reason);
@@ -291,6 +303,52 @@ public interface StoreInfoService extends IService<StoreInfo> {
     int foodLicenceType(int id);
 
     /**
+     * 店铺娱乐经营许可证
+     * @param storeImg
+     * @return
+     */
+    int uploadEntertainmentLicence(StoreImg storeImg);
+
+    /**
+     * 获取店铺娱乐经营许可证状态以及店铺状态为审核中合同图片list
+     * @param id
+     * @return
+     */
+    Map<String,Object> getStoreEntertainmentLicenceStatus(int id);
+
+    /**
+     * 娱乐经营许可证审核通过后 图片类型变为审核通过后 img_type26
+     *
+     */
+    int entertainmentLicenceType(int id);
+
+    /**
+     * 获取门店代金券
+     */
+    List<LifeCouponVo> getStoreCouponList(String storeId);
+
+
+    /**
+     * 获取活动banner图
+     */
+    List<StoreImg> getBannerUrl (String storeId);
+    /**
+     * 获取活动banner图
+     */
+
+    List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId);
+
+
+    /**
+     * web-分页查询店铺信息
+     *
+
+     * @return IPage<StoreInfoVo>
+     */
+    List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify);
+
+
+    /**
      * 根据门店及图片地址查询最新OCR识别数据
      *
      * @param imageUrl 图片URL
@@ -298,9 +356,6 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     Map<String, Object> getStoreOcrData(String imageUrl, String merchantName);
 
-    void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
-
-
     /**
      * 查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选
      *
@@ -310,13 +365,17 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
      * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选
      * @param categoryId  分类ID(二级或三级分类的dictId,从getAllBusinessSection接口获取),可选
+     * @param storeName   店铺名称(模糊查询),可选
      * @param pageNum     页码
      * @param pageSize    页容
      * @return IPage<StoreInfoVo> 分页的门店信息列表
      */
-    IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
-
+    IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName,Integer storeType, int pageNum, int pageSize);
 
+    /**
+     * web端查询门店明细
+     */
+    StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu);
 
     /**
      * 获取休闲娱乐分类数据(主分类和子分类)
@@ -337,6 +396,14 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     List<StoreDictionary> getAllBusinessSection(String businessSection);
 
+    /**
+     * 获取门店下三级分类结构
+     * 根据门店ID查询该门店的经营板块、经营种类和分类的三级结构
+     *
+     * @param storeId 门店ID
+     * @return StoreThreeLevelStructureVo 包含门店信息和三级分类树形结构
+     */
+    StoreThreeLevelStructureVo getStoreThreeLevelStructure(Integer storeId);
 
     /**
      * 你可能还喜欢(推荐店铺)
@@ -352,9 +419,16 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat);
 
+    /**
+     * 获取三级分类数据
+     * 根据二级分类ID获取该分类下的三级分类
+     *
+     * @param parentId 二级分类ID
+     * @return List<StoreDictionaryVo> 三级分类列表
+     */
+    List<StoreDictionaryVo> getBusinessClassifyData(Integer parentId);
 
-
-
+    void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
 
 
     /**
@@ -365,60 +439,11 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @param distance    距离范围(单位:公里)
      * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
      * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7),可选,如果指定则只查询该类型
+     * @param categoryId  字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺
+     * @param storeName   商家名称(模糊查询)
      * @param pageNum     页码
      * @param pageSize    页容
      * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
      */
-    IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
-
-    /**
-     * 获取活动banner图
-     */
-    List<StoreImg> getBannerUrl (String storeId);
-    /**
-     * 获取活动详情banner图
-     */
-
-    List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId);
-
-    /**
-     * web-分页查询店铺信息
-     *
-
-     * @return IPage<StoreInfoVo>
-     */
-    List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify);
-
-
-    /**
-     * web端查询门店明细
-     */
-    StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu);
-
-    /**
-     * 获取门店代金券
-     */
-    List<LifeCouponVo> getStoreCouponList(String storeId);
-
-    /**
-     * 中台web端查询门店明细
-     */
-    StoreInfoVo getNewStoreDetail(String storeId);
-
-    /**
-     * 新中台web端修改门店及门店用户
-     *
-     * @return ResponseEntity
-     */
-    StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto);
-
-
-    /**
-     * 根据business_classify字段获取字典表数据实现UI图中的功能
-     *
-     * @param parentId 父分类ID(可选),如果提供则只返回该父分类下的子分类;如果为null则返回所有子分类
-     * @return List<StoreDictionaryVo> 分类列表
-     */
-    List<StoreDictionaryVo> getBusinessClassifyData(Integer parentId);
-
+    IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName, int pageNum, int pageSize);
 }

+ 75 - 0
alien-store/src/main/java/shop/alien/store/service/impl/BathFacilityServiceServiceImpl.java

@@ -254,5 +254,80 @@ public class BathFacilityServiceServiceImpl extends ServiceImpl<BathFacilityServ
         
         return result;
     }
+
+    @Override
+    public IPage<BathFacilityServiceVo> getStorePageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory) {
+        Page<BathFacilityService> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<BathFacilityService> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BathFacilityService::getStoreId, storeId);
+        if (facilityCategory != null) {
+            queryWrapper.eq(BathFacilityService::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByDesc(BathFacilityService::getCreatedTime);
+        IPage<BathFacilityService> facilityServicePage = facilityServiceMapper.selectPage(page, queryWrapper);
+        return facilityServicePage.convert(this::convertToVo);
+    }
+
+    @Override
+    public BathFacilityServiceVo getStoreDetail(Integer id) {
+        BathFacilityService facilityService = facilityServiceMapper.selectById(id);
+        if (facilityService == null) {
+            return null;
+        }
+        return convertToVo(facilityService);
+    }
+
+    @Override
+    public List<BathFacilityServiceCategoryVo> getStoreCategorySummary(Integer storeId) {
+        List<BathFacilityServiceCategoryVo> result = new ArrayList<>();
+
+        // 遍历所有分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)
+        for (int category = 1; category < FACILITY_CATEGORY_NAMES.length; category++) {
+            BathFacilityServiceCategoryVo categoryVo = new BathFacilityServiceCategoryVo();
+            categoryVo.setFacilityCategory(category);
+            categoryVo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+
+            // 查询该分类下的设备列表(不分页)
+            LambdaQueryWrapper<BathFacilityService> facilityWrapper = new LambdaQueryWrapper<>();
+            facilityWrapper.eq(BathFacilityService::getStoreId, storeId)
+                    .eq(BathFacilityService::getFacilityCategory, category);
+            facilityWrapper.orderByDesc(BathFacilityService::getCreatedTime);
+            List<BathFacilityService> facilityServiceList = facilityServiceMapper.selectList(facilityWrapper);
+
+            // 设置设备数量
+            categoryVo.setFacilityCount(facilityServiceList != null ? facilityServiceList.size() : 0);
+
+            // 查询该分类的代表图片(从第一个设备的第一张图片获取)
+            if (!CollectionUtils.isEmpty(facilityServiceList)) {
+                BathFacilityService firstFacility = facilityServiceList.get(0);
+                // 查询该设备的第一张图片
+                LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+                imageWrapper.eq(StoreImg::getStoreId, storeId)
+                        .eq(StoreImg::getBusinessId, firstFacility.getId())
+                        .eq(StoreImg::getImgType, IMG_TYPE_BATH_FACILITY);
+                imageWrapper.orderByAsc(StoreImg::getImgSort);
+                imageWrapper.last("LIMIT 1");
+                StoreImg firstImage = storeImgMapper.selectOne(imageWrapper);
+                if (firstImage != null) {
+                    categoryVo.setCategoryImage(firstImage.getImgUrl());
+                }
+            }
+
+            // 转换为VO列表(包含设备信息和图片)
+            List<BathFacilityServiceVo> facilityVoList = new ArrayList<>();
+            if (!CollectionUtils.isEmpty(facilityServiceList)) {
+                for (BathFacilityService facilityService : facilityServiceList) {
+                    // 使用现有的convertToVo方法,它会自动查询图片
+                    BathFacilityServiceVo vo = convertToVo(facilityService);
+                    facilityVoList.add(vo);
+                }
+            }
+            categoryVo.setFacilityList(facilityVoList);
+
+            result.add(categoryVo);
+        }
+
+        return result;
+    }
 }
 

+ 367 - 0
alien-store/src/main/java/shop/alien/store/service/impl/DrinkInfoServiceImpl.java

@@ -0,0 +1,367 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.DrinkInfo;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.StoreMenu;
+import shop.alien.entity.store.vo.DrinkInfoVo;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.StoreMenuMapper;
+import shop.alien.store.service.DrinkInfoService;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 酒水餐食信息 服务实现类
+ * 基于 store_menu 表实现
+ * dish_menu_type: 1=餐食(菜单/菜品),2=酒水
+ * 注意:drink_info 表已废弃,现在使用 store_menu 表
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class DrinkInfoServiceImpl implements DrinkInfoService {
+
+    private final StoreMenuMapper storeMenuMapper;
+    private final StoreImgMapper storeImgMapper;
+    
+    /**
+     * 菜单类型常量
+     * 1 = 餐食(菜单/菜品)
+     * 2 = 酒水
+     */
+    private static final String MENU_TYPE_FOOD = "1";
+    private static final String MENU_TYPE_DRINK = "2";
+    
+    /**
+     * 菜单图片类型
+     */
+    private static final Integer IMG_TYPE_MENU = 7;
+
+    @Override
+    public IPage<DrinkInfoVo> getDrinkInfoPage(int page, int size, Integer storeId, Integer type, String category, String name, Integer createUserId) {
+        LambdaQueryWrapper<StoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        
+        // type 参数:1=餐食,2=酒水
+        if (type != null) {
+            queryWrapper.eq(StoreMenu::getDishMenuType, type.toString());
+        } else {
+            // 不传则查询所有(餐食和酒水)
+            queryWrapper.in(StoreMenu::getDishMenuType, MENU_TYPE_FOOD, MENU_TYPE_DRINK);
+        }
+        
+        // 筛选条件
+        if (createUserId != null) {
+            queryWrapper.eq(StoreMenu::getCreatedUserId, createUserId);
+        }
+        if (storeId != null) {
+            queryWrapper.eq(StoreMenu::getStoreId, storeId);
+        }
+        if (category != null && !category.isEmpty()) {
+            queryWrapper.eq(StoreMenu::getCategory, category);
+        }
+        if (name != null && !name.isEmpty()) {
+            queryWrapper.like(StoreMenu::getDishName, name);
+        }
+        // 按排序和创建时间倒序
+        queryWrapper.orderByAsc(StoreMenu::getSort);
+        queryWrapper.orderByDesc(StoreMenu::getCreatedTime);
+
+        IPage<StoreMenu> menuPage = storeMenuMapper.selectPage(new Page<>(page, size), queryWrapper);
+        // 转换为Vo
+        IPage<DrinkInfoVo> drinkInfoVoPage = new Page<>(page, size);
+        drinkInfoVoPage.setTotal(menuPage.getTotal());
+        List<DrinkInfoVo> drinkInfoVoList = menuPage.getRecords().stream()
+                .map(this::convertMenuToVo)
+                .collect(Collectors.toList());
+        drinkInfoVoPage.setRecords(drinkInfoVoList);
+
+        return drinkInfoVoPage;
+    }
+
+    @Override
+    public DrinkInfoVo getDrinkInfoById(Integer id) {
+        StoreMenu menu = storeMenuMapper.selectById(id);
+        // 验证是否为餐食或酒水类型
+        if (menu == null || 
+            (!MENU_TYPE_FOOD.equals(menu.getDishMenuType()) && !MENU_TYPE_DRINK.equals(menu.getDishMenuType()))) {
+            return null;
+        }
+        return convertMenuToVo(menu);
+    }
+
+    @Override
+    public boolean saveDrinkInfo(DrinkInfo drinkInfo) {
+        // 转换为 StoreMenu 对象
+        StoreMenu menu = new StoreMenu();
+        menu.setStoreId(drinkInfo.getStoreId());
+        menu.setDishName(drinkInfo.getName());
+        menu.setDishPrice(drinkInfo.getPrice());
+        menu.setCostPrice(drinkInfo.getCostPrice());
+        // type: 1=餐食,2=酒水,默认为酒水
+        menu.setDishMenuType(drinkInfo.getType() != null ? drinkInfo.getType().toString() : MENU_TYPE_DRINK);
+        menu.setCategory(drinkInfo.getCategory());
+        menu.setAlcoholVolume(drinkInfo.getAlcoholVolume() != null ? drinkInfo.getAlcoholVolume().toString() : "0");
+        menu.setFlavor(drinkInfo.getFlavor());
+        menu.setDescription(drinkInfo.getDescription());
+        menu.setDishType(drinkInfo.getIsRecommended() != null ? drinkInfo.getIsRecommended() : 0);
+        menu.setSort(drinkInfo.getSort() != null ? drinkInfo.getSort() : 0);
+        menu.setDishesUnit("份");
+        menu.setLikeCount(0);
+        
+        // 保存菜单
+        int result = storeMenuMapper.insert(menu);
+        
+        // 如果有图片URL,保存到 store_img 表
+        if (result > 0 && StringUtils.hasText(drinkInfo.getPicUrl())) {
+            StoreImg img = new StoreImg();
+            img.setStoreId(menu.getStoreId());
+            img.setImgType(IMG_TYPE_MENU);
+            img.setImgUrl(drinkInfo.getPicUrl());
+            img.setBusinessId(menu.getId());
+            String typeDesc = MENU_TYPE_DRINK.equals(menu.getDishMenuType()) ? "酒水" : "餐食";
+            img.setImgDescription(typeDesc + "图片-" + menu.getDishName());
+            img.setImgSort(1);
+            storeImgMapper.insert(img);
+            
+            // 更新菜单的 img_id
+            menu.setImgId(img.getId());
+            storeMenuMapper.updateById(menu);
+        }
+        
+        return result > 0;
+    }
+
+    @Override
+    public boolean updateDrinkInfo(DrinkInfo drinkInfo) {
+        if (drinkInfo.getId() == null) {
+            return false;
+        }
+        
+        // 验证是否为餐食或酒水类型
+        StoreMenu existMenu = storeMenuMapper.selectById(drinkInfo.getId());
+        if (existMenu == null || 
+            (!MENU_TYPE_FOOD.equals(existMenu.getDishMenuType()) && !MENU_TYPE_DRINK.equals(existMenu.getDishMenuType()))) {
+            log.warn("该商品不是餐食/酒水类型,无法修改");
+            return false;
+        }
+        
+        // 构建更新对象
+        StoreMenu menu = new StoreMenu();
+        menu.setId(drinkInfo.getId());
+        if (StringUtils.hasText(drinkInfo.getName())) {
+            menu.setDishName(drinkInfo.getName());
+        }
+        if (drinkInfo.getPrice() != null) {
+            menu.setDishPrice(drinkInfo.getPrice());
+        }
+        if (drinkInfo.getCostPrice() != null) {
+            menu.setCostPrice(drinkInfo.getCostPrice());
+        }
+        if (StringUtils.hasText(drinkInfo.getCategory())) {
+            menu.setCategory(drinkInfo.getCategory());
+        }
+        if (drinkInfo.getAlcoholVolume() != null) {
+            menu.setAlcoholVolume(drinkInfo.getAlcoholVolume().toString());
+        }
+        if (StringUtils.hasText(drinkInfo.getFlavor())) {
+            menu.setFlavor(drinkInfo.getFlavor());
+        }
+        if (StringUtils.hasText(drinkInfo.getDescription())) {
+            menu.setDescription(drinkInfo.getDescription());
+        }
+        if (drinkInfo.getIsRecommended() != null) {
+            menu.setDishType(drinkInfo.getIsRecommended());
+        }
+        if (drinkInfo.getSort() != null) {
+            menu.setSort(drinkInfo.getSort());
+        }
+        if (drinkInfo.getType() != null) {
+            menu.setDishMenuType(drinkInfo.getType().toString());
+        }
+        
+        // 处理图片更新
+        if (StringUtils.hasText(drinkInfo.getPicUrl())) {
+            // 删除旧图片
+            if (existMenu.getImgId() != null) {
+                storeImgMapper.deleteById(existMenu.getImgId());
+            }
+            
+            // 保存新图片
+            StoreImg img = new StoreImg();
+            img.setStoreId(existMenu.getStoreId());
+            img.setImgType(IMG_TYPE_MENU);
+            img.setImgUrl(drinkInfo.getPicUrl());
+            img.setBusinessId(drinkInfo.getId());
+            String typeDesc = MENU_TYPE_DRINK.equals(existMenu.getDishMenuType()) ? "酒水" : "餐食";
+            String dishName = menu.getDishName() != null ? menu.getDishName() : existMenu.getDishName();
+            img.setImgDescription(typeDesc + "图片-" + dishName);
+            img.setImgSort(1);
+            storeImgMapper.insert(img);
+            
+            menu.setImgId(img.getId());
+        }
+        
+        return storeMenuMapper.updateById(menu) > 0;
+    }
+
+    @Override
+    public boolean deleteDrinkInfo(Integer id) {
+        // 验证是否为餐食或酒水类型
+        StoreMenu menu = storeMenuMapper.selectById(id);
+        if (menu == null || 
+            (!MENU_TYPE_FOOD.equals(menu.getDishMenuType()) && !MENU_TYPE_DRINK.equals(menu.getDishMenuType()))) {
+            log.warn("该商品不是餐食/酒水类型,无法删除");
+            return false;
+        }
+        
+        // 逻辑删除(由 @TableLogic 自动处理)
+        return storeMenuMapper.deleteById(id) > 0;
+    }
+
+    @Override
+    public boolean updateStatus(Integer id, Integer status) {
+        // 注意:store_menu 表有 status 字段了,但 StoreMenu 实体类暂未映射
+        LambdaUpdateWrapper<StoreMenu> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreMenu::getId, id)
+                .in(StoreMenu::getDishMenuType, MENU_TYPE_FOOD, MENU_TYPE_DRINK);  // 允许修改餐食和酒水
+        // TODO: 在 StoreMenu 实体类中添加 status 字段映射后启用
+        // .set(StoreMenu::getStatus, status);
+        return true;  // 暂时返回 true
+    }
+
+    @Override
+    public boolean updateIsRecommended(Integer id, Integer isRecommended) {
+        LambdaUpdateWrapper<StoreMenu> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreMenu::getId, id)
+                .eq(StoreMenu::getDishMenuType, MENU_TYPE_DRINK)  // 只支持酒水
+                .set(StoreMenu::getDishType, isRecommended);
+        return storeMenuMapper.update(null, updateWrapper) > 0;
+    }
+
+    @Override
+    public boolean updateSort(Integer id, Integer sort) {
+        LambdaUpdateWrapper<StoreMenu> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreMenu::getId, id)
+                .eq(StoreMenu::getDishMenuType, MENU_TYPE_DRINK)  // 只支持酒水
+                .set(StoreMenu::getSort, sort);
+        return storeMenuMapper.update(null, updateWrapper) > 0;
+    }
+
+    @Override
+    public List<DrinkInfoVo> getDrinkInfoListByStoreId(Integer storeId, Integer type) {
+        LambdaQueryWrapper<StoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreMenu::getStoreId, storeId);
+        
+        // type 参数:2=酒水,3=餐食
+        if (type != null) {
+            queryWrapper.eq(StoreMenu::getDishMenuType, type.toString());
+        } else {
+            // 不传则查询所有(餐食和酒水)
+            queryWrapper.in(StoreMenu::getDishMenuType, MENU_TYPE_FOOD, MENU_TYPE_DRINK);
+        }
+        
+        queryWrapper.orderByAsc(StoreMenu::getSort);
+        queryWrapper.orderByDesc(StoreMenu::getCreatedTime);
+
+        List<StoreMenu> menuList = storeMenuMapper.selectList(queryWrapper);
+        return menuList.stream()
+                .map(this::convertMenuToVo)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<DrinkInfoVo> getRecommendedList(Integer storeId, Integer createUserId) {
+        LambdaQueryWrapper<StoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(StoreMenu::getDishMenuType, MENU_TYPE_FOOD, MENU_TYPE_DRINK)  // 查询餐食和酒水
+                .eq(StoreMenu::getDishType, 1);  // 推荐
+        
+        // 只查询指定用户创建的商品
+        if (createUserId != null) {
+            queryWrapper.eq(StoreMenu::getCreatedUserId, createUserId);
+        }
+        
+        if (storeId != null) {
+            queryWrapper.eq(StoreMenu::getStoreId, storeId);
+        }
+        queryWrapper.orderByAsc(StoreMenu::getSort);
+        queryWrapper.orderByDesc(StoreMenu::getCreatedTime);
+
+        List<StoreMenu> menuList = storeMenuMapper.selectList(queryWrapper);
+        return menuList.stream()
+                .map(this::convertMenuToVo)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public boolean batchUpdateSort(List<DrinkInfo> drinkInfoList) {
+        if (drinkInfoList == null || drinkInfoList.isEmpty()) {
+            return false;
+        }
+        try {
+            for (DrinkInfo drinkInfo : drinkInfoList) {
+                if (drinkInfo.getId() != null && drinkInfo.getSort() != null) {
+                    LambdaUpdateWrapper<StoreMenu> updateWrapper = new LambdaUpdateWrapper<>();
+                    updateWrapper.eq(StoreMenu::getId, drinkInfo.getId())
+                            .in(StoreMenu::getDishMenuType, MENU_TYPE_FOOD, MENU_TYPE_DRINK)  // 支持餐食和酒水
+                            .set(StoreMenu::getSort, drinkInfo.getSort());
+                    storeMenuMapper.update(null, updateWrapper);
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("批量更新排序失败", e);
+            return false;
+        }
+    }
+    /**
+     * 将 StoreMenu 转换为 DrinkInfoVo
+     *
+     * @param menu StoreMenu对象
+     * @return DrinkInfoVo对象
+     */
+    private DrinkInfoVo convertMenuToVo(StoreMenu menu) {
+        DrinkInfoVo vo = new DrinkInfoVo();
+        vo.setId(menu.getId());
+        vo.setName(menu.getDishName());
+        vo.setPrice(menu.getDishPrice());
+        vo.setCostPrice(menu.getCostPrice());
+        // 转换类型为数字:1=餐食,2=酒水
+        vo.setType(menu.getDishMenuType() != null ? Integer.parseInt(menu.getDishMenuType()) : 2);
+        vo.setCategory(menu.getCategory());
+        vo.setAlcoholVolume(menu.getAlcoholVolume() != null ? new java.math.BigDecimal(menu.getAlcoholVolume()) : java.math.BigDecimal.ZERO);
+        vo.setFlavor(menu.getFlavor());
+        vo.setDescription(menu.getDescription());
+        vo.setIsRecommended(menu.getDishType());
+        vo.setStoreId(menu.getStoreId());
+        vo.setCreateUserId(menu.getCreatedUserId());
+        vo.setUpdateUserId(menu.getUpdatedUserId());
+        vo.setSort(menu.getSort());
+        vo.setStatus(1);  // 默认上架
+        vo.setCreateTime(menu.getCreatedTime());
+        vo.setUpdateTime(menu.getUpdatedTime());
+        
+        // 获取图片URL
+        if (menu.getImgId() != null) {
+            StoreImg img = storeImgMapper.selectById(menu.getImgId());
+            if (img != null) {
+                vo.setPicUrl(img.getImgUrl());
+            }
+        }
+        
+        return vo;
+    }
+}

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

@@ -106,16 +106,16 @@ public class LifeBlacklistServiceImpl extends ServiceImpl<LifeBlacklistMapper, L
                 if ("1".equals(lifeBlacklist.getBlockedType())) {
                     StoreUser storeUser = storeUserMapper.selectById(lifeBlacklist.getBlockedId());
                     StoreImg storeImg = null;
-//                    if (storeUser != null) {
-//                        storeImg = storeImgMapper.selectOne(new LambdaUpdateWrapper<StoreImg>().eq(StoreImg::getStoreId, storeUser.getStoreId()).eq(StoreImg::getImgType, 10));
-//                    }
                     if (storeUser != null) {
-                        lifeBlacklist.setName(storeUser.getNickName());
+                        storeImg = storeImgMapper.selectOne(new LambdaUpdateWrapper<StoreImg>().eq(StoreImg::getStoreId, storeUser.getStoreId()).eq(StoreImg::getImgType, 10));
+                    }
+                    if (storeUser != null) {
+                        lifeBlacklist.setName(storeUser.getName());
                         lifeBlacklist.setPhoneId(storeUser.getPhone());
                         lifeBlacklist.setUserImage(storeUser.getHeadImg());
-//                        if (storeImg != null) {
-//                            lifeBlacklist.setUserImage(storeImg.getImgUrl());
-//                        }
+                        if (storeImg != null) {
+                            lifeBlacklist.setUserImage(storeImg.getImgUrl());
+                        }
                     }
                 } else {
                     LifeUser lifeUser = lifeUserMapper.selectById(lifeBlacklist.getBlockedId());

+ 1 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java

@@ -686,6 +686,7 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
             LifeDiscountCouponVo lifeDiscountCouponVo = new LifeDiscountCouponVo();
             if (null != storeInfoOne) {
                 lifeDiscountCouponVo.setBusinessSection(storeInfoOne.getBusinessSection());
+                lifeDiscountCouponVo.setBusinessSectionName(storeInfoOne.getBusinessSectionName());
             }
             lifeDiscountCouponVo.setCouponId(lifeDiscountCouponOne.getId());
             BeanUtils.copyProperties(lifeDiscountCouponOne, lifeDiscountCouponVo);

+ 7 - 1
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponStoreFriendServiceImpl.java

@@ -471,11 +471,17 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
     }
 
     @Override
-    public List<LifeDiscountCouponFriendRuleVo> getRuleList(String storeId) {
+    public List<LifeDiscountCouponFriendRuleVo> getRuleList(String storeId, String acName, String status) {
         List<LifeDiscountCouponFriendRuleVo> ruleList = lifeDiscountCouponFriendRuleDetailMapper.getRuleList(storeId);
         if (ObjectUtils.isNotEmpty(ruleList)) {
             ruleList.forEach(i -> i.setStatus(i.getEndDate().after(new Date()) ? "0" : "1"));
         }
+        if (StringUtils.isNotEmpty(acName)) {
+            ruleList = ruleList.stream().filter(i -> i.getAcName().contains(acName)).collect(Collectors.toList());
+        }
+        if (StringUtils.isNotEmpty(status)) {
+            ruleList = ruleList.stream().filter(i -> i.getStatus().equals(status)).collect(Collectors.toList());
+        }
         return ruleList;
     }
 

+ 28 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackReplyServiceImpl.java

@@ -0,0 +1,28 @@
+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.stereotype.Service;
+import shop.alien.entity.store.LifeFeedbackReply;
+import shop.alien.mapper.LifeFeedbackReplyMapper;
+import shop.alien.store.service.LifeFeedbackReplyService;
+
+import java.util.List;
+
+/**
+ * 反馈回复 Service实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LifeFeedbackReplyServiceImpl extends ServiceImpl<LifeFeedbackReplyMapper, LifeFeedbackReply> implements LifeFeedbackReplyService {
+
+    private final LifeFeedbackReplyMapper lifeFeedbackReplyMapper;
+
+    @Override
+    public List<LifeFeedbackReply> getByFeedbackId(Integer feedbackId) {
+        return lifeFeedbackReplyMapper.selectByFeedbackId(feedbackId);
+    }
+}
+

+ 705 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackServiceImpl.java

@@ -0,0 +1,705 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeFeedback;
+import shop.alien.entity.store.LifeFeedbackReply;
+import shop.alien.entity.store.LifeImg;
+import shop.alien.entity.store.LifeLog;
+import shop.alien.entity.store.dto.*;
+import shop.alien.entity.store.vo.*;
+import shop.alien.entity.store.vo.FeedbackReplyVo;
+import shop.alien.mapper.LifeFeedbackMapper;
+import shop.alien.mapper.LifeLogMapper;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.store.config.WebSocketProcess;
+import shop.alien.store.service.LifeFeedbackService;
+import shop.alien.store.service.LifeFeedbackReplyService;
+import shop.alien.store.service.LifeImgService;
+import com.alibaba.fastjson2.JSONObject;
+
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * 意见反馈 Service实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, LifeFeedback> implements LifeFeedbackService {
+
+    private final LifeFeedbackMapper lifeFeedbackMapper;
+    private final LifeImgService lifeImgService;
+    private final LifeLogMapper lifeLogMapper;
+    private final LifeFeedbackReplyService lifeFeedbackReplyService;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final WebSocketProcess webSocketProcess;
+
+    @Override
+    public R<String> submitFeedback(LifeFeedbackDto dto) {
+        try {
+            // 1. 参数校验
+            if (dto.getUserId() == null) {
+                return R.fail("用户ID不能为空");
+            }
+            if (dto.getFeedbackSource() == null) {
+                return R.fail("反馈来源不能为空");
+            }
+            if (dto.getFeedbackType() == null) {
+                return R.fail("反馈类型不能为空");
+            }
+            if (dto.getContent() == null || dto.getContent().trim().isEmpty()) {
+                return R.fail("反馈内容不能为空");
+            }
+
+            // 2. 创建反馈记录(使用MyBatis Plus的save方法)
+            LifeFeedback feedback = new LifeFeedback();
+            BeanUtils.copyProperties(dto, feedback);
+            // 如果feedbackWay为空,默认为用户主动反馈(0)
+            if (feedback.getFeedbackWay() == null) {
+                feedback.setFeedbackWay(0);
+            }
+            feedback.setFeedbackTime(new Date());
+            feedback.setHandleStatus(0); // 处理中
+            feedback.setCreateTime(new Date());
+
+            boolean saveResult = this.save(feedback);
+            if (!saveResult) {
+                return R.fail("提交反馈失败");
+            }
+
+            // 3. 保存附件(图片和视频)
+            List<LifeImg> fileList = new ArrayList<>();
+            // 收集所有视频的截图URL,避免重复保存为普通图片
+            List<String> videoThumbnailUrls = new ArrayList<>();
+
+            if (!CollectionUtils.isEmpty(dto.getFileUrlList())) {
+                // 先处理视频,找到所有视频及其封面图
+                List<String> videoUrls = new ArrayList<>();
+                List<String> imageUrls = new ArrayList<>();
+
+                // 分类:区分视频和图片
+                for (String fileUrl : dto.getFileUrlList()) {
+                    if (isVideoUrl(fileUrl)) {
+                        videoUrls.add(fileUrl);
+                    } else if (isImageUrl(fileUrl)) {
+                        imageUrls.add(fileUrl);
+                    }
+                }
+
+                // 处理视频:自动匹配封面图
+                for (String videoUrl : videoUrls) {
+                    LifeImg video = new LifeImg();
+                    video.setFeedbackId(feedback.getId());
+                    video.setImgUrl(videoUrl);
+                    video.setFileType(2); // 2-视频
+                    video.setUploadTime(new Date());
+
+                    // 从fileUrlList中查找对应的封面图URL(通过文件名匹配)
+                    // 视频URL格式: .../video/xxx123456.mp4
+                    // 封面图URL格式: .../video/xxx123456.jpg 或 .../image/xxx123456.jpg
+                    String videoFileName = videoUrl.substring(videoUrl.lastIndexOf('/') + 1);
+                    String videoNameWithoutExt = videoFileName.substring(0, videoFileName.lastIndexOf('.'));
+
+                    // 在图片列表中查找匹配的封面图
+                    for (String imgUrl : imageUrls) {
+                        String imgFileName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1);
+                        if (imgFileName.contains(".")) {
+                            String imgNameWithoutExt = imgFileName.substring(0, imgFileName.lastIndexOf('.'));
+                            // 如果文件名(不含扩展名)相同,且是图片格式,则认为是该视频的封面图
+                            if (videoNameWithoutExt.equals(imgNameWithoutExt) && isImageUrl(imgUrl)) {
+                                video.setThumbnailUrl(imgUrl);
+                                videoThumbnailUrls.add(imgUrl); // 记录已使用的封面图URL
+                                break;
+                            }
+                        }
+                    }
+
+                    fileList.add(video);
+                }
+
+                // 处理图片(排除已作为视频封面的URL)
+                for (String imgUrl : imageUrls) {
+                    // 如果该URL已被用作视频封面,则跳过,不重复保存
+                    if (!videoThumbnailUrls.contains(imgUrl)) {
+                        LifeImg img = new LifeImg();
+                        img.setFeedbackId(feedback.getId());
+                        img.setImgUrl(imgUrl);
+                        img.setFileType(1); // 1-图片
+                        img.setUploadTime(new Date());
+                        fileList.add(img);
+                    }
+                }
+            }
+
+            if (!fileList.isEmpty()) {
+                lifeImgService.batchSave(fileList);
+            }
+
+            // 4. 记录日志(只记录详细内容)
+            saveLog(feedback.getId(), feedback.getContent(), "0");
+
+            return R.success("提交成功");
+        } catch (Exception e) {
+            log.error("提交反馈失败", e);
+            return R.fail("提交反馈失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public IPage<LifeFeedbackVo> getFeedbackList(Integer userId, Integer feedbackSource, int page, int size) {
+        try {
+            // 使用自定义SQL查询(已包含工作人员名称)
+            // 查询用户反馈(feedbackWay=0)和AI识别(feedbackWay=1)的记录
+            Page<LifeFeedbackVo> pageParam = new Page<>(page, size);
+            IPage<LifeFeedbackVo> voPage = lifeFeedbackMapper.selectFeedbackListWithStaff(
+                    pageParam, userId, feedbackSource, null, null
+            );
+
+            // 为每条记录查询附件(图片和视频)并设置反馈类型名称
+            voPage.getRecords().forEach(vo -> {
+                List<String> imgUrls = lifeImgService.getImgUrlsByFeedbackId(vo.getId());
+                vo.setImgUrlList(imgUrls);
+                List<String> videoUrls = lifeImgService.getVideoUrlsByFeedbackId(vo.getId());
+                vo.setVideoUrlList(videoUrls);
+                // 设置反馈类型名称
+                vo.setFeedbackTypeName(getFeedbackTypeName(vo.getFeedbackType()));
+            });
+
+            return voPage;
+        } catch (Exception e) {
+            log.error("查询反馈列表失败", e);
+            return new Page<>(page, size);
+        }
+    }
+
+    @Override
+    public R<LifeFeedbackVo> getFeedbackDetail(Integer feedbackId) {
+        try {
+            // 1. 使用自定义SQL查询反馈详情(已包含工作人员名称)
+            LifeFeedbackVo vo = lifeFeedbackMapper.selectFeedbackDetail(feedbackId);
+            if (vo == null) {
+                return R.fail("反馈记录不存在");
+            }
+
+            // 2. 查询附件(图片和视频)
+            // 查询所有附件,然后过滤出原始反馈的附件(排除回复附件)
+            List<LifeImg> allImgs = lifeImgService.getByFeedbackId(feedbackId);
+            List<String> imgUrls = new ArrayList<>();
+            List<String> videoUrls = new ArrayList<>();
+            Date feedbackTime = vo.getFeedbackTime();
+            if (feedbackTime != null) {
+                long feedbackTimeMs = feedbackTime.getTime();
+                for (LifeImg img : allImgs) {
+                    if (img.getUploadTime() != null) {
+                        long imgTimeMs = img.getUploadTime().getTime();
+                        // 判断附件是否属于原始反馈(时间差在5分钟内,且早于最早的回复)
+                        // 简化处理:如果是反馈后5分钟内的附件,认为是原始反馈的附件
+                        List<LifeFeedbackReply> replyList = lifeFeedbackReplyService.getByFeedbackId(feedbackId);
+                        boolean isOriginalFeedback = true;
+                        if (!replyList.isEmpty()) {
+                            Date firstReplyTime = replyList.get(0).getCreateTime();
+                            // 如果附件时间在最早回复时间之后,则不属于原始反馈
+                            if (img.getUploadTime().after(firstReplyTime)) {
+                                isOriginalFeedback = false;
+                            } else {
+                                long timeDiff = Math.abs(imgTimeMs - feedbackTimeMs);
+                                if (timeDiff > 5 * 60 * 1000) { // 超过5分钟
+                                    isOriginalFeedback = false;
+                                }
+                            }
+                        } else {
+                            long timeDiff = Math.abs(imgTimeMs - feedbackTimeMs);
+                            if (timeDiff > 5 * 60 * 1000) { // 超过5分钟
+                                isOriginalFeedback = false;
+                            }
+                        }
+
+                        if (isOriginalFeedback) {
+                            if (img.getFileType() == 1) {
+                                imgUrls.add(img.getImgUrl());
+                            } else if (img.getFileType() == 2) {
+                                videoUrls.add(img.getImgUrl());
+                            }
+                        }
+                    }
+                }
+            } else {
+                // 如果没有反馈时间,使用简单方式:只查询图片和视频
+                for (LifeImg img : allImgs) {
+                    if (img.getFileType() == 1) {
+                        imgUrls.add(img.getImgUrl());
+                    } else if (img.getFileType() == 2) {
+                        videoUrls.add(img.getImgUrl());
+                    }
+                }
+            }
+            vo.setImgUrlList(imgUrls);
+            vo.setVideoUrlList(videoUrls);
+            // 设置反馈类型名称
+            vo.setFeedbackTypeName(getFeedbackTypeName(vo.getFeedbackType()));
+
+            // 3. 查询回复列表(从life_feedback_reply表)
+            List<LifeFeedbackReply> replyList = lifeFeedbackReplyService.getByFeedbackId(feedbackId);
+            // 转换为VO格式(使用之前查询的allImgs)
+            List<LifeFeedbackVo> replyVoList = new ArrayList<>();
+            for (LifeFeedbackReply reply : replyList) {
+                LifeFeedbackVo replyVo = new LifeFeedbackVo();
+                replyVo.setId(reply.getId());
+                replyVo.setContent(reply.getReplyContent());
+                replyVo.setFeedbackTime(reply.getCreateTime());
+                // reply_type: 0-平台回复, 1-我的回复
+                replyVo.setStaffId(reply.getReplyType() == 0 ? 1 : null); // 平台回复有staffId,用户回复为null
+                // 查询回复的附件(通过时间判断:上传时间在回复创建时间前后5分钟内)
+                List<String> replyImgUrls = new ArrayList<>();
+                List<String> replyVideoUrls = new ArrayList<>();
+                Date replyTime = reply.getCreateTime();
+                long replyTimeMs = replyTime.getTime();
+                for (LifeImg img : allImgs) {
+                    if (img.getUploadTime() != null) {
+                        long imgTimeMs = img.getUploadTime().getTime();
+                        // 判断附件是否属于该回复(时间差在5分钟内)
+                        long timeDiff = Math.abs(imgTimeMs - replyTimeMs);
+                        if (timeDiff <= 5 * 60 * 1000) { // 5分钟
+                            if (img.getFileType() == 1) {
+                                replyImgUrls.add(img.getImgUrl());
+                            } else if (img.getFileType() == 2) {
+                                replyVideoUrls.add(img.getImgUrl());
+                            }
+                        }
+                    }
+                }
+                replyVo.setImgUrlList(replyImgUrls);
+                replyVo.setVideoUrlList(replyVideoUrls);
+                replyVoList.add(replyVo);
+            }
+
+            // 4. 按时间升序排序回复
+            replyVoList.sort((a, b) -> a.getFeedbackTime().compareTo(b.getFeedbackTime()));
+            vo.setPlatformReplies(replyVoList);
+
+            return R.data(vo);
+        } catch (Exception e) {
+            log.error("查询反馈详情失败", e);
+            return R.fail("查询反馈详情失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public R<String> userReply(UserReplyDto dto) {
+        try {
+            // 1. 参数校验
+            if (dto.getUserId() == null) {
+                return R.fail("用户ID不能为空");
+            }
+            if (dto.getFeedbackSource() == null) {
+                return R.fail("反馈来源不能为空");
+            }
+            if (dto.getFeedbackId() == null) {
+                return R.fail("反馈ID不能为空");
+            }
+            if (dto.getContent() == null || dto.getContent().trim().isEmpty()) {
+                return R.fail("回复内容不能为空");
+            }
+
+            // 2. 查询原始反馈(用于验证反馈是否存在)
+            LifeFeedback originalFeedback = lifeFeedbackMapper.selectById(dto.getFeedbackId());
+            if (originalFeedback == null) {
+                return R.fail("反馈记录不存在");
+            }
+
+            // 3. 创建用户回复记录(保存到life_feedback_reply表)
+            LifeFeedbackReply userReply = new LifeFeedbackReply();
+            userReply.setFeedbackId(dto.getFeedbackId());
+            userReply.setReplyType(1); // 1-我的回复(用户回复)
+            userReply.setReplyContent(dto.getContent());
+            userReply.setCreateTime(new Date());
+            userReply.setUpdateTime(new Date());
+
+            boolean saveResult = lifeFeedbackReplyService.save(userReply);
+            if (!saveResult) {
+                return R.fail("回复失败");
+            }
+
+            // 4. 记录日志(只记录内容)
+            saveLog(dto.getFeedbackId(), dto.getContent(), "2");
+
+            return R.success("回复成功");
+        } catch (Exception e) {
+            log.error("用户回复失败", e);
+            return R.fail("用户回复失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 保存操作日志
+     * @param feedbackId 反馈ID
+     * @param context 日志内容
+     * @param type 操作类型:0-创建反馈工单,1-分配跟踪人员,2-回复用户,3-问题解决状态
+     */
+    private void saveLog(Integer feedbackId, String context, String type) {
+        try {
+            LifeLog lifeLog = new LifeLog();
+            lifeLog.setFeedbackId(feedbackId);
+            lifeLog.setContext(context);
+            lifeLog.setType(type);
+            lifeLog.setCreatedTime(new Date());
+            lifeLogMapper.insert(lifeLog);
+        } catch (Exception e) {
+            log.error("保存日志失败", e);
+        }
+    }
+
+    /**
+     * 获取反馈类型名称
+     * @param feedbackType 反馈类型:0-bug反馈,1-优化反馈,2-新增功能反馈
+     * @return 反馈类型名称
+     */
+    private String getFeedbackTypeName(Integer feedbackType) {
+        if (feedbackType == null) {
+            return "";
+        }
+        switch (feedbackType) {
+            case 0:
+                return "bug反馈";
+            case 1:
+                return "优化反馈";
+            case 2:
+                return "新增功能反馈";
+            default:
+                return "";
+        }
+    }
+
+    /**
+     * 判断URL是否为视频
+     * @param url 文件URL
+     * @return true-视频,false-非视频
+     */
+    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");
+    }
+
+    /**
+     * 判断URL是否为图片
+     * @param url 文件URL
+     * @return true-图片,false-非图片
+     */
+    private boolean isImageUrl(String url) {
+        if (url == null || url.isEmpty()) {
+            return false;
+        }
+        String lowerUrl = url.toLowerCase();
+        return lowerUrl.endsWith(".jpg") ||
+               lowerUrl.endsWith(".jpeg") ||
+               lowerUrl.endsWith(".png") ||
+               lowerUrl.endsWith(".bmp") ||
+               lowerUrl.endsWith(".webp") ||
+               lowerUrl.endsWith(".gif") ||
+               lowerUrl.endsWith(".svg");
+    }
+
+
+    // ==================== 中台接口实现 ====================
+
+    @Override
+    public R<IPage<LifeFeedbackListVo>> getWebFeedbackList(LifeFeedbackQueryDto queryDto) {
+        try {
+            Page<LifeFeedbackListVo> pageParam = new Page<>(queryDto.getPage(), queryDto.getSize());
+            IPage<LifeFeedbackListVo> result = lifeFeedbackMapper.selectWebFeedbackList(
+                    pageParam,
+                    queryDto.getFeedbackType(),
+                    queryDto.getHandleStatus(),
+                    queryDto.getFeedbackSource(),
+                    queryDto.getFeedbackWay()
+            );
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("中台-查询意见反馈列表失败", e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public R<LifeFeedbackDetailVo> getWebFeedbackDetail(Integer feedbackId) {
+        try {
+            if (feedbackId == null) {
+                return R.fail("反馈ID不能为空");
+            }
+
+            // 1. 查询反馈详情
+            LifeFeedbackDetailVo detail = lifeFeedbackMapper.selectWebFeedbackDetail(feedbackId);
+            if (detail == null) {
+                return R.fail("反馈记录不存在");
+            }
+
+            // 2. 查询附件列表(图片/视频)
+            List<FeedbackAttachmentVo> attachments = new ArrayList<>();
+            List<LifeImg> imgList = lifeImgService.getByFeedbackId(feedbackId);
+            if (!CollectionUtils.isEmpty(imgList)) {
+                for (LifeImg img : imgList) {
+                    FeedbackAttachmentVo attachment = new FeedbackAttachmentVo();
+                    attachment.setId(img.getId());
+                    attachment.setFileType(img.getFileType() != null ? img.getFileType() : 1);
+                    attachment.setFileUrl(img.getImgUrl());
+                    attachment.setThumbnailUrl(img.getThumbnailUrl());
+                    attachments.add(attachment);
+                }
+            }
+            detail.setAttachments(attachments);
+
+            // 3. 查询回复列表(平台回复和用户回复)
+            List<LifeFeedbackReply> replyList = lifeFeedbackReplyService.getByFeedbackId(feedbackId);
+            List<FeedbackReplyVo> replyVoList = new ArrayList<>();
+            for (LifeFeedbackReply reply : replyList) {
+                FeedbackReplyVo replyVo = new FeedbackReplyVo();
+                replyVo.setId(reply.getId());
+                replyVo.setFeedbackId(reply.getFeedbackId());
+                replyVo.setReplyType(reply.getReplyType());
+                replyVo.setReplyTypeName(reply.getReplyType() == 0 ? "平台回复" : "用户回复");
+                replyVo.setReplyContent(reply.getReplyContent());
+                replyVo.setCreateTime(reply.getCreateTime());
+                
+                // 查询回复的附件(通过时间判断:上传时间在回复创建时间前后5分钟内)
+                List<String> replyImgUrls = new ArrayList<>();
+                List<String> replyVideoUrls = new ArrayList<>();
+                Date replyTime = reply.getCreateTime();
+                if (replyTime != null && !CollectionUtils.isEmpty(imgList)) {
+                    long replyTimeMs = replyTime.getTime();
+                    for (LifeImg img : imgList) {
+                        if (img.getUploadTime() != null) {
+                            long imgTimeMs = img.getUploadTime().getTime();
+                            // 判断附件是否属于该回复(时间差在5分钟内)
+                            long timeDiff = Math.abs(imgTimeMs - replyTimeMs);
+                            if (timeDiff <= 5 * 60 * 1000) { // 5分钟
+                                if (img.getFileType() == 1) {
+                                    replyImgUrls.add(img.getImgUrl());
+                                } else if (img.getFileType() == 2) {
+                                    replyVideoUrls.add(img.getImgUrl());
+                                }
+                            }
+                        }
+                    }
+                }
+                replyVo.setImgUrlList(replyImgUrls);
+                replyVo.setVideoUrlList(replyVideoUrls);
+                replyVoList.add(replyVo);
+            }
+            
+            // 按时间升序排序回复
+            replyVoList.sort((a, b) -> a.getCreateTime().compareTo(b.getCreateTime()));
+            detail.setReplies(replyVoList);
+
+            return R.data(detail);
+        } catch (Exception e) {
+            log.error("中台-查询反馈详情失败", e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<String> webReplyUser(LifeFeedbackReplyWebDto replyDto) {
+        try {
+            // 1. 参数校验
+            if (replyDto.getFeedbackId() == null) {
+                return R.fail("反馈ID不能为空");
+            }
+            if (replyDto.getContent() == null || replyDto.getContent().trim().isEmpty()) {
+                return R.fail("回复内容不能为空");
+            }
+
+            // 2. 查询原始反馈
+            LifeFeedback feedback = lifeFeedbackMapper.selectById(replyDto.getFeedbackId());
+            if (feedback == null) {
+                return R.fail("反馈记录不存在");
+            }
+
+            // 3. 保存平台回复到life_feedback_reply表
+            LifeFeedbackReply platformReply = new LifeFeedbackReply();
+            platformReply.setFeedbackId(replyDto.getFeedbackId());
+            platformReply.setReplyType(0); // 0-平台回复
+            platformReply.setReplyContent(replyDto.getContent());
+            platformReply.setCreateTime(new Date());
+            platformReply.setUpdateTime(new Date());
+            
+            boolean saveResult = lifeFeedbackReplyService.save(platformReply);
+            if (!saveResult) {
+                return R.fail("保存回复失败");
+            }
+
+            // 4. 记录回复日志(类型3-回复用户)
+            String logContent = replyDto.getContent();
+            if (replyDto.getUserReply() != null && !replyDto.getUserReply().trim().isEmpty()) {
+                logContent = replyDto.getContent() + "||用户回复:" + replyDto.getUserReply();
+            }
+            saveFeedbackLog(replyDto.getFeedbackId(), 3, logContent);
+
+            // 5. 发送通知给用户
+            sendFeedbackReplyNotice(feedback, replyDto.getContent());
+
+            return R.success("回复成功");
+        } catch (Exception e) {
+            log.error("中台-回复用户失败", e);
+            return R.fail("回复失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<String> updateWebFeedbackStatus(LifeFeedbackStatusDto statusDto) {
+        try {
+            // 1. 参数校验
+            if (statusDto.getFeedbackId() == null) {
+                return R.fail("反馈ID不能为空");
+            }
+
+            // 2. 更新状态为已解决
+            LifeFeedback updateFeedback = new LifeFeedback();
+            updateFeedback.setId(statusDto.getFeedbackId());
+            updateFeedback.setHandleStatus(1); // 已解决
+            updateFeedback.setUpdateTime(new Date());
+
+            boolean result = this.updateById(updateFeedback);
+            if (!result) {
+                return R.fail("更新失败");
+            }
+
+            // 3. 记录日志(类型0-问题解决状态)
+            String logContent = "问题已解决";
+            saveFeedbackLog(statusDto.getFeedbackId(), 0, logContent);
+
+            return R.success("更新成功");
+        } catch (Exception e) {
+            log.error("中台-更新反馈状态失败", e);
+            return R.fail("更新失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 保存反馈操作日志
+     * @param feedbackId 反馈ID
+     * @param type 操作类型:0-问题解决状态,1-分配跟踪人员,2-创建反馈工单,3-回复用户
+     * @param context 日志内容
+     */
+    private void saveFeedbackLog(Integer feedbackId, Integer type, String context) {
+        try {
+            LifeLog lifeLog = new LifeLog();
+            lifeLog.setFeedbackId(feedbackId);
+            lifeLog.setType(String.valueOf(type));
+            lifeLog.setContext(context);
+            lifeLog.setCreatedTime(new Date());
+            lifeLog.setDeleteFlag(0);
+            lifeLogMapper.insert(lifeLog);
+        } catch (Exception e) {
+            log.error("保存反馈日志失败", e);
+        }
+    }
+
+    /**
+     * 发送平台回复通知给用户
+     * @param feedback 反馈记录
+     * @param replyContent 回复内容
+     */
+    private void sendFeedbackReplyNotice(LifeFeedback feedback, String replyContent) {
+        try {
+            String receiverId = null;
+            
+            // 根据反馈来源判断是用户端还是商家端
+            if (feedback.getFeedbackSource() == null) {
+                log.warn("反馈来源为空,无法发送通知,feedbackId={}", feedback.getId());
+                return;
+            }
+            
+            // userId对应store_user表的id,统一从store_user表查询
+            if (feedback.getUserId() == null) {
+                log.warn("用户ID为空,无法发送通知,feedbackId={}", feedback.getId());
+                return;
+            }
+            
+            StoreUser storeUser = storeUserMapper.selectById(feedback.getUserId());
+            if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
+                log.warn("未找到商户用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
+                return;
+            }
+            
+            // 根据feedbackSource设置不同的接收者ID格式
+            if (feedback.getFeedbackSource() == 0) {
+                // 用户端 - 使用user_手机号格式
+                receiverId = "user_" + storeUser.getPhone();
+            } else if (feedback.getFeedbackSource() == 1) {
+                // 商家端 - 使用store_手机号格式
+                receiverId = "store_" + storeUser.getPhone();
+            } else {
+                log.warn("未知的反馈来源,feedbackSource={}, feedbackId={}", feedback.getFeedbackSource(), feedback.getId());
+                return;
+            }
+            
+            // 构建通知消息
+            JSONObject messageJson = new JSONObject();
+            messageJson.put("feedbackId", feedback.getId()); // 添加反馈ID用于区分
+            messageJson.put("message", "平台已回复您的意见反馈:" + replyContent);
+
+            // 创建通知记录
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setContext(messageJson.toJSONString());
+            lifeNotice.setTitle("意见反馈回复通知");
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1); // 1-系统通知
+            lifeNotice.setBusinessId(feedback.getId());
+            
+            // 保存通知
+            lifeNoticeMapper.insert(lifeNotice);
+            
+            // 通过WebSocket发送实时通知
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSONObject.toJSONString(lifeNotice));
+            
+            try {
+                webSocketProcess.sendMessage(receiverId, JSONObject.toJSONString(webSocketVo));
+                log.info("平台回复通知发送成功,feedbackId={}, receiverId={}", feedback.getId(), receiverId);
+            } catch (Exception e) {
+                log.error("发送WebSocket通知失败,feedbackId={}, receiverId={}, error={}", 
+                        feedback.getId(), receiverId, e.getMessage());
+            }
+            
+        } catch (Exception e) {
+            log.error("发送平台回复通知异常,feedbackId={}, error={}", feedback.getId(), e.getMessage(), e);
+        }
+    }
+}
+

+ 56 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeImgServiceImpl.java

@@ -0,0 +1,56 @@
+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.stereotype.Service;
+import shop.alien.entity.store.LifeImg;
+import shop.alien.mapper.LifeImgMapper;
+import shop.alien.store.service.LifeImgService;
+
+import java.util.List;
+
+/**
+ * 反馈图片 Service实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LifeImgServiceImpl extends ServiceImpl<LifeImgMapper, LifeImg> implements LifeImgService {
+
+    private final LifeImgMapper lifeImgMapper;
+
+    @Override
+    public List<LifeImg> getByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.selectByFeedbackId(feedbackId);
+    }
+
+    @Override
+    public boolean batchSave(List<LifeImg> imgList) {
+        if (imgList == null || imgList.isEmpty()) {
+            return false;
+        }
+        // 确保每个附件都有上传时间
+        imgList.forEach(item -> {
+            if (item.getUploadTime() == null) {
+                item.setUploadTime(new java.util.Date());
+            }
+        });
+        return lifeImgMapper.batchInsert(imgList) > 0;
+    }
+
+    @Override
+    public boolean removeByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.deleteByFeedbackId(feedbackId) > 0;
+    }
+
+    @Override
+    public List<String> getImgUrlsByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.selectImgUrlsByFeedbackId(feedbackId);
+    }
+
+    @Override
+    public List<String> getVideoUrlsByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.selectVideoUrlsByFeedbackId(feedbackId);
+    }
+}

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

@@ -112,15 +112,15 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             if (result > 0) {
                 // AI审核
                 //登录获取token
-                String token = aiUserViolationUtils.getAccessToken();
-                //调用AI接口
-                String taskId = aiUserViolationUtils.createTask(token, lifeuserViolation);
-                if (org.springframework.util.StringUtils.isEmpty(taskId)) {
-                    log.warn("Failed to create AI task for second round review, lifeuserViolation id={}", lifeuserViolation.getId());
-                    return 0;
-                }
-                lifeuserViolation.setAiTaskId(taskId);
-                lifeUserViolationMapper.updateById(lifeuserViolation);
+//                String token = aiUserViolationUtils.getAccessToken();
+//                //调用AI接口
+//                String taskId = aiUserViolationUtils.createTask(token, lifeuserViolation);
+//                if (org.springframework.util.StringUtils.isEmpty(taskId)) {
+//                    log.warn("Failed to create AI task for second round review, lifeuserViolation id={}", lifeuserViolation.getId());
+//                    return 0;
+//                }
+//                lifeuserViolation.setAiTaskId(taskId);
+//                lifeUserViolationMapper.updateById(lifeuserViolation);
 
 
                 //String phoneId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");

+ 256 - 0
alien-store/src/main/java/shop/alien/store/service/impl/OperationalActivityServiceImpl.java

@@ -0,0 +1,256 @@
+package shop.alien.store.service.impl;
+
+import com.alibaba.excel.util.StringUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+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.LifeDiscountCoupon;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.entity.storePlatform.StoreOperationalActivity;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
+import shop.alien.mapper.LifeDiscountCouponMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.StoreInfoMapper;
+import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
+import shop.alien.store.service.OperationalActivityService;
+
+import java.util.*;
+
+/**
+ * 运营活动服务实现类
+ * <p>
+ * 提供运营活动的增删改查功能,包括:
+ * 1. 活动的创建、更新、删除
+ * 2. 活动列表查询、分页查询
+ * 3. 活动状态管理
+ * </p>
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class OperationalActivityServiceImpl implements OperationalActivityService {
+
+    private final StoreOperationalActivityMapper activityMapper;
+
+    private final StoreImgMapper imgMapper;
+
+    private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
+
+    private final StoreInfoMapper storeInfoMapper;
+
+    @Override
+    public int auditActivity(Integer id, Integer status, String approvalComments) {
+        log.info("OperationalActivityServiceImpl.auditActivity: id={}, status={}, approvalComments={}", id, status, approvalComments);
+
+        if (id == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+        if (status == null) {
+            throw new IllegalArgumentException("审核状态不能为空");
+        }
+
+        if (!Objects.equals(status, 1) && StringUtils.isBlank(approvalComments)) {
+            throw new IllegalArgumentException("驳回原因不能为空");
+        }
+
+        StoreOperationalActivity update = new StoreOperationalActivity();
+        update.setId(id);
+        if (Objects.equals(status, 1)) {
+            StoreOperationalActivity activity = activityMapper.selectById(id);
+            if (activity == null) {
+                throw new IllegalArgumentException("活动不存在");
+            }
+            update.setStatus(resolveApprovedStatus(activity));
+            update.setApprovalComments(null);
+        } else {
+            update.setStatus(3);
+            update.setApprovalComments(approvalComments);
+        }
+
+        return activityMapper.updateById(update);
+    }
+
+    @Override
+    public IPage<StoreOperationalActivityVO> pageActivityDetail(Integer storeId, String storeName, Integer pageNum, Integer pageSize) {
+        log.info("OperationalActivityServiceImpl.pageActivityDetail: storeId={}, storeName={}, pageNum={}, pageSize={}", storeId, storeName, pageNum, pageSize);
+
+        if (storeId == null && StringUtils.isBlank(storeName)) {
+            throw new IllegalArgumentException("请至少提供商户ID或商户名称");
+        }
+
+        int current = (pageNum == null || pageNum <= 0) ? 1 : pageNum;
+        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
+
+        // 收集门店ID
+        LinkedHashSet<Integer> storeIds = new LinkedHashSet<>();
+        if (storeId != null) {
+            storeIds.add(storeId);
+        }
+        if (StringUtils.isNotBlank(storeName)) {
+            LambdaQueryWrapper<StoreInfo> storeWrapper = new LambdaQueryWrapper<>();
+            storeWrapper.like(StoreInfo::getStoreName, storeName)
+                    .eq(StoreInfo::getDeleteFlag, 0)
+                    .select(StoreInfo::getId, StoreInfo::getStoreName);
+            List<StoreInfo> storeList = storeInfoMapper.selectList(storeWrapper);
+            for (StoreInfo info : storeList) {
+                if (info.getId() != null) {
+                    storeIds.add(info.getId());
+                }
+            }
+        }
+
+        if (storeIds.isEmpty()) {
+            return new Page<>(current, size, 0);
+        }
+
+        LambdaQueryWrapper<StoreOperationalActivity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.in(StoreOperationalActivity::getStoreId, storeIds);
+        wrapper.eq(StoreOperationalActivity::getDeleteFlag, 0);
+        wrapper.orderByDesc(StoreOperationalActivity::getCreatedTime);
+
+        IPage<StoreOperationalActivity> entityPage = activityMapper.selectPage(new Page<>(current, size), wrapper);
+
+        List<StoreOperationalActivityVO> voRecords = new ArrayList<>(entityPage.getRecords().size());
+        Map<Integer, String> storeCache = new LinkedHashMap<>();
+
+        for (StoreOperationalActivity activity : entityPage.getRecords()) {
+            StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
+            BeanUtils.copyProperties(activity, vo);
+            vo.setStatusName(resolveStatusName(activity.getStatus()));
+
+            if (activity.getCouponId() != null) {
+                LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
+                if (coupon != null) {
+                    vo.setCouponName(coupon.getName());
+                }
+            }
+
+            attachStoreInfo(vo, storeCache);
+            fillActivityImages(vo);
+            voRecords.add(vo);
+        }
+
+        Page<StoreOperationalActivityVO> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
+        voPage.setRecords(voRecords);
+        return voPage;
+    }
+
+    @Override
+    public StoreOperationalActivityVO getActivityDetailById(Integer id) {
+        log.info("OperationalActivityServiceImpl.getActivityDetailById: id={}", id);
+
+        if (id == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+
+        StoreOperationalActivity activity = activityMapper.selectById(id);
+        if (activity == null) {
+            return null;
+        }
+
+        StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
+        BeanUtils.copyProperties(activity, vo);
+        vo.setStatusName(resolveStatusName(activity.getStatus()));
+
+        if (activity.getCouponId() != null) {
+            LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
+            if (coupon != null) {
+                vo.setCouponName(coupon.getName());
+            }
+        }
+
+        attachStoreInfo(vo, new LinkedHashMap<>());
+        fillActivityImages(vo);
+        return vo;
+    }
+
+    private int resolveApprovedStatus(StoreOperationalActivity activity) {
+        Date now = new Date();
+        if (activity.getStartTime() == null || activity.getEndTime() == null) {
+            return 2;
+        }
+        if (now.before(activity.getStartTime())) {
+            return 2;
+        }
+        if (!now.after(activity.getEndTime())) {
+            return 5;
+        }
+        return 7;
+    }
+
+    private String resolveStatusName(Integer status) {
+        if (status == null) {
+            return null;
+        }
+        switch (status) {
+            case 1:
+                return "待审核";
+            case 2:
+                return "未开始";
+            case 3:
+                return "审核拒绝";
+            case 4:
+                return "已售罄";
+            case 5:
+                return "进行中";
+            case 6:
+                return "已下架";
+            case 7:
+                return "已结束";
+            default:
+                return null;
+        }
+    }
+
+    private void attachStoreInfo(StoreOperationalActivityVO vo, Map<Integer, String> cache) {
+        if (vo == null || vo.getStoreId() == null) {
+            return;
+        }
+        Integer sid = vo.getStoreId();
+        if (cache != null && cache.containsKey(sid)) {
+            vo.setStoreName(cache.get(sid));
+            return;
+        }
+        StoreInfo storeInfo = storeInfoMapper.selectById(sid);
+        if (storeInfo != null) {
+            vo.setStoreName(storeInfo.getStoreName());
+            if (cache != null) {
+                cache.put(sid, storeInfo.getStoreName());
+            }
+        }
+    }
+
+    private void fillActivityImages(StoreOperationalActivityVO vo) {
+        if (vo == null || vo.getStoreId() == null || vo.getId() == null) {
+            return;
+        }
+        StoreImg titleImg = imgMapper.selectOne(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, vo.getStoreId())
+                .eq(StoreImg::getBusinessId, vo.getId())
+                .eq(StoreImg::getImgType, 26)
+                .eq(StoreImg::getDeleteFlag, 0));
+        if (titleImg != null) {
+            vo.setActivityTitleImg(titleImg);
+            vo.setActivityTitleImgUrl(titleImg.getImgUrl());
+        }
+
+        StoreImg detailImg = imgMapper.selectOne(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, vo.getStoreId())
+                .eq(StoreImg::getBusinessId, vo.getId())
+                .eq(StoreImg::getImgType, 27)
+                .eq(StoreImg::getDeleteFlag, 0));
+        if (detailImg != null) {
+            vo.setActivityDetailImg(detailImg);
+            vo.setActivityDetailImgUrl(detailImg.getImgUrl());
+        }
+    }
+}
+

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels