Parcourir la source

Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into store-plantform

# Conflicts:
#	alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
#	alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java
wxd il y a 1 semaine
Parent
commit
be90c55232
62 fichiers modifiés avec 4593 ajouts et 240 suppressions
  1. 4 0
      alien-entity/src/main/java/shop/alien/entity/second/SecondGoods.java
  2. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/LawyerConsultationOrder.java
  3. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeUserDynamics.java
  4. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeUserViolation.java
  5. 154 0
      alien-entity/src/main/java/shop/alien/entity/store/RefundRecord.java
  6. 66 0
      alien-entity/src/main/java/shop/alien/entity/store/SecondAiTask.java
  7. 12 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreClockIn.java
  8. 1 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreComment.java
  9. 7 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreCommentAppeal.java
  10. 5 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreDictionary.java
  11. 73 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreVerificationCode.java
  12. 6 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/LawyerConsultationOrderDto.java
  13. 6 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/PayStatusRequest.java
  14. 3 3
      alien-entity/src/main/java/shop/alien/entity/store/excelVo/BusinessSectionExcelVo.java
  15. 34 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/AiApproveStoreInfo.java
  16. 5 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java
  17. 33 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreCommentCountVo.java
  18. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreInfoVo.java
  19. 5 2
      alien-entity/src/main/java/shop/alien/mapper/LawyerConsultationOrderMapper.java
  20. 17 0
      alien-entity/src/main/java/shop/alien/mapper/RefundRecordMapper.java
  21. 12 0
      alien-entity/src/main/java/shop/alien/mapper/SecondAiTaskMapper.java
  22. 28 0
      alien-entity/src/main/java/shop/alien/mapper/StoreCommentAppealMapper.java
  23. 13 0
      alien-entity/src/main/java/shop/alien/mapper/StoreVerificationCodeMapper.java
  24. 20 0
      alien-entity/src/main/resources/mapper/StoreVerificationCodeMapper.xml
  25. 1 1
      alien-gateway/src/main/resources/bootstrap.yml
  26. 4 0
      alien-job/src/main/java/shop/alien/job/feign/SecondGoodsFeign.java
  27. 217 0
      alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java
  28. 183 0
      alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java
  29. 190 0
      alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java
  30. 445 10
      alien-job/src/main/java/shop/alien/job/store/AiTagJob.java
  31. 419 0
      alien-job/src/main/java/shop/alien/job/store/BadReviewAppealJob.java
  32. 1 1
      alien-job/src/main/resources/bootstrap.yml
  33. 11 0
      alien-lawyer/src/main/java/shop/alien/lawyer/feign/AlienStoreFeign.java
  34. 7 3
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java
  35. 63 25
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderExpirationServiceImpl.java
  36. 84 0
      alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java
  37. 545 0
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java
  38. 146 0
      alien-second/src/main/java/shop/alien/second/util/AiTaskUtils.java
  39. 1 1
      alien-second/src/main/resources/bootstrap.yml
  40. 11 0
      alien-store/src/main/java/shop/alien/store/controller/StoreCommentController.java
  41. 35 0
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  42. 85 0
      alien-store/src/main/java/shop/alien/store/controller/StoreVerificationCodeController.java
  43. 49 0
      alien-store/src/main/java/shop/alien/store/controller/UserStoreController.java
  44. 17 0
      alien-store/src/main/java/shop/alien/store/service/RefundRecordService.java
  45. 9 0
      alien-store/src/main/java/shop/alien/store/service/StoreCommentService.java
  46. 11 0
      alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
  47. 36 0
      alien-store/src/main/java/shop/alien/store/service/StoreVerificationCodeService.java
  48. 4 0
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerConsultationOrderServiceImpl.java
  49. 100 81
      alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java
  50. 103 90
      alien-store/src/main/java/shop/alien/store/service/impl/PlatformBusinessSectionServiceImpl.java
  51. 21 0
      alien-store/src/main/java/shop/alien/store/service/impl/RefundRecordServiceImpl.java
  52. 51 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreCommentServiceImpl.java
  53. 113 3
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  54. 71 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreVerificationCodeServiceImpl.java
  55. 261 7
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/AlipayPaymentStrategyImpl.java
  56. 384 10
      alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPaymentStrategyImpl.java
  57. 264 0
      alien-store/src/main/java/shop/alien/store/util/AiUserViolationUtils.java
  58. 78 0
      alien-store/src/main/java/shop/alien/store/util/ai/AiAuthTokenUtil.java
  59. 26 0
      alien-store/src/main/java/shop/alien/store/util/ali/AliSms.java
  60. 1 1
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java
  61. 1 1
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java
  62. 1 1
      alien-store/src/main/resources/bootstrap.yml

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/second/SecondGoods.java

@@ -112,6 +112,10 @@ public class SecondGoods implements Serializable {
     @ApiModelProperty(value = "审核失败原因")
     private String failedReason;
 
+    @TableField("ai_task_id")
+    @ApiModelProperty(value = "ai_task_id")
+    private String aiTaskId;
+
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @ApiModelProperty(value = "创建时间")

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

@@ -175,5 +175,9 @@ public class LawyerConsultationOrder extends Model<LawyerConsultationOrder> {
     @ApiModelProperty(value = "退款申请处理动作:1-同意,2-拒绝")
     @TableField(exist = false)
     private String processAction;
+
+    @ApiModelProperty(value = "支付方式 1支付宝 2微信")
+    @TableField("pay_type")
+    private  String payType;
 }
 

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

@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 /**
@@ -112,4 +113,32 @@ public class LifeUserDynamics {
     @ApiModelProperty(value = "启用状态0-启用,1-禁用")
     @TableField("enable_status")
     private Integer enableStatus;
+
+    @ApiModelProperty(value = "达人id")
+    @TableField("expert_id")
+    private Integer expertId;
+
+    @ApiModelProperty(value = "业务id")
+    @TableField("business_id")
+    private Integer businessId;
+
+    @ApiModelProperty(value = "转发量")
+    @TableField("transfer_count")
+    private Integer transferCount;
+
+    @ApiModelProperty(value = "达人动态实际浏览数")
+    @TableField("reality_count")
+    private Integer realityCount;
+
+    @ApiModelProperty(value = "是否审核(未审核:0,审核中:1,审核完成:2)")
+    @TableField("check_flag")
+    private Integer checkFlag;
+
+    @ApiModelProperty(value = "AI审核结果查询id")
+    @TableField("ai_task_id")
+    private String aiTaskId;
+
+    @ApiModelProperty(value = "审核失败原因")
+    @TableField("reason")
+    private String reason;
 }

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

@@ -131,6 +131,10 @@ public class LifeUserViolation extends Model<LifeUserViolation> {
     @TableField(value = "goods_id")
     private Integer goodsId;
 
+    @ApiModelProperty(value = "商品ID")
+    @TableField(value = "ai_task_id")
+    private String aiTaskId;
+
     @Override
     protected Serializable pkVal() {
         return this.id;

+ 154 - 0
alien-entity/src/main/java/shop/alien/entity/store/RefundRecord.java

@@ -0,0 +1,154 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+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 lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * 退款记录表
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude
+@ApiModel(value = "RefundRecord对象", description = "退款记录表")
+@TableName("refund_record")
+public class RefundRecord extends Model<RefundRecord> {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "用户ID")
+    @TableField("user_id")
+    private Integer userId;
+
+    @ApiModelProperty(value = "商户订单号:商户系统生成的原交易订单唯一标识")
+    @TableField("out_trade_no")
+    private String outTradeNo;
+
+    @ApiModelProperty(value = "第三方支付订单号:微信支付订单号或支付宝交易号")
+    @TableField("transaction_id")
+    private String transactionId;
+
+    @ApiModelProperty(value = "商户退款单号:商户系统生成的退款单号")
+    @TableField("out_refund_no")
+    private String outRefundNo;
+
+    @ApiModelProperty(value = "第三方退款单号:微信退款单号或支付宝退款单号")
+    @TableField("refund_id")
+    private String refundId;
+
+    @ApiModelProperty(value = "支付类型:WECHAT_PAY-微信支付, ALIPAY-支付宝")
+    @TableField("pay_type")
+    private String payType;
+
+    @ApiModelProperty(value = "退款状态:SUCCESS-退款成功, CLOSED-退款关闭, PROCESSING-退款处理中, ABNORMAL-退款异常")
+    @TableField("refund_status")
+    private String refundStatus;
+
+    @ApiModelProperty(value = "订单总金额:单位分")
+    @TableField("total_amount")
+    private Long totalAmount;
+
+    @ApiModelProperty(value = "退款金额:单位分")
+    @TableField("refund_amount")
+    private Long refundAmount;
+
+    @ApiModelProperty(value = "实际退款金额:单位分,最终成功退回给用户的金额")
+    @TableField("actual_refund_amount")
+    private Long actualRefundAmount;
+
+    @ApiModelProperty(value = "退款币种:默认CNY")
+    @TableField("currency")
+    private String currency;
+
+    @ApiModelProperty(value = "退款原因")
+    @TableField("refund_reason")
+    private String refundReason;
+
+    @ApiModelProperty(value = "退款渠道:ORIGINAL-原路退回, BALANCE-退回余额, OTHER_BALANCE-其他余额, OTHER_BANKCARD-其他银行卡")
+    @TableField("refund_channel")
+    private String refundChannel;
+
+    @ApiModelProperty(value = "退款资金来源:AVAILABLE-可用余额, UNSETTLED-未结算资金")
+    @TableField("funds_account")
+    private String fundsAccount;
+
+    @ApiModelProperty(value = "退款创建时间:退款请求创建时间")
+    @TableField("refund_create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date refundCreateTime;
+
+    @ApiModelProperty(value = "退款成功时间:退款成功完成时间")
+    @TableField("refund_success_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date refundSuccessTime;
+
+    @ApiModelProperty(value = "用户收到退款账户:退款到账的账户信息")
+    @TableField("user_received_account")
+    private String userReceivedAccount;
+
+    @ApiModelProperty(value = "退款详情:退款渠道明细列表(JSON格式)")
+    @TableField("refund_detail")
+    private String refundDetail;
+
+    @ApiModelProperty(value = "错误码:退款失败时的错误码")
+    @TableField("error_code")
+    private String errorCode;
+
+    @ApiModelProperty(value = "错误信息:退款失败时的错误描述")
+    @TableField("error_msg")
+    private String errorMsg;
+
+    @ApiModelProperty(value = "响应数据:第三方支付平台返回的完整响应数据(JSON格式)")
+    @TableField("response_data")
+    private String responseData;
+
+    @ApiModelProperty(value = "业务订单ID:关联的业务订单ID")
+    @TableField("order_id")
+    private String orderId;
+
+    @ApiModelProperty(value = "店铺ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "备注")
+    @TableField("remark")
+    private String remark;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

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

@@ -0,0 +1,66 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+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;
+
+/**
+ * second_ai_task 表实体
+ */
+@Data
+@TableName("second_ai_task")
+@ApiModel(value = "SecondAiTask", description = "AI视频审核任务表")
+public class SecondAiTask implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("数据ID")
+    @TableField("data_id")
+    private String dataId;
+
+    @ApiModelProperty("视频URL")
+    @TableField("video_url")
+    private String videoUrl;
+
+    @ApiModelProperty("任务ID")
+    @TableField("task_id")
+    private String taskId;
+
+    @ApiModelProperty("任务状态 SUBMITTED/PROCESSING/...")
+    @TableField("status")
+    private String status;
+
+    @ApiModelProperty("风险级别 none/low/medium/high")
+    @TableField("risk_level")
+    private String riskLevel;
+
+    @ApiModelProperty("审核结果(JSON)")
+    @TableField("result")
+    private String result;
+
+    @ApiModelProperty("重试次数")
+    @TableField("retry_count")
+    private Integer retryCount;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}

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

@@ -83,6 +83,18 @@ public class StoreClockIn extends Model<StoreClockIn> {
     @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
     private Integer updatedUserId;
 
+    @ApiModelProperty(value = "是否审核(未审核:0,审核中:1,审核完成:2)")
+    @TableField("check_flag")
+    private Integer checkFlag;
+
+    @ApiModelProperty(value = "AI审核结果查询id")
+    @TableField("ai_task_id")
+    private String aiTaskId;
+
+    @ApiModelProperty(value = "审核失败原因")
+    @TableField("reason")
+    private String reason;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -20,6 +20,7 @@ import java.util.Date;
 @Data
 @JsonInclude
 @EqualsAndHashCode(callSuper = false)
+@TableName("store_comment")
 @ApiModel(value = "StoreComment对象", description = "评论表")
 public class StoreComment extends Model<StoreComment> {
 

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

@@ -74,4 +74,11 @@ public class StoreCommentAppeal extends Model<StoreCommentAppeal> {
     @TableField("updated_user_id")
     private Integer updatedUserId;
 
+    @ApiModelProperty(value = "AI接口返回的ID")
+    @TableField("record_id")
+    private Integer recordId;
+
+    @ApiModelProperty(value = "申诉ai审批过程")
+    @TableField("appeal_ai_approval")
+    private String appealAiApproval;
 }

+ 5 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreDictionary.java

@@ -84,6 +84,11 @@ public class StoreDictionary extends Model<StoreDictionary> {
     @TableField("hidden")
     private Integer hidden;
 
+    @ApiModelProperty(value = "排序ID")
+    @TableField("sort_id")
+    private Integer sortId;
+
+
     @Override
     protected Serializable pkVal() {
         return this.id;

+ 73 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreVerificationCode.java

@@ -0,0 +1,73 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 短信验证码记录
+ */
+@Data
+@TableName("store_verification_code")
+public class StoreVerificationCode extends Model<StoreVerificationCode> {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("主键id")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("业务类型 (0:登录,1:修改密码,2:注册,3:修改手机号,4:注销店铺,5:注销账号,6:忘记密码)")
+    @TableField("business_type")
+    private Integer businessType;
+
+    @ApiModelProperty("手机号")
+    @TableField("phone")
+    private String phone;
+
+    @ApiModelProperty("端区分(0:用户,1:商家,2:律师端)")
+    @TableField("app_type")
+    private Integer appType;
+
+    @ApiModelProperty("验证码")
+    @TableField("code")
+    private String code;
+
+    @ApiModelProperty("创建时间")
+    @TableField("created_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty("更新时间")
+    @TableField("updated_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty("创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty("更新人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty("删除标志(0:正常,1:删除)")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+}
+

+ 6 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/LawyerConsultationOrderDto.java

@@ -124,5 +124,11 @@ public class LawyerConsultationOrderDto extends Model<LawyerConsultationOrderDto
     @ApiModelProperty(value = "律师接单状态  0接单  1拒绝接单")
     @TableField("accept_orders_status")
     private  Integer acceptOrdersStatus;
+
+
+    @ApiModelProperty(value = "支付方式 1支付宝 2微信")
+    @TableField("pay_type")
+    private  String payType;
+
 }
 

+ 6 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/PayStatusRequest.java

@@ -26,5 +26,11 @@ public class PayStatusRequest implements Serializable {
 
     @ApiModelProperty(value = "订单状态, 0:待支付,1.待接单 2:进行中, 3:已完成, 4:已取消,5.已退款")
     private Integer orderStatus;
+
+    @ApiModelProperty(value = "支付宝或VX的str")
+    private String orderStr;
+    
+    @ApiModelProperty
+    private String alipayNo;
 }
 

+ 3 - 3
alien-entity/src/main/java/shop/alien/entity/store/excelVo/BusinessSectionExcelVo.java

@@ -17,15 +17,15 @@ import shop.alien.entity.store.excelVo.util.ExcelHeader;
 @ApiModel(value = "BusinessSectionExcelVo对象", description = "经营版块Excel导入导出对象")
 public class BusinessSectionExcelVo {
 
-    @ExcelHeader("一级分类名称")
+    @ExcelHeader("经营板块")
     @ApiModelProperty(value = "一级分类名称(必填,如果二级和三级为空,则创建一级分类)")
     private String firstLevelName;
 
-    @ExcelHeader("二级分类名称")
+    @ExcelHeader("经营种类")
     @ApiModelProperty(value = "二级分类名称(可选,如果三级为空,则创建二级分类)")
     private String secondLevelName;
 
-    @ExcelHeader("三级分类名称")
+    @ExcelHeader("分类")
     @ApiModelProperty(value = "三级分类名称(可选,如果填写则创建三级分类)")
     private String thirdLevelName;
 

+ 34 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/AiApproveStoreInfo.java

@@ -0,0 +1,34 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 店铺审核 - AI 服务请求体
+ */
+@Data
+public class AiApproveStoreInfo {
+
+    @ApiModelProperty(value = "用户id")
+    private String userId;
+
+    @ApiModelProperty(value = "商户名称(公司/门店名称)")
+    private String merchant_name;
+
+    @ApiModelProperty(value = "经营范围 / 业务描述")
+    private String business_scope;
+
+    @ApiModelProperty(value = "联系人姓名")
+    private String contact_name;
+
+    @ApiModelProperty(value = "联系人手机号")
+    private String contact_phone;
+
+    @ApiModelProperty(value = "联系人邮箱")
+    private String contact_email;
+
+    @ApiModelProperty(value = "证照/资质门头照片列表")
+    private List<String> license_images;
+}

+ 5 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java

@@ -276,5 +276,10 @@ public class LawyerConsultationOrderVO implements Serializable {
             return null;
         }
     }
+
+    @ApiModelProperty(value = "支付方式 1支付宝 2微信")
+    @TableField("pay_type")
+    private  String payType;
+
 }
 

+ 33 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreCommentCountVo.java

@@ -0,0 +1,33 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 评价计数VO
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/12/01
+ */
+@Data
+@JsonInclude
+public class StoreCommentCountVo {
+
+    @ApiModelProperty(value = "全部评价数")
+    private Integer totalCount;
+
+    @ApiModelProperty(value = "有图评价数")
+    private Integer imageCount;
+
+    @ApiModelProperty(value = "好评数")
+    private Integer goodCount;
+
+    @ApiModelProperty(value = "中评数")
+    private Integer midCount;
+
+    @ApiModelProperty(value = "差评数")
+    private Integer badCount;
+}
+

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

@@ -214,4 +214,7 @@ public class StoreInfoVo extends StoreInfo {
 
     @ApiModelProperty(value = "动态数量")
     private Integer dynamicsNum;
+
+    @ApiModelProperty(value = "是否提供餐食")
+    private Integer  mealsFlag;
 }

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

@@ -98,7 +98,8 @@ public interface LawyerConsultationOrderMapper extends BaseMapper<LawyerConsulta
             "lawyer_earnings ," +
             "accept_orders_time ," +
             "reason_order_refusal ," +
-            "accept_orders_status " +
+            "accept_orders_status ," +
+            "pay_type " +
             ")"+
             "VALUES" +
             " (" +
@@ -130,7 +131,8 @@ public interface LawyerConsultationOrderMapper extends BaseMapper<LawyerConsulta
             "#{lawyerEarnings} ," +
             "#{acceptOrdersTime} ," +
             "#{reasonOrderRefusal} ," +
-            "#{acceptOrdersStatus}" +
+            "#{acceptOrdersStatus} ," +
+            "#{payType} " +
             ")")
             int insertOrder(LawyerConsultationOrderDto order);
 
@@ -396,6 +398,7 @@ public interface LawyerConsultationOrderMapper extends BaseMapper<LawyerConsulta
             "        lco.alipay_no,\n" +
             "        lco.order_str,\n" +
             "        lco.apply_refund_status,\n" +
+            "        lco.pay_type,\n" +
             "        CASE\n" +
             "        WHEN EXISTS (SELECT 1 FROM lawyer_order_review lor WHERE lor.order_id = lco.id) THEN '1'\n" +
             "        ELSE '2'\n" +

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/RefundRecordMapper.java

@@ -0,0 +1,17 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.store.RefundRecord;
+
+/**
+ * <p>
+ * 退款记录表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface RefundRecordMapper extends BaseMapper<RefundRecord> {
+
+}
+

+ 12 - 0
alien-entity/src/main/java/shop/alien/mapper/SecondAiTaskMapper.java

@@ -0,0 +1,12 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.SecondAiTask;
+
+/**
+ * second_ai_task Mapper
+ */
+@Mapper
+public interface SecondAiTaskMapper extends BaseMapper<SecondAiTask> {
+}

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

@@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
 import shop.alien.entity.store.StoreCommentAppeal;
 import shop.alien.entity.store.vo.StoreCommentAppealVo;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 评论申诉表 Mapper 接口
@@ -63,4 +65,30 @@ public interface StoreCommentAppealMapper extends BaseMapper<StoreCommentAppeal>
             "left join store_user e on a.store_id = e.store_id and e.delete_flag = 0  ${ew.customSqlSegment}")
     StoreCommentAppealVo getCommentDetail(@Param(Constants.WRAPPER) QueryWrapper<StoreCommentAppealVo> queryWrapper);
 
+
+    /**
+     * 申诉列表
+     *
+     * @return List<Map < String, Object>>
+     */
+    @Select("SELECT sca.id, sca.appeal_reason, sca.appeal_status, sc.comment_content, si.img_url, sci.user_img_url, sc.id comment_id, sc.business_id " +
+            "FROM store_comment_appeal sca " +
+            "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")
+    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")
+    List<StoreCommentAppeal> getPendingAppeals();
+
+    @Update("UPDATE store_comment_appeal " +
+            "SET appeal_status = #{appealStatus}, final_result = #{finalResult} " +
+            "WHERE record_id = #{recordId}")
+    void updateByRecordId(@Param("recordId") Integer recordId,
+                          @Param("appealStatus") Integer appealStatus,
+                          @Param("finalResult") String finalResult);
 }

+ 13 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreVerificationCodeMapper.java

@@ -0,0 +1,13 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreVerificationCode;
+
+/**
+ * 短信验证码 Mapper
+ */
+@Mapper
+public interface StoreVerificationCodeMapper extends BaseMapper<StoreVerificationCode> {
+}
+

+ 20 - 0
alien-entity/src/main/resources/mapper/StoreVerificationCodeMapper.xml

@@ -0,0 +1,20 @@
+<?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.StoreVerificationCodeMapper">
+
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.StoreVerificationCode">
+        <id column="id" property="id"/>
+        <result column="business_type" property="businessType"/>
+        <result column="phone" property="phone"/>
+        <result column="app_type" property="appType"/>
+        <result column="code" property="code"/>
+        <result column="created_time" property="createdTime"/>
+        <result column="updated_time" property="updatedTime"/>
+        <result column="created_user_id" property="createdUserId"/>
+        <result column="updated_user_id" property="updatedUserId"/>
+        <result column="delete_flag" property="deleteFlag"/>
+    </resultMap>
+
+</mapper>
+

+ 1 - 1
alien-gateway/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test

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

@@ -3,6 +3,7 @@ package shop.alien.job.feign;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.second.SecondGoods;
 
 @FeignClient(name = "alien-second", url = "${feign.alienSecond.url}")
 public interface SecondGoodsFeign {
@@ -24,4 +25,7 @@ public interface SecondGoodsFeign {
      */
     @GetMapping("/video/moderation/processTask")
     boolean processTask(@RequestParam("taskId") String taskId);
+
+    @GetMapping("/secondGoods/approveAndListGoods")
+    boolean approveAndListGoods(SecondGoods goods);
 }

+ 217 - 0
alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java

@@ -0,0 +1,217 @@
+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.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.http.*;
+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.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author Lhaibo
+ * @date 2025/11/28
+ * @desc: xxl-job
+ * AI调用审核任务
+ * @since 1.0.0
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiCheckXxlJob {
+
+    // 添加RestTemplate用于HTTP调用
+    private final RestTemplate restTemplate;
+
+    private final LifeUserViolationMapper lifeUserViolationMapper;
+
+    private final StoreDictionaryMapper storeDictionaryMapper;
+
+    private final LifeUserMapper lifeUserMapper;
+
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;
+
+    // 第三方接口地址 登录接口URL
+    @Value("${third-party-login.base-url}")
+    private String loginUrl;
+
+    //用户名
+    @Value("${third-party-user-name.base-url}")
+    private String userName;
+
+    //密码
+    @Value("${third-party-pass-word.base-url}")
+    private String passWord;
+
+    /**
+     * AI自动审核任务处理器
+     * <p>
+     * 定时任务方法,用于批量处理用户违规举报记录,通过AI接口进行自动审核。
+     * 主要流程:
+     * 1. 查询所有待处理的用户违规记录
+     * 2. 遍历每条记录,组装审核请求数据
+     * 3. 调用AI审核接口进行自动审核(当前代码中已准备请求体,待实现接口调用)
+     * </p>
+     *
+     * @author Lhaibo
+     * @date 2025/11/28
+     * @since 1.0.0
+     */
+    @XxlJob("aiCheckJobHandler")
+    public void aiCheckJobHandler() {
+        log.info("开始执行AI自动审核任务");
+
+        try {
+            log.info("登录Ai服务获取token..." + loginUrl);
+            //构建请求参数
+            MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+            formData.add("username", "admin");    // 表单字段 1:用户名
+            formData.add("password", "123456");    // 表单字段 2:密码
+
+            //设置请求头
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+            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);
+            } catch (Exception e) {
+                log.error("类:PostMethod 方法:post", 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::getProcessingStatus, "5"));
+
+                    // 遍历每条违规记录,组装AI审核请求数据
+                    for (LifeUserViolation violation : lifeUserViolations) {
+                        // 初始化请求体Map
+                        Map<String, Object> requestBody = new HashMap<>();
+
+                        // 设置投诉记录ID
+                        requestBody.put("complaint_id", violation.getId());
+
+                        // 查询投诉类型字典信息
+                        StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>()
+                                .eq(StoreDictionary::getTypeName, violation.getDictId()).eq(StoreDictionary::getTypeName, violation.getDictType()));
+                        String complaint_type = "";
+                        if (storeDictionary != null) {
+                            // 设置投诉类型
+                            complaint_type = storeDictionary.getDictDetail();
+                        }
+                        requestBody.put("complaint_type", complaint_type);
+                        // 设置举报人信息
+                        requestBody.put("reporter_user_id", violation.getReportingUserId());
+                        requestBody.put("reporter_user_type", violation.getReportingUserType());
+                        requestBody.put("reporter_info", lifeUserMapper.selectById(violation.getReportingUserId()));
+
+                        // 查询被举报的商品记录
+                        SecondGoodsRecord secondGoodsRecord = secondGoodsRecordMapper.selectById(violation.getBusinessId());
+                        if (secondGoodsRecord != null) {
+                            // 设置被举报人信息
+                            requestBody.put("reported_user_id", secondGoodsRecord.getUserId());
+                            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("complaint_text", violation.getOtherReasonContent());
+
+                        // 设置证据图片数组(将逗号分隔的字符串转换为数组)
+                        requestBody.put("evidence_images", violation.getReportEvidenceImg() != null ? violation.getReportEvidenceImg().split(",") : new String[0]);
+
+                        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("http://192.168.2.78:9000/ai/auto-review/api/v1/product_complaint_record/submit", request, String.class);
+                            log.info("AI自动审核结果:{}", response.getBody());
+                        } catch (Exception e) {
+                            log.error("AI自动审核请求异常", e);
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("AI自动审核任务执行异常", e);
+        }
+
+        log.info("AI自动审核任务执行完成");
+    }
+
+    /**
+     * 二手商品风控记录审核任务
+     * <p>
+     * 定时查询风控记录表中待处理的数据(risk_status = 0),
+     * 预留后续风控审核/处理逻辑(如调用 AI 服务、通知运营等)。
+     * </p>
+     */
+    @XxlJob("riskControlCheckJobHandler")
+    public void riskControlCheckJobHandler() {
+        log.info("开始执行二手商品风控记录审核任务");
+
+        try {
+            // 查询风控记录表中待处理的记录(risk_status = 0)
+            List<SecondRiskControlRecord> riskControlRecords = secondRiskControlRecordMapper.selectList(
+                    new LambdaQueryWrapper<SecondRiskControlRecord>()
+                            .eq(SecondRiskControlRecord::getRiskStatus, 0)
+            );
+
+            log.info("本次待处理风控记录数量:{}", riskControlRecords.size());
+
+            // 按 ruleType 和 businessId 进行分组,生成新的嵌套结构
+            List<Map<String, List<SecondRiskControlRecord>>> groupedByRuleAndBusiness =
+                    (List<Map<String, List<SecondRiskControlRecord>>>) riskControlRecords.stream()
+                            .collect(Collectors.groupingBy(
+                                    SecondRiskControlRecord::getRuleType,
+                                    Collectors.groupingBy(SecondRiskControlRecord::getBusinessId)
+                            )).values();
+
+            for (Map<String, List<SecondRiskControlRecord>> byRuleAndBusiness : groupedByRuleAndBusiness) {
+
+
+            }
+        } catch (Exception e) {
+            log.error("二手商品风控记录审核任务执行异常", e);
+        }
+
+        log.info("二手商品风控记录审核任务执行完成");
+    }
+}

+ 183 - 0
alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java

@@ -0,0 +1,183 @@
+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.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.LifeUserViolation;
+import shop.alien.entity.store.SecondAiTask;
+import shop.alien.mapper.LifeUserViolationMapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiUserViolationJob {
+
+    private final RestTemplate restTemplate;
+    private final LifeUserViolationMapper lifeUserViolationMapper;
+
+    @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";
+
+    private String aiUserViolationCheckUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/user_complaint_record/result";
+
+    @XxlJob("getAiUserViolationResult")
+    public R<String> getAiUserViolationResult() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用AI举报用户辅助系统 登录接口失败");
+        }
+        return getAiUserViolationCheck(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);
+
+        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 R<String> getAiUserViolationCheck(String accessToken) {
+
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+        // 查询所有状态为处理中的申诉
+        List<LifeUserViolation> pendingTasks = lifeUserViolationMapper.selectList(
+                new QueryWrapper<LifeUserViolation>()
+                        .eq("processing_status", "0")
+                        .isNotNull("ai_task_id")
+        );
+
+        // 循环调用查询结果接口
+        for (LifeUserViolation task : pendingTasks) {
+            String completedUrl = buildCompletedUrl(task.getAiTaskId());
+
+            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("AI举报用户审核提交成功, 返回: {}", analyzeBody);
+
+                    JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+                    JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+                    if (dataJsonObj == null) {
+                        log.error("AI举报用户审核返回数据为空");
+                        R.fail("AI举报用户审核返回数据为空");
+                        continue;
+                    }
+
+                    // 获取task_id用于后续查询
+                    String taskId = dataJsonObj.getString("task_id");
+                    if (taskId == null) {
+                        log.error("AI举报用户审核返回task_id为空");
+                        R.fail("AI举报用户审核返回task_id为空");
+                        continue;
+                    }
+
+                    LifeUserViolation aiTask = new LifeUserViolation();
+                    aiTask.setAiTaskId(taskId);
+                    if (dataJsonObj.getString("status").equals("pending")) {
+                        R.fail("审核未结束");
+                        continue;
+                    }
+
+                    if (dataJsonObj.getString("status").equals("completed")) {
+                        if (dataJsonObj.getString("is_valid").equals("true")) {
+                            aiTask.setProcessingStatus("1");
+                            aiTask.setReportResult(dataJsonObj.getString("decision_reason"));
+                        } else {
+                            aiTask.setProcessingStatus("0");
+                            aiTask.setReportResult(dataJsonObj.getString("decision_reason"));
+                        }
+                    }
+
+                    QueryWrapper<LifeUserViolation> queryWrapper = new QueryWrapper<>();
+                    queryWrapper.eq("ai_task_id", taskId);
+                    LifeUserViolation lifeUserViolation = lifeUserViolationMapper.selectOne(queryWrapper);
+                    if (lifeUserViolation == null) {
+                        log.error("AI举报用户不存在");
+                        R.fail("AI举报用户不存在");
+                        continue;
+                    }
+                    lifeUserViolationMapper.update(aiTask, queryWrapper);
+
+                } else {
+                    if (analyzeResp != null) {
+                        log.error("调用AI举报用户审核接口失败, http状态: {}", analyzeResp.getStatusCode());
+                        R.fail("调用AI举报用户审核接口失败, http状态: " + analyzeResp.getStatusCode());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("调用差评申述查询结果接口异常", e);
+            }
+        }
+        return R.success("调用AI举报用户审核结果接口完成");
+    }
+
+    private String buildCompletedUrl(String recordId) {
+        String baseUrl = aiUserViolationCheckUrl;
+        if (!StringUtils.hasText(baseUrl)) {
+            throw new IllegalStateException("ai举报用户接口地址未配置");
+        }
+        if (baseUrl.endsWith("/")) {
+            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+        }
+        // 构建新的URL格式: /api/v1/audit_task/getResult?task_id={recordId}
+        return baseUrl + "?task_id=" + recordId;
+    }
+}

+ 190 - 0
alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java

@@ -0,0 +1,190 @@
+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;
+
+    @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);
+
+        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 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);
+        }
+        // 构建新的URL格式: /api/v1/audit_task/getResult?task_id={recordId}
+        return baseUrl + "?task_id=" + recordId;
+    }
+}

+ 445 - 10
alien-job/src/main/java/shop/alien/job/store/AiTagJob.java

@@ -2,31 +2,29 @@ package shop.alien.job.store;
 
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 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.Value;
 import org.springframework.http.*;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
-import shop.alien.entity.store.StoreCommentSummary;
-import shop.alien.entity.store.StoreCommentSummaryInterest;
-import shop.alien.entity.store.TagsMain;
-import shop.alien.entity.store.TagsSynonym;
-import shop.alien.mapper.StoreCommentSummaryInterestMapper;
-import shop.alien.mapper.StoreCommentSummaryMapper;
-import shop.alien.mapper.TagsMainMapper;
-import shop.alien.mapper.TagsSynonymMapper;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.AiApproveStoreInfo;
+import shop.alien.mapper.*;
 
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+
+import static com.alipay.api.internal.util.AlipayUtils.getFileSuffix;
 
 /**
  * 调用AI标签数据服务类
@@ -49,6 +47,12 @@ public class AiTagJob {
 
     private final StoreCommentSummaryInterestMapper storeCommentSummaryInterestMapper;
 
+    private final LifeUserDynamicsMapper lifeUserDynamicsMapper;
+
+    private final StoreClockInMapper storeClockInMapper;
+
+    private final StoreInfoMapper storeInfoMapper;
+
     // 第三方接口地址 获取所有标签主表信息
     @Value("${third-party-tag.base-url}")
     private String tagMainUrl;
@@ -73,6 +77,13 @@ public class AiTagJob {
     @Value("${third-party-savetag.base-url}")
     private String saveTagUrl;
 
+    // 第三方接口地址 内容合规检测接口
+    @Value("${third-party-contentcheck.base-url}")
+    private String contentComplianceUrl;
+
+    @Value("${third-party-getresult.base-url}")
+    private String getResultUrl;
+
     //用户名
     @Value("${third-party-user-name.base-url}")
     private String userName;
@@ -567,6 +578,430 @@ public class AiTagJob {
         return R.success("任务执行失败 状态码" + responseEntity.getStatusCodeValue());
     }
 
+    /**
+     * 利用AI审核违规内容
+     */
+    @XxlJob("contentComplianceCheckTask")
+    public R<String> contentComplianceCheckTask() {
+        List<LifeUserDynamics> lifeUserDynamics = lifeUserDynamicsMapper.selectList(new LambdaQueryWrapper<LifeUserDynamics>()
+                .eq(LifeUserDynamics::getCheckFlag, 0).eq(LifeUserDynamics::getDeleteFlag, 0));
+
+        // 只依赖数据库字段即可判定待审核的动态,避免重复提交
+        // 常见图片后缀(可按需添加,如 .heic、.svg 等)
+        HashSet<String> IMAGE_SUFFIXES = new HashSet<>(Arrays.asList(
+                "jpg", "jpeg", "png", "gif", "bmp", "webp", "heic", "svg", "tiff"
+        ));
+        // 常见视频后缀(可按需添加,如 .avi、.flv 等)
+        HashSet<String> VIDEO_SUFFIXES = new HashSet<>(Arrays.asList(
+                "mp4", "mov", "mkv", "avi", "flv", "wmv", "mpeg", "mpg", "webm"
+        ));
+
+        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);
+        }
+        HttpHeaders aiHeaders = new HttpHeaders();
+        if (postForEntity != null && postForEntity.getStatusCodeValue() == 200) {
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t" + postForEntity.getBody());
+            String responseBody = postForEntity.getBody();
+            JSONObject jsonObject = JSONObject.parseObject(responseBody);
+            if (jsonObject != null) {
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                String accessToken = dataJson.getString("access_token");
+
+                aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+                aiHeaders.set("Authorization", "Bearer " + accessToken);
+            }
+        }
+
+        List<StoreClockIn> storeClockIns = storeClockInMapper.selectList(new LambdaQueryWrapper<StoreClockIn>()
+                .eq(StoreClockIn::getDeleteFlag, 0).eq(StoreClockIn::getCheckFlag, 0));
+        for (StoreClockIn storeClockIn : storeClockIns) {
+            String imagePath = storeClockIn.getImgUrl() != null ? storeClockIn.getImgUrl() : "";
+            List<String> imageList = new ArrayList<>();
+            List<String> videoList = new ArrayList<>();
+            // 按分隔符拆分路径数组(处理连续分隔符如 ",," 产生的空字符串)
+            String[] allPaths = imagePath.split(",");
+            for (String path : allPaths) {
+                // 去除路径前后空格(避免 " a.jpg " 这类情况)
+                String trimmedPath = path.trim();
+                if (trimmedPath.isEmpty()) {
+                    continue; // 跳过空路径
+                }
+                // 获取文件后缀(忽略大小写)
+                // 找到最后一个 "." 的位置
+                int lastDotIndex = trimmedPath.lastIndexOf('.');
+                // 截取后缀(从 "." 后一位到结尾)
+                String suffix = trimmedPath.substring(lastDotIndex + 1);
+                // 分类添加到对应列表
+                if (IMAGE_SUFFIXES.contains(suffix)) {
+                    imageList.add(trimmedPath);
+                } else if (VIDEO_SUFFIXES.contains(suffix)) {
+                    videoList.add(trimmedPath);
+                }
+            }
+            // 对每一条动态逐条申请 token 并提交,避免批量失败导致重试逻辑复杂化
+            try {
+                Map<String, Object> jsonBody = new HashMap<>();
+                // text/img/video 三种维度一起传入 AI,便于一次完成合规审查
+                jsonBody.put("text", storeClockIn.getContent());
+                jsonBody.put("image_urls", imageList);
+                jsonBody.put("video_urls", videoList);
+
+                HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
+                ResponseEntity<String> response = null;
+                try {
+                    response = restTemplate.postForEntity(contentComplianceUrl, request, String.class);
+                    if (response.getStatusCodeValue() != 200) {
+                        log.error("AI内容审核接口调用失败 http状态:" + response.getStatusCode());
+                    }
+                    JSONObject responseNode = JSONObject.parseObject(response.getBody());
+                    if (responseNode == null) {
+                        log.error("AI接口调用失败,响应内容为空");
+                    }
+                    Integer code = null;
+                    if (responseNode != null) {
+                        code = responseNode.getInteger("code");
+                        if (code == 200) {
+                            JSONObject dataNode = JSONObject.from(responseNode.get("data"));
+                            // 审核发起后仅标记 checkFlag=1 并保存任务号,等待回调或后续轮询更新
+                            StoreClockIn clockIn = new StoreClockIn();
+                            clockIn.setId(storeClockIn.getId());
+                            clockIn.setCheckFlag(1);
+                            clockIn.setAiTaskId(dataNode.get("task_id").toString());
+                            storeClockInMapper.updateById(clockIn);
+                            log.info("动态审核成功,AI返回内容: {}", response.getBody());
+                            XxlJobHelper.handleSuccess("动态内容审核任务执行成功");
+                        }
+                    } else {
+                        log.error("AI接口调用失败,错误码: " + code);
+                    }
+                } catch (Exception e) {
+                    log.error("调用AI内容审核接口失败", e);
+                }
+            } catch (RuntimeException ex) {
+                XxlJobHelper.handleFail("动态内容审核任务执行失败:" + ex.getMessage());
+                return R.fail("动态内容审核任务执行失败:" + ex.getMessage());
+            }
+        }
+
+        for (LifeUserDynamics lifeUserDynamic : lifeUserDynamics) {
+            String imagePath = lifeUserDynamic.getImagePath();
+            List<String> imageList = new ArrayList<>();
+            List<String> videoList = new ArrayList<>();
+            // 按分隔符拆分路径数组(处理连续分隔符如 ",," 产生的空字符串)
+            String[] allPaths = imagePath.split(",");
+            for (String path : allPaths) {
+                // 去除路径前后空格(避免 " a.jpg " 这类情况)
+                String trimmedPath = path.trim();
+                if (trimmedPath.isEmpty()) {
+                    continue; // 跳过空路径
+                }
+                // 获取文件后缀(忽略大小写)
+                // 找到最后一个 "." 的位置
+                int lastDotIndex = trimmedPath.lastIndexOf('.');
+                // 截取后缀(从 "." 后一位到结尾)
+                String suffix = trimmedPath.substring(lastDotIndex + 1);
+                // 分类添加到对应列表
+                if (IMAGE_SUFFIXES.contains(suffix)) {
+                    imageList.add(trimmedPath);
+                } else if (VIDEO_SUFFIXES.contains(suffix)) {
+                    videoList.add(trimmedPath);
+                }
+            }
+            try {
+                Map<String, Object> jsonBody = new HashMap<>();
+                // text/img/video 三种维度一起传入 AI,便于一次完成合规审查
+                jsonBody.put("text", lifeUserDynamic.getContext());
+                jsonBody.put("image_urls", imageList);
+                jsonBody.put("video_urls", videoList);
+
+                HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
+                ResponseEntity<String> response = null;
+                try {
+                    response = restTemplate.postForEntity(contentComplianceUrl, request, String.class);
+                    if (response.getStatusCodeValue() != 200) {
+                        log.error("AI内容审核接口调用失败 http状态:" + response.getStatusCode());
+                    }
+                    JSONObject responseNode = JSONObject.parseObject(response.getBody());
+                    if (responseNode == null) {
+                        log.error("AI接口调用失败,响应内容为空");
+                    }
+                    Integer code = null;
+                    if (responseNode != null) {
+                        code = responseNode.getInteger("code");
+                        if (code == 200) {
+                            JSONObject dataNode = JSONObject.from(responseNode.get("data"));
+                            // 审核发起后仅标记 checkFlag=1 并保存任务号,等待回调或后续轮询更新
+                            LifeUserDynamics dynamics = new LifeUserDynamics();
+                            dynamics.setId(lifeUserDynamic.getId());
+                            dynamics.setCheckFlag(1);
+                            dynamics.setAiTaskId(dataNode.get("task_id").toString());
+                            lifeUserDynamicsMapper.updateById(dynamics);
+                            log.info("动态审核成功,AI返回内容: {}", response.getBody());
+                            XxlJobHelper.handleSuccess("动态内容审核任务执行成功");
+                        }
+                    } else {
+                        log.error("AI接口调用失败,错误码: " + code);
+                    }
+                } catch (Exception e) {
+                    log.error("调用AI内容审核接口失败", e);
+                }
+            } catch (RuntimeException ex) {
+                XxlJobHelper.handleFail("动态内容审核任务执行失败:" + ex.getMessage());
+                return R.fail("动态内容审核任务执行失败:" + ex.getMessage());
+            }
+        }
+        return R.success("动态内容审核任务执行成功");
+    }
+
+    /**
+     * 获取审核违规内容结果
+     */
+    @XxlJob("getCheckTask")
+    public R<String> getCheckTask() {
+        List<LifeUserDynamics> lifeUserDynamics = lifeUserDynamicsMapper.selectList(new LambdaQueryWrapper<LifeUserDynamics>()
+                .eq(LifeUserDynamics::getCheckFlag, 1).eq(LifeUserDynamics::getDeleteFlag, 0));
+        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);
+        }
+        RestTemplate restTemplateWithAuth = new RestTemplate();
+        if (postForEntity != null && postForEntity.getStatusCodeValue() == 200) {
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t" + postForEntity.getBody());
+            String responseBody = postForEntity.getBody();
+            JSONObject jsonObject = JSONObject.parseObject(responseBody);
+            if (jsonObject != null) {
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                String accessToken = dataJson.getString("access_token");
+                List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
+                interceptors.add((request, body, execution) -> {
+                    request.getHeaders().set("Authorization", "Bearer " + accessToken);
+                    return execution.execute(request, body);
+                });
+                restTemplateWithAuth.setInterceptors(interceptors);
+            }
+        }
+
+        List<StoreClockIn> storeClockIns = storeClockInMapper.selectList(new LambdaQueryWrapper<StoreClockIn>().eq(StoreClockIn::getCheckFlag, 1));
+        for (StoreClockIn storeClockIn : storeClockIns) {
+            // 针对已提交且未删除的动态轮询查询结果
+            try {
+                ResponseEntity<String> response = null;
+                try {
+                    response = restTemplateWithAuth.getForEntity(getResultUrl + "?task_id=" + storeClockIn.getAiTaskId(), String.class);
+                    if (response.getStatusCodeValue() != 200) {
+                        log.error("AI内容审核结果获取接口调用失败 http状态:" + response.getStatusCode());
+                    }
+                    JSONObject responseNode = JSONObject.parseObject(response.getBody());
+                    if (responseNode == null) {
+                        log.error("AI接口调用失败,响应内容为空");
+                    }
+                    Integer code = null;
+                    if (responseNode != null) {
+                        code = responseNode.getInteger("code");
+                        if (code == 200) {
+                            JSONObject dataNode = JSONObject.from(responseNode.get("data"));
+                            StoreClockIn clockIn = new StoreClockIn();
+                            clockIn.setId(storeClockIn.getId());
+                            if ("completed".equals(dataNode.get("status"))) {
+                                if (!(boolean) dataNode.get("is_compliant")) {
+                                    // 只要 AI 判定不合规,立即禁用动态并记录原因
+                                    clockIn.setDeleteFlag(1);
+                                    clockIn.setReason(String.valueOf(dataNode.get("failure_reason")));
+                                }
+                                clockIn.setCheckFlag(2);
+                                storeClockInMapper.updateById(clockIn);
+                                if (!(boolean) dataNode.get("is_compliant")) {
+                                    storeClockInMapper.deleteById(clockIn);
+                                }
+                                log.info("动态审核结果获取成功,AI返回内容: {}", response.getBody());
+                                XxlJobHelper.handleSuccess("动态内容审核任务结果获取执行成功");
+                            } else {
+                                log.info("动态审核未完成,AI返回内容: {}", response.getBody());
+                            }
+                        } else {
+                            log.error("AI接口调用失败,错误码: " + code);
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("调用AI内容审核结果获取接口失败", e);
+                }
+            } catch (RuntimeException ex) {
+                XxlJobHelper.handleFail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
+                return R.fail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
+            }
+        }
+
+        for (LifeUserDynamics lifeUserDynamic : lifeUserDynamics) {
+            // 针对已提交且未删除的动态轮询查询结果
+            try {
+                ResponseEntity<String> response = null;
+                try {
+                    response = restTemplateWithAuth.getForEntity(getResultUrl + "?task_id=" + lifeUserDynamic.getAiTaskId(), String.class);
+                    if (response.getStatusCodeValue() != 200) {
+                        log.error("AI内容审核结果获取接口调用失败 http状态:" + response.getStatusCode());
+                    }
+                    JSONObject responseNode = JSONObject.parseObject(response.getBody());
+                    if (responseNode == null) {
+                        log.error("AI接口调用失败,响应内容为空");
+                    }
+                    Integer code = null;
+                    if (responseNode != null) {
+                        code = responseNode.getInteger("code");
+                        if (code == 200) {
+                            JSONObject dataNode = JSONObject.from(responseNode.get("data"));
+                            LifeUserDynamics dynamics = new LifeUserDynamics();
+                            dynamics.setId(lifeUserDynamic.getId());
+                            if ("completed".equals(dataNode.get("status"))) {
+                                if (!(boolean) dataNode.get("is_compliant")) {
+                                    // 只要 AI 判定不合规,立即禁用动态并记录原因
+                                    dynamics.setEnableStatus(1);
+                                    dynamics.setReason(String.valueOf(dataNode.get("failure_reason")));
+                                }
+                                dynamics.setCheckFlag(2);
+                                lifeUserDynamicsMapper.updateById(dynamics);
+                                log.info("动态审核结果获取成功,AI返回内容: {}", response.getBody());
+                                XxlJobHelper.handleSuccess("动态内容审核任务结果获取执行成功");
+                            } else {
+                                log.info("动态审核未完成,AI返回内容: {}", response.getBody());
+                            }
+                        } else {
+                            log.error("AI接口调用失败,错误码: " + code);
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("调用AI内容审核结果获取接口失败", e);
+                }
+            } catch (RuntimeException ex) {
+                XxlJobHelper.handleFail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
+                return R.fail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
+            }
+        }
+        return R.success("动态内容审核任务结果获取执行成功");
+    }
+
+    /**
+     * 店铺AI审核任务
+     * <p>
+     * 定时查询 store_info 表中审核状态为 0(待审核)的门店,
+     * 后续可在此处调用 AI 审核接口进行自动审核。
+     * </p>
+     */
+    @XxlJob("aiApproveStoreInfoTask")
+    public R<String> aiApproveStoreInfoTask() {
+        log.info("开始执行店铺AI审核任务...");
+        try {
+            // 1. 查询待审核门店:store_application_status = 0 且 未删除
+            List<StoreInfo> pendingStores = storeInfoMapper.selectList(
+                    new LambdaQueryWrapper<StoreInfo>()
+                            .eq(StoreInfo::getStoreApplicationStatus, 0)
+                            .eq(StoreInfo::getDeleteFlag, 0)
+            );
+
+            if (pendingStores == null || pendingStores.isEmpty()) {
+                log.info("当前无待审核门店,任务结束。");
+                XxlJobHelper.handleSuccess("当前无待审核门店");
+                return R.success("当前无待审核门店");
+            }
+
+            log.info("本次待审核门店数量:{}", pendingStores.size());
+
+            // 2. 登录 AI 服务获取 token(如需调用 AI 审核接口,可在此处复用 token)
+            MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+            formData.add("username", userName);
+            formData.add("password", passWord);
+
+            HttpHeaders loginHeaders = new HttpHeaders();
+            loginHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+            HttpEntity<MultiValueMap<String, String>> loginRequest = new HttpEntity<>(formData, loginHeaders);
+
+            ResponseEntity<String> loginResponse = null;
+            try {
+                log.info("请求Ai服务登录接口===================> {}", loginUrl);
+                loginResponse = restTemplate.postForEntity(loginUrl, loginRequest, String.class);
+            } catch (Exception e) {
+                log.error("请求AI服务登录接口失败", e);
+            }
+
+            String accessToken = null;
+            if (loginResponse != null && loginResponse.getStatusCodeValue() == 200) {
+                log.info("请求Ai服务登录成功 body={}", loginResponse.getBody());
+                String body = loginResponse.getBody();
+                if (body != null) {
+                    JSONObject jsonObject = JSONObject.parseObject(body);
+                    if (jsonObject != null) {
+                        JSONObject dataJson = jsonObject.getJSONObject("data");
+                        if (dataJson != null) {
+                            accessToken = dataJson.getString("access_token");
+                        }
+                    }
+                }
+            }
+
+            if (accessToken == null) {
+                log.error("获取AI服务 access_token 失败,终止店铺AI审核任务");
+                XxlJobHelper.handleFail("获取AI服务 access_token 失败");
+                return R.fail("获取AI服务 access_token 失败");
+            }
+
+            // 3. 遍历待审核门店,构建 AiApproveStoreInfo 并预留调用 AI 审核接口(具体字段后续补充)
+            HttpHeaders aiHeaders = new HttpHeaders();
+            aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+            aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+            for (StoreInfo storeInfo : pendingStores) {
+                AiApproveStoreInfo aiApproveStoreInfo = new AiApproveStoreInfo();
+
+                HttpEntity<AiApproveStoreInfo> request = new HttpEntity<>(aiApproveStoreInfo, aiHeaders);
+                ResponseEntity<String> response = null;
+                try {
+                    response = restTemplate.postForEntity("http://192.168.2.250:9000/ai/auto-review/api/v1/merchant-onboarding/applications", request, String.class);
+                    if (response.getStatusCodeValue() != 200) {
+                        log.error("店铺AI审核接口调用失败 storeId={}, http状态={}", storeInfo.getId(), response.getStatusCode());
+                        continue;
+                    }
+                    String respBody = response.getBody();
+                    log.info("店铺AI审核返回结果 storeId={}, body={}", storeInfo.getId(), respBody);
+                    // 如需根据 AI 返回结果更新 store_info 审核状态,可在此处解析 respBody 并更新 DB
+                } catch (Exception e) {
+                    log.error("调用店铺AI审核接口异常, storeId={}", storeInfo.getId(), e);
+                }
+            }
+
+            XxlJobHelper.handleSuccess("店铺AI审核任务执行完成");
+            return R.success("店铺AI审核任务执行完成");
+        } catch (Exception e) {
+            log.error("店铺AI审核任务执行异常", e);
+            XxlJobHelper.handleFail("店铺AI审核任务执行异常: " + e.getMessage());
+            return R.fail("店铺AI审核任务执行异常: " + e.getMessage());
+        }
+    }
+
+
+
     class AnalysisRequest {
         private String start_time;
         private String end_time;

+ 419 - 0
alien-job/src/main/java/shop/alien/job/store/BadReviewAppealJob.java

@@ -0,0 +1,419 @@
+package shop.alien.job.store;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+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.apache.commons.collections4.map.HashedMap;
+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.store.StoreComment;
+import shop.alien.entity.store.StoreCommentAppeal;
+import shop.alien.entity.result.R;
+import shop.alien.mapper.StoreCommentAppealMapper;
+import shop.alien.mapper.StoreCommentMapper;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 调用AI差评申诉辅助系统服务类
+ *
+ * @author fcw
+ * @date 2025/11/20 14:21
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class BadReviewAppealJob {
+
+    private final RestTemplate restTemplate;
+
+    private final StoreCommentAppealMapper storeCommentAppealMapper;
+
+    private final StoreCommentMapper storeCommentMapper;
+
+//    @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";
+
+
+
+    /**
+     * 差评申述置信度分析接口地址
+     * 例如: http://192.168.2.250:9004/api/v1/analyze 或通过网关: http://192.168.2.250:9000/ai/auto_review/api/v1/analyze
+     */
+//    @Value("${bad-review-analyze.base-url}")
+//    private String analyzeUrl;
+
+    /**
+     * 调用AI服务获取Token
+     */
+    @XxlJob("getBadReviewAppealJob")
+    public R<String> getBadReviewAppealJob() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用差评申诉辅助系统登录任务执行失败 返回异常");
+        }
+        return invokeAnalyzeTask(accessToken);
+    }
+
+
+    @XxlJob("getAppealCompletedResult")
+    public R<String> getAppealCompletedResult() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用差评申诉辅助系统 登录接口失败");
+        }
+        return getNegativeReviewAppealCompletedResult(accessToken);
+    }
+
+    private R<String> getNegativeReviewAppealCompletedResult(String accessToken) {
+
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+        // 查询所有状态为处理中的申诉
+        List<StoreCommentAppeal> pendingAppeals = storeCommentAppealMapper.getPendingAppeals();
+        // 循环调用查询结果接口
+        for (StoreCommentAppeal appeal : pendingAppeals) {
+            String completedUrl = buildCompletedUrl(appeal.getRecordId());
+
+            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 details = analyzeJson.getJSONObject("details");
+//                    String analysisStatus = details.get("analysis_status").toString();
+//                    if (analysisStatus.equals("pending")) {
+//                        log.error("差评申述置信度分析尚未完成");
+//                        R.fail("差评申述置信度分析尚未完成");
+//                        continue;
+//                    }
+                    JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+                    if (dataJsonObj == null) {
+                        log.error("差评申述置信度分析返回数据为空");
+                        R.fail("差评申述置信度分析返回数据为空");
+                        continue;
+                    }
+
+                    // 获取record_id用于后续查询
+                    Integer recordId = dataJsonObj.getInteger("id");
+                    if (recordId == null) {
+                        log.error("差评申述置信度分析返回record_id为空");
+                        R.fail("差评申述置信度分析返回record_id为空");
+                        continue;
+                    }
+
+                    StoreCommentAppeal sCommentAppeal = new StoreCommentAppeal();
+                    sCommentAppeal.setRecordId(appeal.getRecordId());
+                    // 判断得分大小
+                    if (dataJsonObj.getDouble("user_confidence") > dataJsonObj.getDouble("merchant_confidence")){
+                        sCommentAppeal.setAppealStatus(1);
+                        sCommentAppeal.setFinalResult("已驳回");
+                    } else {
+                        sCommentAppeal.setAppealStatus(2);
+                        sCommentAppeal.setFinalResult("已同意");
+                        //假删除评论
+                        StoreComment storeComment = new StoreComment();
+                        storeComment.setId(appeal.getCommentId());
+                        storeComment.setDeleteFlag(1);
+                        storeCommentMapper.updateById(storeComment);
+                    }
+                    sCommentAppeal.setAppealAiApproval(dataJsonObj.toJSONString());
+
+                    sCommentAppeal.setRecordId(recordId);
+                    storeCommentAppealMapper.updateByRecordId(appeal.getRecordId(),
+                            sCommentAppeal.getAppealStatus(),
+                            sCommentAppeal.getFinalResult());
+                } else {
+                    if (analyzeResp != null) {
+                        log.error("调用差评申述置信度分析接口失败, http状态: {}", analyzeResp.getStatusCode());
+                        R.fail("调用差评申述置信度分析接口失败, http状态: " + analyzeResp.getStatusCode());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("调用差评申述查询结果接口异常", e);
+            }
+        }
+        return R.success("调用差评申述置信度分析结果接口完成");
+    }
+
+
+
+
+    private R<String> invokeAnalyzeTask(String token) {
+        log.info("开始调用差评申述置信度分析接口, url: {}", analyzeUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + token);
+
+        // 查询一段时间内的差评申述
+        // 循环插入
+        List<Map<String, Object>> appealList = storeCommentAppealMapper.getAppealList();
+
+        if (appealList == null || appealList.isEmpty()) {
+            log.info("没有需要处理的差评申述");
+            return R.success("没有需要处理的差评申述");
+        }
+
+        for (Map<String, Object> storeCommentAppeal : appealList) {
+            Map<String, Object> analyzeRequest = new HashedMap<>();
+
+            // 商家申诉材料
+            analyzeRequest.put("merchant_material",
+                    storeCommentAppeal.get("appeal_reason") == null ? "" : storeCommentAppeal.get("appeal_reason").toString());
+
+            // 用户差评内容
+            analyzeRequest.put("user_material",
+                    storeCommentAppeal.get("comment_content") == null ? "" : storeCommentAppeal.get("comment_content").toString());
+
+            // 商家图片:支持多张,转成 Base64 数组
+            List<String> merchantImages = new ArrayList<>();
+            String imgUrls = storeCommentAppeal.get("img_url") == null ? "" : storeCommentAppeal.get("img_url").toString();
+            if (StringUtils.hasText(imgUrls)) {
+                // 假设 img_url 是多个图片用逗号分隔的字符串
+                for (String imageUrl : imgUrls.split(",")) {
+                    String base64 = convertImageToBase64(imageUrl.trim());
+                    if (StringUtils.hasText(base64)) {
+                        merchantImages.add(base64);
+                    }
+                }
+            }
+            analyzeRequest.put("merchant_images", merchantImages);
+
+            // 用户图片:同理
+            List<String> userImages = new ArrayList<>();
+            String userImgUrls = storeCommentAppeal.get("user_img_url") == null ? "" : storeCommentAppeal.get("user_img_url").toString();
+            if (StringUtils.hasText(userImgUrls)) {
+                for (String imageUrl : userImgUrls.split(",")) {
+                    String base64 = convertImageToBase64(imageUrl.trim());
+                    if (StringUtils.hasText(base64)) {
+                        userImages.add(base64);
+                    }
+                }
+            }
+            analyzeRequest.put("user_images", userImages);
+
+            // 其他字段
+            analyzeRequest.put("case_id", storeCommentAppeal.get("comment_id") == null ? "" : storeCommentAppeal.get("comment_id").toString());
+            analyzeRequest.put("order_id", storeCommentAppeal.get("business_id") == null ? "" : storeCommentAppeal.get("business_id").toString());
+
+            HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+            ResponseEntity<String> analyzeResp = null;
+            try {
+                analyzeResp = restTemplate.postForEntity(analyzeUrl, analyzeEntity, String.class);
+            } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+                log.error("调用差评申述置信度分析接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+                continue;
+            } catch (Exception e) {
+                log.error("调用差评申述置信度分析接口异常", e);
+                continue;
+            }
+
+            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;
+                }
+
+                // 获取record_id用于后续查询
+                Integer recordId = dataJsonObj.getInteger("record_id");
+                if (recordId == null) {
+                    log.error("差评申述置信度分析返回record_id为空");
+                    R.fail("差评申述置信度分析返回record_id为空");
+                    continue;
+                }
+
+                //修改评论状态为"处理中"
+                StoreCommentAppeal sCommentAppeal = new StoreCommentAppeal();
+                sCommentAppeal.setId((Integer) storeCommentAppeal.get("id"));
+                sCommentAppeal.setAppealStatus(0);
+                sCommentAppeal.setRecordId(recordId);
+                storeCommentAppealMapper.updateById(sCommentAppeal);
+
+            } else {
+                if (analyzeResp != null) {
+                    log.error("调用差评申述置信度分析接口失败, http状态: {}", analyzeResp.getStatusCode());
+                    R.fail("调用差评申述置信度分析接口失败, http状态: " + analyzeResp.getStatusCode());
+                }
+            }
+        }
+        return R.success("调用差评申述置信度分析接口完成");
+    }
+
+    /**
+     * 将图片URL转换为Base64编码
+     *
+     * @param imageUrl 图片URL
+     * @return Base64编码字符串
+     */
+    private String convertImageToBase64(String imageUrl) {
+        // 1. 检查是否为空
+        if (!StringUtils.hasText(imageUrl)) {
+            log.warn("图片URL为空");
+            return "";
+        }
+
+        // 2. 对URL进行编码处理(解决特殊字符问题)
+        String encodedUrl = encodeImageUrl(imageUrl);
+
+        // 3. 验证URL格式
+        if (!isValidUrl(encodedUrl)) {
+            log.warn("无效的图片URL: {}", imageUrl);
+            return "";
+        }
+
+        try {
+            // 4. 下载图片并转换Base64
+            byte[] imageBytes = restTemplate.getForObject(encodedUrl, byte[].class);
+            if (imageBytes != null) {
+                return java.util.Base64.getEncoder().encodeToString(imageBytes);
+            }
+        } catch (org.springframework.web.client.HttpClientErrorException.NotFound e) {
+            log.warn("图片不存在 (404), URL: {}", imageUrl);
+            return "";
+        } catch (Exception e) {
+            log.error("图片转换为Base64失败, URL: {}", imageUrl, e);
+            return "";
+        }
+        return "";
+    }
+
+    // 新增方法:对URL中的特殊字符进行编码
+    private String encodeImageUrl(String imageUrl) {
+        try {
+            URI uri = new URI(imageUrl);
+            String scheme = uri.getScheme();
+            String host = uri.getHost();
+            String path = uri.getPath();
+            String query = uri.getQuery();
+
+            // 对路径中的特殊字符进行编码
+            String encodedPath = java.net.URLEncoder.encode(path, "UTF-8")
+                    .replace("%2F", "/")  // 保留斜杠
+                    .replace("+", "%20"); // 空格编码
+
+            StringBuilder encodedUrl = new StringBuilder();
+            encodedUrl.append(scheme).append("://").append(host).append(encodedPath);
+
+            if (query != null) {
+                encodedUrl.append("?").append(query);
+            }
+
+            return encodedUrl.toString();
+        } catch (Exception e) {
+            log.warn("URL编码失败,使用原始URL: {}", imageUrl);
+            return imageUrl;
+        }
+    }
+
+
+
+    // 辅助方法:验证URL格式合法性
+    private boolean isValidUrl(String url) {
+        try {
+            // 尝试构造URI,验证格式
+            new URI(url);
+            // 进一步检查是否以http/https开头(可选,根据业务需求)
+            return url.startsWith("http://") || url.startsWith("https://");
+        } catch (URISyntaxException e) {
+            return false;
+        }
+    }
+
+    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 buildCompletedUrl(Integer recordId) {
+        String baseUrl = resultUrl;
+        if (!StringUtils.hasText(baseUrl)) {
+            throw new IllegalStateException("差评申述分析接口地址未配置");
+        }
+        if (baseUrl.endsWith("/")) {
+            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+        }
+        String completedBase = baseUrl.replace("/api/v1/analyze", "");
+        if (!completedBase.endsWith("/")) {
+            completedBase = completedBase + "/";
+        }
+        return completedBase + "api/v1/records/" + recordId + "/completed";
+    }
+
+}

+ 1 - 1
alien-job/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test

+ 11 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/feign/AlienStoreFeign.java

@@ -2,7 +2,12 @@ package shop.alien.lawyer.feign;
 
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.result.R;
+
+import java.util.Map;
 
 /**
  * @author ssk
@@ -25,4 +30,10 @@ public interface AlienStoreFeign {
                          @RequestParam(value = "refundAmount") String refundAmount,
                          @RequestParam(value = "refundReason") String refundReason,
                          @RequestParam(value = "partialRefundCode") String partialRefundCode);
+
+    /**
+     * 微信/支付宝退款
+     */
+    @PostMapping("payment/refunds")
+    R paymentRefunds(@RequestBody Map<String, String> params);
 }

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

@@ -33,10 +33,7 @@ import shop.alien.util.common.constant.LawyerStatusEnum;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-
 import java.text.SimpleDateFormat;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -451,6 +448,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
         order.setAlipayNo(lawyerConsultationOrder.getAlipayNo());
         order.setOrderStr(lawyerConsultationOrder.getOrderStr());
         order.setPlaceId(lawyerConsultationOrder.getPlaceId());
+        order.setPayType(lawyerConsultationOrder.getPayType());
 
         // 设置订单状态
         order.setOrderStatus(0); // 待支付
@@ -541,6 +539,12 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
                 request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus());
 
         LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
+        if (null != request.getOrderStr()){
+            order.setOrderStr(request.getOrderStr());
+        }
+        if (null != request.getAlipayNo()){
+            order.setAlipayNo(request.getAlipayNo());
+        }
         order.setOrderNumber(request.getOrderNumber());
         order.setPaymentStatus(request.getPaymentStatus());
         order.setOrderStatus(request.getOrderStatus());

+ 63 - 25
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderExpirationServiceImpl.java

@@ -9,23 +9,28 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerConsultationOrder;
 import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.lawyer.config.BaseRedisService;
 import shop.alien.lawyer.config.WebSocketProcess;
 import shop.alien.lawyer.controller.AliController;
+import shop.alien.lawyer.feign.AlienStoreFeign;
 import shop.alien.lawyer.listener.RedisKeyExpirationHandler;
 import shop.alien.lawyer.service.OrderExpirationService;
 import shop.alien.mapper.LawyerConsultationOrderMapper;
 import shop.alien.mapper.LifeNoticeMapper;
 import shop.alien.mapper.LifeUserMapper;
 import shop.alien.util.common.DateUtils;
+import shop.alien.util.common.constant.PaymentEnum;
 
 
 import javax.annotation.PostConstruct;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * 訂單過期處理服務實現類
@@ -51,6 +56,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
     private final LifeNoticeMapper lifeNoticeMapper;
     private final LifeUserMapper lifeUserMapper;
     private final WebSocketProcess webSocketProcess;
+    private final AlienStoreFeign alienStoreFeign;
 
     /**
      * Redis key前綴:訂單支付超時
@@ -147,11 +153,6 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
 
                 log.info("檢測到有訂單需要退款,訂單no: {}", orderNo);
 
-                // 處理訂單退款
-                aliController.processRefund(order.getAlipayNo(),  new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString(), refundReason,"");
-
-                log.info("訂單退款成功,訂單no: {}", orderNo);
-
                 //通知
                 LifeNotice lifeNotice = buildUserLifeNotice(order, title, message);
                 WebSocketVo webSocketVo = buildWebSocketVo(lifeNotice);
@@ -161,11 +162,32 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
 
                 log.info("系统通知发送成功,訂單no: {}", orderNo);
 
-                LawyerConsultationOrder update = new LawyerConsultationOrder();
-                update.setId(order.getId());
-                update.setOrderStatus(5);
-                orderMapper.updateById( update);
+                // 處理訂單退款
+                //aliController.processRefund(order.getAlipayNo(),  new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString(), refundReason,"");
+
+                // 微信/支付宝退款
+                Map<String, String> paramMap = new HashMap<>();
+                refundParam(paramMap, order, refundReason);
+                R refunds = alienStoreFeign.paymentRefunds(paramMap);
+
+                if (refunds.getCode() == 200) {
+                    log.info("訂單退款成功,訂單no: {}", orderNo);
+
+                    //退款到账通知
+                    LifeNotice lifeNotice2 = buildUserLifeNotice(order, "退款到账通知", "您的编号为" + orderNo + "的订单,订单金额已原路返还至您的支付渠道,请查收。");
+                    WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice2);
+                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(lifeNotice2.getCreatedTime(), 2));
+
+                    lifeNoticeMapper.insert(lifeNotice2);
+                    webSocketProcess.sendMessage(lifeNotice2.getReceiverId(), JSONObject.from(webSocketVo2).toJSONString());
+
+                    log.info("系统通知发送成功,訂單no: {}", orderNo);
 
+                    LawyerConsultationOrder update = new LawyerConsultationOrder();
+                    update.setId(order.getId());
+                    update.setOrderStatus(5);
+                    orderMapper.updateById( update);
+                }
             }
             //申请退款 律师没处理
             if (expiredKey.contains(ORDER_REFUND_TIMEOUT_PREFIX)) {
@@ -179,30 +201,28 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
 
                 log.info("檢測到有訂單需要退款,訂單no: {}", orderNo);
 
+                //同意退款通知
+                LifeNotice lifeNotice = buildUserLifeNotice(order, "同意退款通知", "您的编号为" + orderNo + "的订单,律师已同意您的退款申请,订单金额将在1-3个工作日原路返还,请注意查收。");
+                WebSocketVo webSocketVo = buildWebSocketVo(lifeNotice);
+
+                lifeNoticeMapper.insert(lifeNotice);
+                webSocketProcess.sendMessage(lifeNotice.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
+
                 // 處理訂單退款
-                String refundResult = aliController.processRefund(order.getAlipayNo(), new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString(), refundReason, "");
+                //String refundResult = aliController.processRefund(order.getAlipayNo(), new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString(), refundReason, "");
 
                 // 微信/支付宝退款
-                /*paramMap.put("payType", "1".equals(order.getPayType()) ? PaymentEnum.ALIPAY.getType() : PaymentEnum.WECHAT_PAY.getType());
-                paramMap.put("outTradeNo", order.getAlipayNo());
-                paramMap.put("refundReason", refundReason);
-                paramMap.put("refundAmount", new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
-                R refunds = alienStoreFeign.paymentRefunds(paramMap);*/
+                Map<String, String> paramMap = new HashMap<>();
+                refundParam(paramMap, order, refundReason);
+                R refunds = alienStoreFeign.paymentRefunds(paramMap);
 
-                if ("调用成功".equals(refundResult)) {
+                if (refunds.getCode() == 200) {
                     log.info("訂單退款成功,訂單no: {}", orderNo);
 
-                    //同意退款通知
-                    LifeNotice lifeNotice = buildUserLifeNotice(order, "同意退款通知", "您的编号为" + orderNo + "的订单,律师已同意您的退款申请,订单金额将在1-3个工作日原路返还,请注意查收。");
-                    WebSocketVo webSocketVo = buildWebSocketVo(lifeNotice);
-
-                    lifeNoticeMapper.insert(lifeNotice);
-                    webSocketProcess.sendMessage(lifeNotice.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
-
                     //退款到账通知
                     LifeNotice lifeNotice2 = buildUserLifeNotice(order, "退款到账通知", "您的编号为" + orderNo + "的订单,订单金额已原路返还至您的支付渠道,请查收。");
-                    WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice);
-                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(lifeNotice.getCreatedTime(), 2));
+                    WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice2);
+                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(lifeNotice2.getCreatedTime(), 2));
 
                     lifeNoticeMapper.insert(lifeNotice2);
                     webSocketProcess.sendMessage(lifeNotice2.getReceiverId(), JSONObject.from(webSocketVo2).toJSONString());
@@ -221,6 +241,24 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
         }
     }
 
+    private void refundParam(Map<String,String> paramMap,LawyerConsultationOrder order,String refundReason){
+        //支付宝
+        if ("1".equals(order.getPayType())) {
+            paramMap.put("payType", PaymentEnum.ALIPAY.getType());
+            paramMap.put("outTradeNo", order.getAlipayNo());
+            paramMap.put("refundReason", refundReason);
+            paramMap.put("refundAmount", new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+        }
+        //微信
+        if ("2".equals(order.getPayType())) {
+            paramMap.put("payType", PaymentEnum.WECHAT_PAY.getType());
+            paramMap.put("outTradeNo", order.getAlipayNo());
+            paramMap.put("reason", refundReason);
+            paramMap.put("refundAmount", order.getOrderAmount().toString());
+            paramMap.put("totalAmount", order.getOrderAmount().toString());
+        }
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void handleOrderPaymentTimeout(String orderNum) {

+ 84 - 0
alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java

@@ -0,0 +1,84 @@
+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;
+
+import java.util.List;
+
+/**
+ * 二手商品审核服务接口
+ * 负责商品的图片、文本、视频审核相关业务逻辑
+ */
+public interface SecondGoodsAuditService {
+
+    /**
+     * 执行内容审核(图片、文本和视频)
+     * @param goodsDTO 商品信息DTO
+     * @param goods 商品实体
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception;
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果,true表示通过,false表示不通过
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception;
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果,true表示通过,false表示不通过
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception;
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 视频审核任务ID列表
+     */
+    List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO);
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    void processVideoModerationResult(SecondVideoTask task);
+
+    /**
+     * 审核通过后上架商品
+     * @param goods 商品信息
+     */
+    void approveAndListGoods(SecondGoods goods);
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     * @param failReason 审核失败原因
+     * @param goodsStatus 商品状态
+     */
+    void createGoodsAudit(SecondGoods goods, String failReason, Integer goodsStatus);
+
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    List<String> extractVideoUrls(List<String> imageUrls);
+
+    /**
+     * 判断URL是否为视频地址
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    boolean isVideoUrl(String url);
+}
+

+ 545 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java

@@ -0,0 +1,545 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+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.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondGoodsAudit;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.enums.SecondGoodsStatusEnum;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.store.SecondAiTask;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.SecondAiTaskMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondGoodsAuditMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.second.service.*;
+import shop.alien.second.util.AiTaskUtils;
+import shop.alien.util.common.Constants;
+import shop.alien.util.common.safe.ImageModerationResultVO;
+import shop.alien.util.common.safe.ImageModerationUtil;
+import shop.alien.util.common.safe.ImageReviewServiceEnum;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+import shop.alien.util.common.safe.TextReviewServiceEnum;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 二手商品审核服务实现类
+ * 负责商品的图片、文本、视频审核相关业务逻辑
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
+
+    private final SecondAiTaskMapper secondAiTaskMapper;
+    private final StoreImgMapper storeImgMapper;
+    /**
+     * 视频审核功能是否启用的配置项
+     */
+    @Value("${video.moderation.enabled}")
+    private boolean videoModerationEnabled;
+
+    /**
+     * 视频审核失败时是否阻塞商品发布的配置项
+     */
+    @Value("${video.moderation.block-on-failure}")
+    private boolean videoModerationBlockOnFailure;
+
+    /**
+     * 视频审核服务,用于处理商品中视频内容的审核
+     */
+    private final VideoModerationService videoModerationService;
+
+    /**
+     * 文本审核工具,用于审核商品标题、描述等文本内容
+     */
+    private final TextModerationUtil textModerationUtil;
+
+    /**
+     * 图片审核工具,用于审核商品图片内容
+     */
+    private final ImageModerationUtil imageModerationUtil;
+
+    /**
+     * 二手商品Mapper
+     */
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    /**
+     * 二手商品审核Mapper
+     */
+    private final SecondGoodsAuditMapper secondGoodsAuditMapper;
+
+    /**
+     * 商品操作历史记录Mapper
+     */
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    /**
+     * 消息通知服务
+     */
+    private final SecondGoodsNotificationService notificationService;
+
+    /**
+     * 操作历史记录服务
+     */
+    private final SecondGoodsOperationRecordService operationRecordService;
+
+    private final AiTaskUtils aiTaskUtil;
+
+    /**
+     * 执行内容审核
+     * @param goodsDTO 商品信息
+     * @param goods 商品实体
+     */
+    @Override
+    public void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception {
+        // 图片审核
+        boolean imageAuditResult = performImageReviews(goods, goodsDTO);
+        
+        // 审核失败,直接返回
+        if (!imageAuditResult) {
+            // 图片审核不通过,记录操作历史
+            operationRecordService.recordGoodsOperation(goods, "图片审核失败");
+            return;
+        }
+
+        // 文本审核
+        boolean textAuditResult = performTextReview(goods, goodsDTO);
+        
+        // 审核失败,直接返回
+        if (!textAuditResult) {
+            // 文本审核不通过,记录操作历史
+            operationRecordService.recordGoodsOperation(goods, "文本审核失败");
+            return;
+        }
+        
+        // 视频审核
+        List<String> taskIds = performVideoReviews(goods, goodsDTO);
+
+        // 如果成功提交了视频审核任务,设置商品状态为审核中
+        if (!taskIds.isEmpty()) {
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode()); // 审核中
+            goods.setVideoTaskId(taskIds.get(0)); // 保存第一个任务ID到商品表
+            goods.setFailedReason("");
+            secondGoodsMapper.updateById(goods);
+            // 审核中审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.UNDER_REVIEW);
+        } else {
+            // 第二轮AI审核
+            boolean b = performSecondRoundReview(goods, goodsDTO);
+            if (!b) {
+                // 第二轮审核失败,记录操作历史
+                operationRecordService.recordGoodsOperation(goods, "第二轮审核失败");
+                goods.setGoodsStatus(2);
+                goods.setFailedReason("调用AI接口失败,未获取到task_id");
+                secondGoodsMapper.updateById(goods);
+            }
+        }
+        
+        // 审核通过后上架商品
+//        approveAndListGoods(goods);
+    }
+
+    // 第二轮审核(AI)
+    private boolean performSecondRoundReview(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        try {
+            // 参数校验
+            if (goodsDTO == null || CollectionUtil.isEmpty(goodsDTO.getImgUrl())) {
+                log.warn("Second round review skipped: empty image URLs for goods id={}",
+                        goods != null ? goods.getId() : "unknown");
+                return false;
+            }
+
+            // 获取访问令牌
+            String accessToken = aiTaskUtil.getAccessToken();
+            if (StringUtils.isEmpty(accessToken)) {
+                log.error("Failed to obtain access token for second round review, goods id={}", goods.getId());
+                return false;
+            }
+
+            // 创建AI任务
+            String taskId = aiTaskUtil.createTask(accessToken, goods.getDescription(), goodsDTO.getImgUrl());
+            if (StringUtils.isEmpty(taskId)) {
+                log.warn("Failed to create AI task for second round review, goods id={}", goods.getId());
+                return false;
+            }
+            goods.setAiTaskId(taskId);
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode());
+            secondGoodsMapper.updateById(goods);
+
+            // 保存任务信息
+            SecondAiTask secondAiTask = new SecondAiTask();
+            secondAiTask.setTaskId(taskId);
+            secondAiTask.setStatus("PROCESSING");
+            Date currentTime = new Date();
+            secondAiTask.setCreateTime(currentTime);
+            secondAiTask.setUpdateTime(currentTime);
+            secondAiTaskMapper.insert(secondAiTask);
+
+            log.info("Successfully created second round AI review task, taskId={}, goodsId={}",
+                    taskId, goods.getId());
+            return true;
+        } catch (Exception e) {
+            log.error("Error during second round review for goods id={}",
+                    goods != null ? goods.getId() : "unknown", e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        // 图片审核(循环处理)
+        List<String> imageUrls = goodsDTO.getImgUrl();
+        // 根据imageUrls过滤不是图片的url
+        imageUrls = imageUrls.stream()
+                .filter(url -> url.toLowerCase().endsWith(".jpg") 
+                        || url.toLowerCase().endsWith(".png") 
+                        || url.toLowerCase().endsWith(".jpeg") 
+                        || url.toLowerCase().endsWith(".gif"))
+                .collect(Collectors.toList());
+        
+        // 图片审核
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            for (String imageUrl : imageUrls) {
+                List<String> imgServicesList = Lists.newArrayList();
+                // 内容治理检测 + AIGC图片风险检测
+                imgServicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+                imgServicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
+                
+                ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl, imgServicesList);
+                if ("high".equals(response.getRiskLevel())) {
+                    // 图片审核不通过或存在高风险
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+                    String failReason = "图片审核不通过:图片中包含" + 
+                            (response.getDescriptions() != null ? response.getDescriptions() : "高风险内容");
+                    goods.setFailedReason(failReason);
+                    // 插入审核记录
+                    createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+                    // 发送审核失败消息
+                    notificationService.sendFailedMsg(goods);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        List<String> servicesList = Lists.newArrayList();
+        servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+        servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+        
+        // 使用商品发布场景的审核服务
+        String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+        TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(test, servicesList);
+
+        if ("high".equals(textCheckResult.getRiskLevel())) {
+            // 文本审核不通过或存在高风险
+            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+            String failReason = "文本审核不通过:" + 
+                    (textCheckResult.getRiskWords() != null ? textCheckResult.getRiskWords() : "存在高风险内容");
+            goods.setFailedReason(failReason);
+            // 插入审核记录
+            createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+            // 发送审核失败消息
+            notificationService.sendFailedMsg(goods);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+        List<String> taskIds = new ArrayList<>();
+        
+        // 视频审核
+        if (videoModerationEnabled) {
+            if (!videoUrls.isEmpty()) {
+                // 提交视频审核任务
+                for (String videoUrl : videoUrls) {
+                    try {
+                        String taskId = videoModerationService.submitVideoModerationTask(videoUrl);
+                        taskIds.add(taskId);
+                    } catch (Exception e) {
+                        log.error("提交视频审核任务失败,视频URL: {}", videoUrl, e);
+                        if (videoModerationBlockOnFailure) {
+                            // 视频审核提交失败,设置为审核失败状态
+                            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                            goods.setFailedReason("视频审核提交失败: " + e.getMessage());
+                            createGoodsAudit(goods, "视频审核提交失败", Constants.AuditStatus.FAILED);
+                            notificationService.sendFailedMsg(goods);
+                        }
+                    }
+                }
+            }
+        }
+        return taskIds;
+    }
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    @Override
+    public void processVideoModerationResult(SecondVideoTask task) {
+        try {
+            // 查找关联的商品
+            QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("video_task_id", task.getTaskId());
+            SecondGoods goods = secondGoodsMapper.selectOne(queryWrapper);
+            
+            if (goods == null) {
+                log.warn("未找到关联的商品,任务ID: {}", task.getTaskId());
+                return;
+            }
+
+
+            // 根据审核结果更新商品状态
+            if ("none".equals(task.getRiskLevel())) {
+                QueryWrapper<StoreImg> imgQueryWrapper = new QueryWrapper<>();
+                imgQueryWrapper.eq("store_id", goods.getId());
+                imgQueryWrapper.eq("img_type", 19);
+                List<StoreImg> storeImgs = storeImgMapper.selectList(imgQueryWrapper);
+                List<String> imgUrls = storeImgs.stream()
+                        .map(StoreImg::getImgUrl)
+                        .filter(imgUrl -> StringUtils.hasText(imgUrl))
+                        .collect(Collectors.toList());
+
+                SecondGoodsVo goodsDTO = new SecondGoodsVo();
+                goodsDTO.setImgUrl(imgUrls);
+
+                // 开始第二轮审核
+                boolean b = performSecondRoundReview(goods, goodsDTO);
+
+                // 审核通过
+//                approveAndListGoods(goods);
+            } else {
+                // 审核不通过
+                goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                
+                // 解析审核结果,生成具体的失败原因
+                String failedReason = parseVideoModerationFailureReason(task);
+                goods.setFailedReason(failedReason);
+                secondGoodsMapper.updateById(goods);
+                
+                // 更新审核记录
+                createGoodsAudit(goods, failedReason, Constants.AuditStatus.FAILED);
+
+                // 记录操作历史
+                operationRecordService.recordGoodsOperation(goods, "视频审核失败");
+                // 发送审核失败消息
+                notificationService.sendFailedMsg(goods);
+            }
+        } catch (Exception e) {
+            log.error("处理视频审核结果时发生异常,任务ID: {}", task.getTaskId(), e);
+        }
+    }
+
+    /**
+     * 审核通过后上架商品
+     * @param goods 商品信息
+     */
+    @Override
+    public void approveAndListGoods(SecondGoods goods) {
+        boolean isNotified = false;
+        try {
+            // 如果所有审核都通过,设置为上架状态
+            goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode()); // 上架
+            goods.setFailedReason("");
+            goods.setReleaseTime(new Date()); // 上架时间
+            secondGoodsMapper.updateById(goods);
+            // 插入审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+            // 发送审核成功消息
+            notificationService.sendMessage(goods);
+            isNotified = true; // 标记通知已发送
+            
+            // 上架 记录商品操作历史
+            String operationName = "";
+            QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("goods_id", goods.getId());
+            List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryWrapper);
+            if (CollectionUtil.isNotEmpty(recordList)) {
+                operationName = "重新发布";
+            } else {
+                operationName = "首次发布";
+            }
+            operationRecordService.recordGoodsOperation(goods, operationName);
+        } catch (Exception e) {
+            log.error("商品上架过程中发生异常,执行回滚", e);
+            // 如果通知已发送但后续操作失败,需要补偿
+            if (isNotified) {
+                // 发送补偿消息,比如撤销通知或标记为异常状态
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     * @param failReason 审核失败原因
+     * @param goodsStatus 商品状态
+     */
+    @Override
+    public void createGoodsAudit(SecondGoods goods, String failReason, Integer goodsStatus) {
+        // 保存审核结果
+        secondGoodsMapper.updateById(goods);
+        // 插入审核记录
+        SecondGoodsAudit auditRecord = new SecondGoodsAudit();
+        auditRecord.setGoodsId(goods.getId());
+        auditRecord.setGoodsStatus(goodsStatus); // 审核状态
+        if (Constants.AuditStatus.FAILED.equals(goodsStatus)) {
+            auditRecord.setFailedReason(failReason);
+        }
+        auditRecord.setCreatedUserId(goods.getUserId());
+        auditRecord.setUpdatedUserId(goods.getUserId());
+        auditRecord.setCreatedTime(new Date());
+        auditRecord.setUpdatedTime(new Date());
+        secondGoodsAuditMapper.insert(auditRecord);
+        goods.setAuditRecordId(auditRecord.getId());
+    }
+
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    @Override
+    public List<String> extractVideoUrls(List<String> imageUrls) {
+        if (CollectionUtil.isEmpty(imageUrls)) {
+            return Collections.emptyList();
+        }
+        
+        List<String> videoUrls = new ArrayList<>();
+        for (String url : imageUrls) {
+            if (isVideoUrl(url)) {
+                videoUrls.add(url);
+            }
+        }
+        return videoUrls;
+    }
+
+    /**
+     * 判断URL是否为视频地址
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    @Override
+    public boolean isVideoUrl(String url) {
+        if (url == null) return false;
+        url = url.toLowerCase();
+        return url.endsWith(".mp4") ||
+                url.endsWith(".avi") ||
+                url.endsWith(".mov") ||
+                url.endsWith(".flv") ||
+                url.endsWith(".wmv") ||
+                url.endsWith(".mkv");
+    }
+
+    /**
+     * 解析视频审核失败原因
+     * @param task 视频审核任务
+     * @return 失败原因
+     */
+    private String parseVideoModerationFailureReason(SecondVideoTask task) {
+        StringBuilder reasonBuilder = new StringBuilder("视频审核不通过,风险等级: " + task.getRiskLevel());
+        
+        try {
+            // 解析审核结果JSON
+            JSONObject resultJson = JSON.parseObject(task.getResult());
+            if (resultJson != null && resultJson.containsKey("data")) {
+                JSONObject data = resultJson.getJSONObject("data");
+                
+                // 处理帧结果(视频画面)
+                if (data.containsKey("FrameResult")) {
+                    JSONObject frameResult = data.getJSONObject("FrameResult");
+                    if (frameResult != null && frameResult.containsKey("FrameSummarys")) {
+                        JSONArray frameSummarys = frameResult.getJSONArray("FrameSummarys");
+                        if (frameSummarys != null && !frameSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规内容:");
+                            for (int i = 0; i < frameSummarys.size(); i++) {
+                                JSONObject summary = frameSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+                
+                // 处理音频结果
+                if (data.containsKey("AudioResult")) {
+                    JSONObject audioResult = data.getJSONObject("AudioResult");
+                    if (audioResult != null && audioResult.containsKey("AudioSummarys")) {
+                        JSONArray audioSummarys = audioResult.getJSONArray("AudioSummarys");
+                        if (audioSummarys != null && !audioSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规音频:");
+                            for (int i = 0; i < audioSummarys.size(); i++) {
+                                JSONObject summary = audioSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.warn("解析视频审核结果失败,使用默认原因,任务ID: {}", task.getTaskId(), e);
+        }
+        
+        return reasonBuilder.toString();
+    }
+}
+

+ 146 - 0
alien-second/src/main/java/shop/alien/second/util/AiTaskUtils.java

@@ -0,0 +1,146 @@
+package shop.alien.second.util;
+
+import com.alibaba.fastjson2.JSONObject;
+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.StoreCommentAppeal;
+
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+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";
+
+    @Value("${third-party-user-name.base-url:UdUser}")
+    private String userName;
+
+    @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";
+
+
+
+    private String auditTaskResultUrl = "http://192.168.2.250:9000/ai/task-core/api/v1/audit_task/getResult";
+
+    /**
+     * 登录 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, String text, List<String> img_urls) {
+        log.info("创建Ai服务任务...{}", auditTaskUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+
+        Map<String, Object> analyzeRequest = new HashedMap<>();
+        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(auditTaskUrl, 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;
+    }
+}

+ 1 - 1
alien-second/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test

+ 11 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreCommentController.java

@@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.vo.LifeUserOrderCommentVo;
+import shop.alien.entity.store.vo.StoreCommentCountVo;
 import shop.alien.entity.store.vo.StoreCommentVo;
 import shop.alien.entity.store.vo.StoreCommitPercentVo;
 import shop.alien.store.service.StoreCommentService;
@@ -154,4 +155,14 @@ public class StoreCommentController {
         log.info("StoreCommentController.getCommentOrderPage?pageNum={}&pageSize={}&type={}&userId={}", pageNum, pageSize, type, userId);
         return R.data(storeCommentService.getCommentOrderPage(pageNum, pageSize, type, userId));
     }
+
+    @ApiOperation("获取店铺评价计数统计")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getAppraiseCount")
+    public R<StoreCommentCountVo> getAppraiseCount(@RequestParam("storeId") Integer storeId) {
+        log.info("StoreCommentController.getAppraiseCount?storeId={}", storeId);
+        return R.data(storeCommentService.getAppraiseCount(storeId));
+    }
 }

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

@@ -684,4 +684,39 @@ public class StoreInfoController {
         return R.data(list);
     }
 
+    @ApiOperation(value = "AI服务-店铺审核")
+    @ApiOperationSupport(order = 15)
+    @PostMapping("/aiApproveStoreInfo")
+    public R<Boolean> aiApproveStoreInfo(@RequestBody AiApproveStoreInfo aiApproveStoreInfo) {
+        log.info("StoreInfoController.aiApproveStoreInfo");
+        try {
+            storeInfoService.aiApproveStoreInfo(aiApproveStoreInfo);
+            return R.success("店铺审核完成");
+        } catch (Exception e) {
+            log.error("AI服务-店铺审核异常", e);
+            return R.fail("店铺审核失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "AI服务-门头识别")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeUserId", value = "门店用户id", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "imageUrl", value = "图片URL", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getStoreOcrData")
+    public R<Map<String, Object>> getStoreOcrData(@RequestParam("storeUserId") String storeUserId,
+                                                  @RequestParam("imageUrl") String imageUrl) {
+        log.info("StoreInfoController.getStoreOcrData?storeId={},imageUrl={}", storeUserId, imageUrl);
+        if (storeUserId == null || storeUserId.trim().isEmpty() || imageUrl == null || imageUrl.trim().isEmpty()) {
+            return R.fail("门店ID与图片URL不能为空");
+        }
+        Map<String, Object> ocrData = null;
+        try {
+            ocrData = storeInfoService.getStoreOcrData(storeUserId, imageUrl);
+        } catch (Exception e) {
+            return R.fail("未查询到OCR识别数据");
+        }
+        return R.data(ocrData);
+    }
 }

+ 85 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreVerificationCodeController.java

@@ -0,0 +1,85 @@
+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 io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreVerificationCode;
+import shop.alien.store.service.StoreVerificationCodeService;
+
+/**
+ * 短信验证码 CRUD
+ */
+@Api(tags = {"二期-验证码管理"})
+@Slf4j
+@ApiSort(30)
+@CrossOrigin
+@RestController
+@RequestMapping("/storeVerificationCode")
+@RequiredArgsConstructor
+public class StoreVerificationCodeController {
+
+    private final StoreVerificationCodeService storeVerificationCodeService;
+
+    @ApiOperation("分页查询验证码记录")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "appType", value = "端区分(0:用户,1:商家,2:律师端)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreVerificationCode>> page(@RequestParam(value = "phone", required = false) String phone,
+                                                @RequestParam(value = "appType", required = false) Integer appType,
+                                                @RequestParam(value = "businessType", required = false) Integer businessType,
+                                                @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
+                                                @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) {
+        log.info("StoreVerificationCodeController.page?phone={}&appType={}&businessType={}&pageNum={}&pageSize={}",
+                phone, appType, businessType, pageNum, pageSize);
+        return R.data(storeVerificationCodeService.pageList(phone, appType, businessType, pageNum, pageSize));
+    }
+
+    @ApiOperation("根据ID获取验证码记录")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParam(name = "id", value = "主键id", dataType = "Integer", paramType = "path", required = true)
+    @GetMapping("/{id}")
+    public R<StoreVerificationCode> detail(@PathVariable("id") Integer id) {
+        log.info("StoreVerificationCodeController.detail?id={}", id);
+        return R.data(storeVerificationCodeService.getById(id));
+    }
+
+    @ApiOperation("新增验证码记录")
+    @ApiOperationSupport(order = 3)
+    @PostMapping
+    public R<StoreVerificationCode> create(@RequestBody StoreVerificationCode request) {
+        log.info("StoreVerificationCodeController.create?request={}", request);
+        return R.data(storeVerificationCodeService.create(request));
+    }
+
+    @ApiOperation("更新验证码记录")
+    @ApiOperationSupport(order = 4)
+    @PutMapping
+    public R<StoreVerificationCode> update(@RequestBody StoreVerificationCode request) {
+        log.info("StoreVerificationCodeController.update?request={}", request);
+        return R.data(storeVerificationCodeService.updateRecord(request));
+    }
+
+    @ApiOperation("删除验证码记录")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParam(name = "id", value = "主键id", dataType = "Integer", paramType = "path", required = true)
+    @DeleteMapping("/{id}")
+    public R<Boolean> delete(@PathVariable("id") Integer id) {
+        log.info("StoreVerificationCodeController.delete?id={}", id);
+        return R.data(storeVerificationCodeService.removeRecord(id));
+    }
+}
+

+ 49 - 0
alien-store/src/main/java/shop/alien/store/controller/UserStoreController.java

@@ -1,17 +1,41 @@
 package shop.alien.store.controller;
 
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.*;
 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.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.StoreCommentAppeal;
+import shop.alien.entity.store.vo.StoreCommentAppealVo;
+import shop.alien.mapper.StoreCommentAppealMapper;
+import shop.alien.mapper.StoreCommentMapper;
 import shop.alien.store.service.LifeUserStoreService;
 import shop.alien.util.common.AlipayTradeAppPay;
 import shop.alien.util.common.ListToPage;
 import shop.alien.util.common.UniqueRandomNumGenerator;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -25,6 +49,30 @@ public class UserStoreController {
 
     private final LifeUserStoreService lifeUserStoreService;
 
+    private final RestTemplate restTemplate;
+
+    private final StoreCommentAppealMapper storeCommentAppealMapper;
+
+    private final StoreCommentMapper storeCommentMapper;
+
+//    @Value("${third-party-login.base-url}")
+    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
+
+    private String resultUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/records/{record_id}/completed";
+
+//    @Value("${third-party-user-name.base-url}")
+    private String userName = "UdUser";
+
+//    @Value("${third-party-pass-word.base-url}")
+    private String passWord = "123456";
+
+    /**
+     * 差评申述置信度分析接口地址
+     * 例如: http://192.168.2.250:9004/api/v1/analyze 或通过网关: http://192.168.2.250:9000/ai/auto_review/api/v1/analyze
+     */
+//    @Value("${bad-review-analyze.base-url}")
+    private String analyzeUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/analyze";
+
     @ApiOperation("查询商铺列表")
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "分页页数", dataType = "String", paramType = "query", required = true),
@@ -118,4 +166,5 @@ public class UserStoreController {
         log.info("LifeUserStoreController.buyQuan?quanId={},couponId={},userId={},cnt={},payPrice={},orderNo={}", quanId, couponId, userId, cnt, payPrice, orderNo);
         return R.data(lifeUserStoreService.buyQuan(quanId, couponId, userId, cnt, payPrice, orderNo));
     }
+
 }

+ 17 - 0
alien-store/src/main/java/shop/alien/store/service/RefundRecordService.java

@@ -0,0 +1,17 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.RefundRecord;
+
+/**
+ * <p>
+ * 退款记录表 服务类
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface RefundRecordService extends IService<RefundRecord> {
+
+}
+

+ 9 - 0
alien-store/src/main/java/shop/alien/store/service/StoreCommentService.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.store.StoreComment;
 import shop.alien.entity.store.vo.LifeUserOrderCommentVo;
+import shop.alien.entity.store.vo.StoreCommentCountVo;
 import shop.alien.entity.store.vo.StoreCommentVo;
 import shop.alien.entity.store.vo.StoreCommitPercentVo;
 
@@ -104,4 +105,12 @@ public interface StoreCommentService extends IService<StoreComment> {
      */
     IPage<LifeUserOrderCommentVo> getCommentOrderPage(Integer pageNum, Integer pageSize, Integer type, String userId);
 
+    /**
+     * 获取店铺评价计数统计
+     *
+     * @param storeId 门店id
+     * @return StoreCommentCountVo
+     */
+    StoreCommentCountVo getAppraiseCount(Integer storeId);
+
 }

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

@@ -279,4 +279,15 @@ public interface StoreInfoService extends IService<StoreInfo> {
      *
      */
     int foodLicenceType(int id);
+
+    /**
+     * 根据门店及图片地址查询最新OCR识别数据
+     *
+     * @param storeUserId  门店ID
+     * @param imageUrl 图片URL
+     * @return OCR识别记录
+     */
+    Map<String, Object> getStoreOcrData(String storeUserId, String imageUrl);
+
+    void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
 }

+ 36 - 0
alien-store/src/main/java/shop/alien/store/service/StoreVerificationCodeService.java

@@ -0,0 +1,36 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreVerificationCode;
+
+/**
+ * 短信验证码 service
+ */
+public interface StoreVerificationCodeService extends IService<StoreVerificationCode> {
+
+    /**
+     * 分页查询
+     */
+    IPage<StoreVerificationCode> pageList(String phone,
+                                          Integer appType,
+                                          Integer businessType,
+                                          Integer pageNum,
+                                          Integer pageSize);
+
+    /**
+     * 新增记录
+     */
+    StoreVerificationCode create(StoreVerificationCode request);
+
+    /**
+     * 更新记录
+     */
+    StoreVerificationCode updateRecord(StoreVerificationCode request);
+
+    /**
+     * 删除
+     */
+    boolean removeRecord(Integer id);
+}
+

+ 4 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -537,6 +537,10 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
                 request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus());
 
         LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
+
+        if (null != request.getOrderStr()){
+            order.setOrderStr(request.getOrderStr());
+        }
         order.setOrderNumber(request.getOrderNumber());
         order.setPaymentStatus(request.getPaymentStatus());
         order.setOrderStatus(request.getOrderStatus());

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

@@ -7,6 +7,7 @@ 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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -17,6 +18,7 @@ import org.springframework.util.CollectionUtils;
 import shop.alien.entity.second.SecondGoodsRecord;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.LifeUserViolationDto;
+import shop.alien.entity.store.excelVo.LifeUserOrderExcelVo;
 import shop.alien.entity.store.excelVo.LifeUserViolationExcelVO;
 import shop.alien.entity.store.excelVo.util.ExcelGenerator;
 import shop.alien.entity.store.vo.LifeUserViolationVo;
@@ -25,19 +27,24 @@ import shop.alien.mapper.*;
 import shop.alien.mapper.second.SecondGoodsRecordMapper;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.*;
+import shop.alien.store.util.AiUserViolationUtils;
 import shop.alien.store.util.FunctionMagic;
 import shop.alien.util.ali.AliOSSUtil;
 import shop.alien.util.common.EnumUtil;
+import shop.alien.util.common.JwtUtil;
 
 import java.io.File;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
+import static shop.alien.util.common.constant.Constant.*;
+
 
 /**
  * <p>
@@ -81,6 +88,8 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
     private final SecondGoodsRecordMapper secondGoodsRecordMapper;
     private final StoreImgService storeImgService;
 
+    private final AiUserViolationUtils aiUserViolationUtils;
+
     @Value("${spring.web.resources.excel-path}")
     private String excelPath;
 
@@ -101,34 +110,47 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             }
             int result = lifeUserViolationMapper.insert(lifeuserViolation);
             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 phoneId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
-                // 举报人消息
-                LifeNotice lifeNotice = getLifeNotice(lifeuserViolation);
-                lifeNoticeMapper.insert(lifeNotice);
-                WebSocketVo websocketVo = new WebSocketVo();
-                websocketVo.setSenderId("system");
-                websocketVo.setReceiverId(lifeNotice.getReceiverId());
-                websocketVo.setCategory("notice");
-                websocketVo.setNoticeType("1");
-                websocketVo.setIsRead(0);
-                websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
-                webSocketProcess.sendMessage(lifeNotice.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
-
-                // 被举报人消息
-                if (StringUtils.isNotEmpty(lifeuserViolation.getReportContextType()) && "1,2,3".contains(lifeuserViolation.getReportContextType())) {
-                    LifeNotice lifeNoticeReported = getLifeReportedNotice(lifeuserViolation);
-                    if (lifeNoticeReported != null) {
-                        lifeNoticeMapper.insert(lifeNoticeReported);
-                        WebSocketVo websocketVoReported = new WebSocketVo();
-                        websocketVoReported.setSenderId("system");
-                        websocketVoReported.setReceiverId(lifeNoticeReported.getReceiverId());
-                        websocketVoReported.setCategory("notice");
-                        websocketVoReported.setNoticeType("1");
-                        websocketVoReported.setIsRead(0);
-                        websocketVoReported.setText(com.alibaba.fastjson2.JSONObject.from(lifeNoticeReported).toJSONString());
-                        webSocketProcess.sendMessage(lifeNoticeReported.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVoReported).toJSONString());
+                    // 举报人消息
+                    LifeNotice lifeNotice = getLifeNotice(lifeuserViolation);
+                    lifeNoticeMapper.insert(lifeNotice);
+                    WebSocketVo websocketVo = new WebSocketVo();
+                    websocketVo.setSenderId("system");
+                    websocketVo.setReceiverId(lifeNotice.getReceiverId());
+                    websocketVo.setCategory("notice");
+                    websocketVo.setNoticeType("1");
+                    websocketVo.setIsRead(0);
+                    websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+                    webSocketProcess.sendMessage(lifeNotice.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
+
+                    // 被举报人消息
+                    if(StringUtils.isNotEmpty(lifeuserViolation.getReportContextType()) && "1,2,3".contains(lifeuserViolation.getReportContextType())){
+                        LifeNotice lifeNoticeReported = getLifeReportedNotice(lifeuserViolation);
+                        if (lifeNoticeReported != null) {
+                            lifeNoticeMapper.insert(lifeNoticeReported);
+                            WebSocketVo websocketVoReported = new WebSocketVo();
+                            websocketVoReported.setSenderId("system");
+                            websocketVoReported.setReceiverId(lifeNoticeReported.getReceiverId());
+                            websocketVoReported.setCategory("notice");
+                            websocketVoReported.setNoticeType("1");
+                            websocketVoReported.setIsRead(0);
+                            websocketVoReported.setText(com.alibaba.fastjson2.JSONObject.from(lifeNoticeReported).toJSONString());
+                            webSocketProcess.sendMessage(lifeNoticeReported.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVoReported).toJSONString());
+                        }
                     }
-                }
                 return result;
             }
         } catch (Exception e) {
@@ -153,16 +175,16 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
         if ("1".equals(lifeuserViolation.getReportingUserType())) {
             StoreUser storeUsers = storeUserMapper.selectById(reportUserId);
-            phoneId = "store_" + storeUsers.getPhone();
+            phoneId = "store_"+storeUsers.getPhone();
         } else {
             LifeUser lifeUsers = lifeUserMapper.selectById(reportUserId);
-            phoneId = "user_" + lifeUsers.getUserPhone();
+            phoneId = "user_"+lifeUsers.getUserPhone();
         }
 
         lifeNotice.setReceiverId(phoneId);
 
-        if (StringUtils.isNotEmpty(reportContextType) && "1,2,3".contains(reportContextType)) {
-            String violationType = StringUtils.isNotEmpty(lifeuserViolation.getViolationType()) ? lifeuserViolation.getViolationType() : "13";
+        if(StringUtils.isNotEmpty(reportContextType) && "1,2,3".contains(reportContextType)){
+            String violationType = StringUtils.isNotEmpty(lifeuserViolation.getViolationType())?lifeuserViolation.getViolationType():"13";
             String violationText = EnumUtil.getStatusValue(Integer.parseInt(violationType));
 
             String storeOrUserName = "";
@@ -182,25 +204,25 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
             switch (reportContextType) {
                 case "1":
-                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”,涉嫌" + violationText + ",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
+                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”,涉嫌"+violationText+",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
                     break;
                 case "2":
                     String dynamicsId = lifeuserViolation.getDynamicsId();
                     String dynamicsDate = simpleDateFormat.format(new Date());
-                    if (StringUtils.isNotEmpty(dynamicsId)) {
+                    if(StringUtils.isNotEmpty(dynamicsId)){
                         LifeUserDynamics lifeUserDynamics = lifeUserDynamicsMapper.selectById(dynamicsId);
                         dynamicsDate = simpleDateFormats.format(lifeUserDynamics.getCreatedTime());
                     }
-                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在" + dynamicsDate + "发布的动态,涉嫌" + violationText + ",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
+                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在"+dynamicsDate+"发布的动态,涉嫌"+violationText+",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
                     break;
                 case "3":
                     String commonId = lifeuserViolation.getCommentId();
                     String commonDate = simpleDateFormat.format(new Date());
-                    if (StringUtils.isNotEmpty(commonId)) {
-                        StoreComment storeComment = storeCommentMapper.selectById(commonId);
+                    if(StringUtils.isNotEmpty(commonId)){
+                        StoreComment storeComment  = storeCommentMapper.selectById(commonId);
                         commonDate = simpleDateFormats.format(storeComment.getCreatedTime());
                     }
-                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在" + commonDate + "发布的评论,涉嫌" + violationText + ",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
+                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在"+commonDate+"发布的评论,涉嫌"+violationText+",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
                     break;
             }
         }
@@ -229,7 +251,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             lifeNotice.setReceiverId("user_" + phoneId);
 
         }
-        if (StringUtils.isEmpty(phoneId)) {
+        if (StringUtils.isEmpty(phoneId)){
             return null;
         }
         String violationText = EnumUtil.getStatusValue(Integer.parseInt(lifeuserViolation.getViolationType()));
@@ -243,8 +265,8 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         String storeDate = simpleDateFormat.format(new Date());
         String reportContextType = lifeuserViolation.getReportContextType();
 
-        if (StringUtils.isNotEmpty(reportContextType) && reportContextType.equals("1")) {
-            message = "您在" + storeDate + "被举报涉嫌" + violationText + ",平台将会进行核实。如确实存在违规行为,平台将禁用您的账号**天,到期后账号可恢复使用,应用内的环境需要我们共同维护。";
+        if(StringUtils.isNotEmpty(reportContextType) && reportContextType.equals("1")){
+            message = "您在" + storeDate + "被举报涉嫌"+violationText+",平台将会进行核实。如确实存在违规行为,平台将禁用您的账号**天,到期后账号可恢复使用,应用内的环境需要我们共同维护。";
         } else {
             return null;
         }
@@ -310,9 +332,8 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         QueryWrapper<LifeUserViolationVo> queryWrapper = new QueryWrapper<>();
 
         // 基础查询条件
-        queryWrapper.eq("luv.delete_flag", 0);
-//                //排除二手的举报
-//                .notIn("luv.report_context_type", Arrays.asList("4", "5"));
+        queryWrapper.eq("luv.delete_flag", 0)
+                .in("luv.report_context_type", Arrays.asList("1", "2", "3"));
 
         // 动态查询条件
         queryWrapper.like(StringUtils.isNotEmpty(nickName), "ui.nick_name", nickName)
@@ -350,7 +371,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
     @Override
     public void approve(int id, String processingStatus, String reportResult) {
-        if (id == 0 || StringUtils.isBlank(processingStatus)) {
+        if(id==0 || StringUtils.isBlank(processingStatus)){
             return;
         }
         LifeUserViolation v = lifeUserViolationMapper.selectById(id);
@@ -364,15 +385,15 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         String violationTime = simpleDateFormat.format(v.getCreatedTime());
         // 被举报人信息
         String reportedUserName = "";
-        if (StringUtils.isNotEmpty(v.getReportedUserId())) {
+        if(StringUtils.isNotEmpty(v.getReportedUserId())){
             if (v.getReportedUserType().equals("1")) {
                 StoreUser storeUser = storeUserMapper.selectById(v.getReportedUserId());
-                if (storeUser != null) {
+                if(storeUser != null){
                     reportedUserName = storeUser.getNickName();
                 }
-            } else {
+            } else{
                 LifeUser lifeUser = lifeUserMapper.selectById(v.getReportedUserId());
-                if (lifeUser != null) {
+                if(lifeUser != null) {
                     reportedUserName = lifeUser.getUserName();
                 }
             }
@@ -380,15 +401,15 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         // 被举报动态信息
         String dynamicsId = v.getDynamicsId();
         String dynamicsDate = simpleDateFormats.format(new Date());
-        if (StringUtils.isNotEmpty(dynamicsId)) {
+        if(StringUtils.isNotEmpty(dynamicsId)){
             LifeUserDynamics lifeUserDynamics = lifeUserDynamicsMapper.selectById(dynamicsId);
             dynamicsDate = simpleDateFormats.format(lifeUserDynamics.getCreatedTime());
         }
         // 被举报评论信息
         String commonId = v.getCommentId();
         String commonDate = simpleDateFormats.format(new Date());
-        if (StringUtils.isNotEmpty(commonId)) {
-            StoreComment storeComment = storeCommentMapper.selectById(commonId);
+        if(StringUtils.isNotEmpty(commonId)){
+            StoreComment storeComment  = storeCommentMapper.selectById(commonId);
             commonDate = simpleDateFormats.format(storeComment.getCreatedTime());
         }
 
@@ -401,13 +422,13 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             // 通过
             if (v.getReportContextType().equals("1")) {
                 // 用户
-                message = "您在" + violationTime + "举报用户“" + reportedUserName + "”,涉嫌违法违规,经核实,确实存在违规行为,平台已将用户禁用,感谢您为此做出的贡献。";
+                message = "您在"+violationTime+"举报用户“"+reportedUserName+"”,涉嫌违法违规,经核实,确实存在违规行为,平台已将用户禁用,感谢您为此做出的贡献。";
                 title = "用户举报成功通知";
             }
             if (v.getReportContextType().equals("2")) {
                 // 动态
-                message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + dynamicsDate + "发布的动态,涉嫌违法违规,经核实,确实存在违规行为,平台已将此动态下架,感谢您为此做出的贡献";
-                reportedMessage = "您在" + dynamicsDate + "发布的动态,经核实,确实存在违规行为,平台已将此动态下架,应用内的环境需要我们共同维护";
+                message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+dynamicsDate+"发布的动态,涉嫌违法违规,经核实,确实存在违规行为,平台已将此动态下架,感谢您为此做出的贡献";
+                reportedMessage = "您在"+dynamicsDate+"发布的动态,经核实,确实存在违规行为,平台已将此动态下架,应用内的环境需要我们共同维护";
                 lifeUserDynamicsService.removeById(v.getDynamicsId());
                 title = "动态举报成功通知";
             }
@@ -415,29 +436,29 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 // 评论
                 if (v.getReportedUserType().equals("1")) {
                     // 商户
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + commonDate + "发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+commonDate+"发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
                     storeCommentService.removeById(v.getCommentId());
                 } else {
                     // 用户
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + commonDate + "发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+commonDate+"发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
                     lifeCommentMapper.deleteById(v.getCommentId());
                 }
-                reportedMessage = "您在" + commonDate + "发布的评论,经核实,确实存在违规行为,平台已将此评论下架,应用内的环境需要我们共同维护。";
+                reportedMessage = "您在"+commonDate+"发布的评论,经核实,确实存在违规行为,平台已将此评论下架,应用内的环境需要我们共同维护。";
                 title = "评论举报成功通知";
             }
         } else {
             // 驳回
             switch (v.getReportContextType()) {
                 case "1":
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
                     title = "用户举报失败通知";
                     break;
                 case "2":
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + dynamicsDate + "发布的动态,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+dynamicsDate+"发布的动态,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
                     title = "动态举报失败通知";
                     break;
                 case "3":
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + commonDate + "发布的评论,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+commonDate+"发布的评论,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
                     title = "评论举报失败通知";
                     break;
             }
@@ -479,7 +500,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
 
         // 被举报通知
-        if (StringUtils.isNotEmpty(reportedMessage)) {
+        if(StringUtils.isNotEmpty(reportedMessage)) {
             LifeNotice reportedLifeMessage = new LifeNotice();
             com.alibaba.fastjson2.JSONObject jsonObjectReported = new com.alibaba.fastjson2.JSONObject();
             jsonObjectReported.put("message", reportedMessage);
@@ -522,12 +543,10 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         if (entity == null) return null;
         LifeUserViolationDto dto = new LifeUserViolationDto();
         BeanUtils.copyProperties(entity, dto);
-        LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreImg::getStoreId, entity.getId());
-        queryWrapper.in(StoreImg::getImgType, 19);
-        List<StoreImg> storeImgList = storeImgService.list(queryWrapper);
-        List<String> collect = storeImgList.stream().map(StoreImg::getImgUrl).collect(Collectors.toList());
-        dto.setImageList(collect);
+        if (Objects.nonNull(entity.getReportEvidenceImg())) {
+            List<String> list = Arrays.stream(entity.getReportEvidenceImg().split(",")).map(String::trim).collect(Collectors.toList());
+            dto.setImageList(list);
+        }
         // 处理举报人信息
         FunctionMagic.handleUserInfo(dto.getReportingUserType(), dto.getReportingUserId(), storeId -> storeUserService.getOne(FunctionMagic.idQueryWrapper(storeId)), lifeId -> lifeUserService.getOne(FunctionMagic.idQueryWrapper(lifeId)), user -> {
             if (user instanceof StoreUser) {
@@ -576,7 +595,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         queryWrapper.orderByDesc("luv.updated_time");
 
         List<LifeUserViolationVo> violationList = lifeUserViolationMapper.getViolationList(queryWrapper);
-
+        
         // 如果查询结果为空,返回空列表生成的Excel
         if (CollectionUtils.isEmpty(violationList)) {
             log.warn("导出Excel时查询结果为空,nickName={}, phone={}, processingStatus={}", nickName, phone, processingStatus);
@@ -584,7 +603,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
         // 日期格式化器(复用)
         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-
+        
         // 使用Stream API进行转换,提高性能和可读性
         AtomicInteger serialNumber = new AtomicInteger(1);
         List<LifeUserViolationExcelVO> excelDataList = violationList.stream()
@@ -594,10 +613,10 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         // 生成Excel文件
         String fileName = UUID.randomUUID().toString().replace("-", "");
         String filePath = ExcelGenerator.generateExcel(
-                excelPath + excelGeneratePath + fileName + ".xlsx",
-                excelDataList,
+                excelPath + excelGeneratePath + fileName + ".xlsx", 
+                excelDataList, 
                 LifeUserViolationExcelVO.class);
-
+        
         // 上传到OSS并返回URL
         return aliOSSUtil.uploadFile(new File(filePath), "excel/" + fileName + ".xlsx");
     }
@@ -605,18 +624,18 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
     /**
      * 将 LifeUserViolationVo 转换为 LifeUserViolationExcelVO
      *
-     * @param vo           源对象
+     * @param vo 源对象
      * @param serialNumber 序号
-     * @param formatter    日期格式化器
+     * @param formatter 日期格式化器
      * @return Excel VO对象
      */
     private LifeUserViolationExcelVO convertToExcelVO(LifeUserViolationVo vo, int serialNumber, DateTimeFormatter formatter) {
         LifeUserViolationExcelVO excelVO = new LifeUserViolationExcelVO();
         BeanUtils.copyProperties(vo, excelVO);
-
+        
         excelVO.setId(serialNumber);
         excelVO.setNickname(vo.getNickName());
-
+        
         // 处理举报凭证图片(安全分割,避免数组越界)
         if (StringUtils.isNotEmpty(vo.getReportEvidenceImg())) {
             String[] imageParts = vo.getReportEvidenceImg().split(",");
@@ -624,7 +643,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setReportEvidenceImg(imageParts[0].trim());
             }
         }
-
+        
         // 处理举报内容类型
         if (StringUtils.isNotEmpty(vo.getReportContextType())) {
             try {
@@ -634,7 +653,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setReportContextType(vo.getReportContextType());
             }
         }
-
+        
         // 处理举报理由
         if (StringUtils.isNotEmpty(vo.getViolationType())) {
             try {
@@ -644,7 +663,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setViolationType(vo.getViolationType());
             }
         }
-
+        
         // 处理处理状态
         if (StringUtils.isNotEmpty(vo.getProcessingStatus())) {
             try {
@@ -654,7 +673,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setProcessingStatus(vo.getProcessingStatus());
             }
         }
-
+        
         // 格式化创建时间
         if (Objects.nonNull(vo.getCreatedTime())) {
             try {
@@ -667,7 +686,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 log.warn("格式化创建时间失败,createdTime={}, error={}", vo.getCreatedTime(), e.getMessage());
             }
         }
-
+        
         // 格式化处理时间
         if (Objects.nonNull(vo.getProcessingTime())) {
             try {
@@ -680,7 +699,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 log.warn("格式化处理时间失败,processingTime={}, error={}", vo.getProcessingTime(), e.getMessage());
             }
         }
-
+        
         return excelVO;
     }
 

+ 103 - 90
alien-store/src/main/java/shop/alien/store/service/impl/PlatformBusinessSectionServiceImpl.java

@@ -52,9 +52,9 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
     public List<StoreDictionary> queryBusinessSectionTree() {
         // 查询所有经营种类数据
         LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");
         queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
-        queryWrapper.orderByAsc(StoreDictionary::getDictId);
+        queryWrapper.orderByAsc(StoreDictionary::getSortId);
         List<StoreDictionary> storeDictionaryList = storeDictionaryMapper.selectList(queryWrapper);
 
         // 构建三级树形结构
@@ -122,7 +122,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         }
 
         // 设置固定字段
-        storeDictionary.setTypeName("business_section");
+//        storeDictionary.setTypeName("business_section");
 //        storeDictionary.setTypeDetail("经营板块");
         storeDictionary.setDeleteFlag(0);
         storeDictionary.setCreatedTime(new Date());
@@ -137,16 +137,24 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         // 如果是二级或三级,验证父节点是否存在
         if (parentId != 0) {
             StoreDictionary parent = storeDictionaryMapper.selectById(parentId);
-            if (parent == null || !"business_section".equals(parent.getTypeName()) || parent.getDeleteFlag() == 1) {
+            if (parent == null || parent.getDeleteFlag() == 1) {
                 throw new IllegalArgumentException("父节点不存在或已删除");
             }
         }
 
-        // 生成 dict_id:查询同级(相同 parent_id)的最大 dict_id,然后 +1
-        String maxDictId = getMaxDictIdByParentId(parentId);
-        int nextDictId = (maxDictId == null ? 0 : Integer.parseInt(maxDictId)) + 1;
+        // 生成 dict_id:根据 typeName 查询最大 dict_id,然后 +1
+        String typeName = storeDictionary.getTypeName();
+        if (StringUtils.isBlank(typeName)) {
+            throw new IllegalArgumentException("typeName不能为空");
+        }
+        String maxDictId = getMaxDictIdByTypeName(typeName);
+        int nextDictId = (maxDictId == null ? 1 : Integer.parseInt(maxDictId)) + 1;
         storeDictionary.setDictId(String.valueOf(nextDictId));
 
+        // 生成 sort_id
+        Integer maxSortId = getMaxSortIdByParentId(parentId);
+        storeDictionary.setSortId(maxSortId == null ? 1 : maxSortId + 1);
+
         // 保存
         return this.save(storeDictionary);
     }
@@ -172,9 +180,6 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         if (existing == null) {
             throw new IllegalArgumentException("记录不存在");
         }
-        if (!"business_section".equals(existing.getTypeName())) {
-            throw new IllegalArgumentException("该记录不是经营种类类型");
-        }
         if (existing.getDeleteFlag() == 1) {
             throw new IllegalArgumentException("该记录已删除");
         }
@@ -202,8 +207,8 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         // 标准化parentId用于比较(null转为0)
         Integer normalizedParentId = (parentId == null) ? 0 : parentId;
         Integer normalizedExistingParentId = (existingParentId == null) ? 0 : existingParentId;
-        String oldDictId = existing.getDictId();
-        String newDictId = storeDictionary.getDictId();
+        Integer oldSortId = existing.getSortId();
+        Integer newSortId = storeDictionary.getSortId();
         boolean parentIdChanged = !normalizedParentId.equals(normalizedExistingParentId);
         
         // 判断是否修改了关键字段(parent_id)
@@ -221,41 +226,41 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         if (parentIdChanged) {
             if (parentId != 0) {
                 StoreDictionary parent = storeDictionaryMapper.selectById(parentId);
-                if (parent == null || !"business_section".equals(parent.getTypeName()) || parent.getDeleteFlag() == 1) {
+                if (parent == null || parent.getDeleteFlag() == 1) {
                     throw new IllegalArgumentException("父节点不存在或已删除");
                 }
             }
             
-            // 如果用户指定了新位置(newDictId不为空),使用用户指定的位置
+            // 如果用户指定了新位置(newSortId不为空),使用用户指定的位置
             // 否则,使用新节点下的最大+1
-            if (StringUtils.isBlank(newDictId)) {
-                String maxDictId = getMaxDictIdByParentId(parentId);
-                int nextDictId = (maxDictId == null ? 0 : Integer.parseInt(maxDictId)) + 1;
-                newDictId = String.valueOf(nextDictId);
-                storeDictionary.setDictId(newDictId);
+            if (newSortId == null) {
+                Integer maxSortId = getMaxSortIdByParentId(parentId);
+                int nextSortId = (maxSortId == null ? 1 : maxSortId) + 1;
+                newSortId = nextSortId;
+                storeDictionary.setSortId(newSortId);
             }
             
             // 原节点下,原位置之后的记录需要前移(dictId - 1)
-            adjustSortOrderForMoveOut(existing.getId(), normalizedExistingParentId, oldDictId);
+            adjustSortOrderForMoveOut(existing.getId(), normalizedExistingParentId, oldSortId);
             
             // 新节点下,如果指定了新位置,需要调整新节点下的排序
-            if (StringUtils.isNotBlank(newDictId)) {
+            if (newSortId != null) {
                 try {
-                    int newOrder = Integer.parseInt(newDictId);
-                    String maxDictId = getMaxDictIdByParentId(parentId);
-                    int maxOrder = (maxDictId == null ? 0 : Integer.parseInt(maxDictId));
+                    int newOrder = newSortId;
+                    Integer maxSortId = getMaxSortIdByParentId(parentId);
+                    int maxOrder = (maxSortId == null ? 0 : maxSortId);
                     // 如果新位置不是最大+1,需要调整新节点下的排序
                     if (newOrder <= maxOrder) {
-                        adjustSortOrderForMoveIn(existing.getId(), normalizedParentId, newDictId);
+                        adjustSortOrderForMoveIn(existing.getId(), normalizedParentId, newSortId);
                     }
                 } catch (NumberFormatException e) {
-                    log.warn("无法解析新dictId: {}", newDictId, e);
+                    log.warn("无法解析新sortId: {}", newSortId, e);
                 }
             }
         }
 
         // 设置固定字段
-        storeDictionary.setTypeName("business_section");
+//        storeDictionary.setTypeName("business_section");
         // 如果没有指定删除标记,保持原有值;如果指定了,使用新值
         Integer oldHidden = existing.getHidden();
         Integer newHidden = storeDictionary.getHidden() == null ? oldHidden : storeDictionary.getHidden();
@@ -268,11 +273,11 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         // 处理排序逻辑:如果 parentId 没有改变,但 dictId 改变了,需要调整同级其他记录的排序
         if (!parentIdChanged) {
             // 如果 dictId 为空,保持原有值
-            if (StringUtils.isBlank(newDictId)) {
-                storeDictionary.setDictId(oldDictId);
-            } else if (!newDictId.equals(oldDictId)) {
+            if (newSortId == null) {
+                storeDictionary.setSortId(oldSortId);
+            } else if (!newSortId.equals(oldSortId)) {
                 // 调整同级其他记录的排序
-                adjustSortOrder(existing.getId(), normalizedParentId, oldDictId, newDictId);
+                adjustSortOrder(existing.getId(), normalizedParentId, oldSortId, newSortId);
             }
         }
 
@@ -297,7 +302,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
      */
     private boolean hasUndeletedChildren(Integer parentId) {
         LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
         queryWrapper.eq(StoreDictionary::getParentId, parentId);
         queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
         queryWrapper.last("LIMIT 1");
@@ -320,7 +325,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         
         // 查询所有未删除的子节点
         LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");
         queryWrapper.eq(StoreDictionary::getParentId, parentId);
         queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
         
@@ -343,14 +348,32 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
     }
 
     /**
-     * 根据 parent_id 获取同级最大 dict_id
+     * 根据 typeName 获取最大 dict_id
      *
-     * @param parentId 父节点ID
+     * @param typeName 类型名称
      * @return 最大 dict_id,如果不存在则返回 null
      */
-    private String getMaxDictIdByParentId(Integer parentId) {
+    private String getMaxDictIdByTypeName(String typeName) {
         LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        queryWrapper.eq(StoreDictionary::getTypeName, typeName);
+        queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
+        
+        queryWrapper.orderByDesc(StoreDictionary::getDictId);
+        queryWrapper.last("LIMIT 1");
+        
+        StoreDictionary maxDict = storeDictionaryMapper.selectOne(queryWrapper);
+        return maxDict != null ? maxDict.getDictId() : null;
+    }
+
+    /**
+     * 根据parentId获取最大的sortId
+     *
+     * @param parentId 父节点ID
+     * @return 最大的sortId,如果不存在则返回null
+     */
+    private Integer getMaxSortIdByParentId(Integer parentId) {
+        LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
         queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
         
         if (parentId == null || parentId == 0) {
@@ -360,11 +383,11 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
             queryWrapper.eq(StoreDictionary::getParentId, parentId);
         }
         
-        queryWrapper.orderByDesc(StoreDictionary::getDictId);
+        queryWrapper.orderByDesc(StoreDictionary::getSortId);
         queryWrapper.last("LIMIT 1");
         
         StoreDictionary maxDict = storeDictionaryMapper.selectOne(queryWrapper);
-        return maxDict != null ? maxDict.getDictId() : null;
+        return maxDict != null && maxDict.getSortId() != null ? maxDict.getSortId() : null;
     }
 
     /**
@@ -374,13 +397,13 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
      *
      * @param currentId 当前记录ID
      * @param parentId 父节点ID
-     * @param oldDictId 原dictId
-     * @param newDictId 新dictId
+     * @param oldSortId 原sortId
+     * @param newSortId 新sortId
      */
-    private void adjustSortOrder(Integer currentId, Integer parentId, String oldDictId, String newDictId) {
+    private void adjustSortOrder(Integer currentId, Integer parentId, Integer oldSortId, Integer newSortId) {
         try {
-            int oldOrder = Integer.parseInt(oldDictId);
-            int newOrder = Integer.parseInt(newDictId);
+            int oldOrder = oldSortId;
+            int newOrder = newSortId;
             
             // 如果排序没有改变,直接返回
             if (oldOrder == newOrder) {
@@ -389,7 +412,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
             
             // 查询同级所有记录(排除当前记录)
             LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+            queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
             queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
             queryWrapper.ne(StoreDictionary::getId, currentId);
             
@@ -411,14 +434,14 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
                 // 将新dictId到原dictId之间的记录的dictId都+1
                 for (StoreDictionary sibling : siblings) {
                     try {
-                        int siblingOrder = Integer.parseInt(sibling.getDictId());
+                        int siblingOrder = sibling.getSortId();
                         if (siblingOrder >= newOrder && siblingOrder < oldOrder) {
-                            sibling.setDictId(String.valueOf(siblingOrder + 1));
+                            sibling.setSortId(siblingOrder + 1);
                             sibling.setUpdatedTime(new Date());
                             storeDictionaryMapper.updateById(sibling);
                         }
                     } catch (NumberFormatException e) {
-                        log.warn("无法解析dictId: {}", sibling.getDictId(), e);
+                        log.warn("无法解析sortId: {}", sibling.getSortId(), e);
                     }
                 }
             } 
@@ -427,19 +450,19 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
                 // 将原dictId到新dictId之间的记录的dictId都-1
                 for (StoreDictionary sibling : siblings) {
                     try {
-                        int siblingOrder = Integer.parseInt(sibling.getDictId());
+                        int siblingOrder = sibling.getSortId();
                         if (siblingOrder > oldOrder && siblingOrder <= newOrder) {
-                            sibling.setDictId(String.valueOf(siblingOrder - 1));
+                            sibling.setSortId(siblingOrder - 1);
                             sibling.setUpdatedTime(new Date());
                             storeDictionaryMapper.updateById(sibling);
                         }
                     } catch (NumberFormatException e) {
-                        log.warn("无法解析dictId: {}", sibling.getDictId(), e);
+                        log.warn("无法解析sortId: {}", sibling.getSortId(), e);
                     }
                 }
             }
         } catch (NumberFormatException e) {
-            log.error("调整排序失败,dictId格式错误: oldDictId={}, newDictId={}", oldDictId, newDictId, e);
+            log.error("调整排序失败,sortId格式错误: oldSortId={}, newSortId={}", oldSortId, newSortId, e);
         } catch (Exception e) {
             log.error("调整排序失败", e);
         }
@@ -450,15 +473,15 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
      *
      * @param currentId 当前记录ID
      * @param oldParentId 原父节点ID
-     * @param oldDictId 原dictId
+     * @param oldSortId 原sortId
      */
-    private void adjustSortOrderForMoveOut(Integer currentId, Integer oldParentId, String oldDictId) {
+    private void adjustSortOrderForMoveOut(Integer currentId, Integer oldParentId, Integer oldSortId) {
         try {
-            int oldOrder = Integer.parseInt(oldDictId);
+            int oldOrder = oldSortId;
             
             // 查询原节点同级所有记录(排除当前记录)
             LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+            queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
             queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
             queryWrapper.ne(StoreDictionary::getId, currentId);
             
@@ -478,18 +501,18 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
             // 原位置之后的记录需要前移(dictId - 1)
             for (StoreDictionary sibling : siblings) {
                 try {
-                    int siblingOrder = Integer.parseInt(sibling.getDictId());
+                    int siblingOrder = sibling.getSortId();
                     if (siblingOrder > oldOrder) {
-                        sibling.setDictId(String.valueOf(siblingOrder - 1));
+                        sibling.setSortId(siblingOrder - 1);
                         sibling.setUpdatedTime(new Date());
                         storeDictionaryMapper.updateById(sibling);
                     }
                 } catch (NumberFormatException e) {
-                    log.warn("无法解析dictId: {}", sibling.getDictId(), e);
+                    log.warn("无法解析sortId: {}", sibling.getSortId(), e);
                 }
             }
         } catch (NumberFormatException e) {
-            log.error("移出节点排序调整失败,dictId格式错误: oldDictId={}", oldDictId, e);
+            log.error("移出节点排序调整失败,sortId格式错误: oldSortId={}", oldSortId, e);
         } catch (Exception e) {
             log.error("移出节点排序调整失败", e);
         }
@@ -500,15 +523,15 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
      *
      * @param currentId 当前记录ID
      * @param newParentId 新父节点ID
-     * @param newDictId 新dictId
+     * @param newSortId 新sortId
      */
-    private void adjustSortOrderForMoveIn(Integer currentId, Integer newParentId, String newDictId) {
+    private void adjustSortOrderForMoveIn(Integer currentId, Integer newParentId, Integer newSortId) {
         try {
-            int newOrder = Integer.parseInt(newDictId);
+            int newOrder = newSortId;
             
             // 查询新节点同级所有记录(排除当前记录)
             LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+            queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
             queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
             queryWrapper.ne(StoreDictionary::getId, currentId);
             
@@ -528,9 +551,9 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
             // 新位置之后的记录需要后移(dictId + 1)
             for (StoreDictionary sibling : siblings) {
                 try {
-                    int siblingOrder = Integer.parseInt(sibling.getDictId());
+                    int siblingOrder = sibling.getSortId();
                     if (siblingOrder >= newOrder) {
-                        sibling.setDictId(String.valueOf(siblingOrder + 1));
+                        sibling.setSortId(siblingOrder + 1);
                         sibling.setUpdatedTime(new Date());
                         storeDictionaryMapper.updateById(sibling);
                     }
@@ -539,7 +562,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
                 }
             }
         } catch (NumberFormatException e) {
-            log.error("移入节点排序调整失败,dictId格式错误: newDictId={}", newDictId, e);
+            log.error("移入节点排序调整失败,sortId格式错误: newSortId={}", newSortId, e);
         } catch (Exception e) {
             log.error("移入节点排序调整失败", e);
         }
@@ -733,7 +756,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
                                      int level, String hidden, List<String> errorMessages, int rowIndex) {
         try {
             // 查找或创建一级分类
-            StoreDictionary firstLevel = findOrCreateLevel(firstLevelName, 0, 0, hidden);
+            StoreDictionary firstLevel = findOrCreateLevel(firstLevelName, null, 0, hidden);
             if (firstLevel == null) {
                 throw new IllegalArgumentException("创建一级分类失败");
             }
@@ -769,7 +792,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
     private StoreDictionary findOrCreateLevel(String dictDetail, Integer parentId, int expectedLevel, String hidden) {
         // 查找是否存在
         LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
         queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
         queryWrapper.eq(StoreDictionary::getDictDetail, dictDetail);
 
@@ -788,40 +811,30 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
 
         // 创建新分类
         StoreDictionary newDict = new StoreDictionary();
-        newDict.setTypeName("business_section");
         newDict.setDictDetail(dictDetail);
         // 一级为经营版块 ,二级为经营种类,三级为分类
         if (expectedLevel == 0) {
             newDict.setTypeDetail("经营板块");
+            newDict.setTypeName("business_section");
         } else if (expectedLevel == 1) {
             newDict.setTypeDetail("经营种类");
+            newDict.setTypeName("business_type");
         } else if (expectedLevel == 2) {
             newDict.setTypeDetail("分类");
+            newDict.setTypeName("business_classify");
         }
-        newDict.setParentId(parentId == null ? 0 : parentId);
+        newDict.setParentId(parentId == null ? null : parentId);
         newDict.setHidden(StringUtils.isNotBlank(hidden) && hidden.equals("隐藏") ? 1 : 0);
         newDict.setDeleteFlag(0);
         newDict.setCreatedTime(new Date());
 
         // 生成 dict_id
-        String maxDictId = getMaxDictIdByParentId(newDict.getParentId());
-        /*int nextDictId;
-        if (StringUtils.isNotBlank(sortOrder) && expectedLevel == 1) {
-            try {
-                nextDictId = Integer.parseInt(sortOrder);
-                // 如果指定的排序已存在,需要调整
-                String existingDictId = getDictIdByParentIdAndOrder(newDict.getParentId(), nextDictId);
-                if (existingDictId != null) {
-                    // 使用最大+1
-                    nextDictId = (maxDictId == null ? 0 : Integer.parseInt(maxDictId)) + 1;
-                }
-            } catch (NumberFormatException e) {
-                nextDictId = (maxDictId == null ? 0 : Integer.parseInt(maxDictId)) + 1;
-            }
-        } else {
-            nextDictId = (maxDictId == null ? 0 : Integer.parseInt(maxDictId)) + 1;
-        }*/
+        String maxDictId = getMaxDictIdByTypeName(newDict.getTypeName());
         newDict.setDictId(String.valueOf(maxDictId == null ? 1 : Integer.parseInt(maxDictId) + 1));
+        
+        // 生成 sort_id
+        Integer maxSortId = getMaxSortIdByParentId(newDict.getParentId());
+        newDict.setSortId(maxSortId == null ? 1 : maxSortId + 1);
 
         boolean saved = this.save(newDict);
         return saved ? newDict : null;
@@ -836,7 +849,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
      */
     private String getDictIdByParentIdAndOrder(Integer parentId, int order) {
         LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");;
         queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
         queryWrapper.eq(StoreDictionary::getDictId, String.valueOf(order));
         
@@ -848,7 +861,7 @@ public class PlatformBusinessSectionServiceImpl extends ServiceImpl<StoreDiction
         }
         
         StoreDictionary dict = storeDictionaryMapper.selectOne(queryWrapper);
-        return dict != null ? dict.getDictId() : null;
+        return dict != null ? dict.getSortId().toString() : null;
     }
 
     /**

+ 21 - 0
alien-store/src/main/java/shop/alien/store/service/impl/RefundRecordServiceImpl.java

@@ -0,0 +1,21 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.RefundRecord;
+import shop.alien.mapper.RefundRecordMapper;
+import shop.alien.store.service.RefundRecordService;
+
+/**
+ * <p>
+ * 退款记录表 服务实现类
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Service
+public class RefundRecordServiceImpl extends ServiceImpl<RefundRecordMapper, RefundRecord> implements RefundRecordService {
+
+}
+

+ 51 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreCommentServiceImpl.java

@@ -857,4 +857,55 @@ public class StoreCommentServiceImpl extends ServiceImpl<StoreCommentMapper, Sto
 
         return commentOrderPage;
     }
+
+    /**
+     * 获取店铺评价计数统计
+     *
+     * @param storeId 门店id
+     * @return StoreCommentCountVo
+     */
+    @Override
+    public StoreCommentCountVo getAppraiseCount(Integer storeId) {
+        StoreCommentCountVo countVo = new StoreCommentCountVo();
+
+        // 查询条件:只统计当前门店、根评论(reply_id为null)、未删除的评论
+        LambdaQueryWrapper<StoreComment> totalWrapper = buildBaseStoreCommentWrapper(storeId);
+        countVo.setTotalCount(storeCommentMapper.selectCount(totalWrapper));
+
+        // 2. 有图评论数(img_id不为空且不为空字符串)
+        LambdaQueryWrapper<StoreComment> imageWrapper = buildBaseStoreCommentWrapper(storeId);
+        imageWrapper.isNotNull(StoreComment::getImgId)
+                .apply("img_id != ''");
+        countVo.setImageCount(storeCommentMapper.selectCount(imageWrapper));
+
+        // 3. 好评数(score >= 4.5)
+        LambdaQueryWrapper<StoreComment> goodWrapper = buildBaseStoreCommentWrapper(storeId);
+        goodWrapper.ge(StoreComment::getScore, 4.5);
+        countVo.setGoodCount(storeCommentMapper.selectCount(goodWrapper));
+
+        // 4. 中评数(score >= 3 && score <= 4)
+        LambdaQueryWrapper<StoreComment> midWrapper = buildBaseStoreCommentWrapper(storeId);
+        midWrapper.ge(StoreComment::getScore, 3.0)
+                .le(StoreComment::getScore, 4.0);
+        countVo.setMidCount(storeCommentMapper.selectCount(midWrapper));
+
+        // 5. 差评数(score >= 0.5 && score <= 2.5)
+        LambdaQueryWrapper<StoreComment> badWrapper = buildBaseStoreCommentWrapper(storeId);
+        badWrapper.ge(StoreComment::getScore, 0.5)
+                .le(StoreComment::getScore, 2.5);
+        countVo.setBadCount(storeCommentMapper.selectCount(badWrapper));
+
+        return countVo;
+    }
+
+    /**
+     * 构建当前门店根评论的查询条件
+     */
+    private LambdaQueryWrapper<StoreComment> buildBaseStoreCommentWrapper(Integer storeId) {
+        return new LambdaQueryWrapper<StoreComment>()
+                .eq(StoreComment::getStoreId, storeId)
+                .gt(StoreComment::getScore,0 )
+                .eq(StoreComment::getBusinessType, 5)
+                .eq(StoreComment::getDeleteFlag, 0);
+    }
 }

+ 113 - 3
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -14,12 +14,12 @@ import lombok.RequiredArgsConstructor;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.geo.Point;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.*;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
+import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.store.*;
@@ -40,10 +40,12 @@ import shop.alien.store.service.*;
 import shop.alien.store.util.CommonConstant;
 import shop.alien.store.util.FileUploadUtil;
 import shop.alien.store.util.GroupConstant;
+import shop.alien.store.util.ai.AiAuthTokenUtil;
 import shop.alien.util.ali.AliOSSUtil;
 import shop.alien.util.common.DistanceUtil;
 import shop.alien.util.common.constant.CouponStatusEnum;
 import shop.alien.util.common.constant.CouponTypeEnum;
+import shop.alien.util.common.constant.OcrTypeEnum;
 import shop.alien.util.common.constant.OrderStatusEnum;
 
 import javax.annotation.Resource;
@@ -137,6 +139,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     private final LifeBlacklistMapper lifeBlacklistMapper;
 
+    private final OcrImageUploadMapper ocrImageUploadMapper;
+
+    private final AiAuthTokenUtil aiAuthTokenUtil;
+
+    private final RestTemplate restTemplate;
+
     /** 商户证照历史记录数据访问对象 */
     private final StoreLicenseHistoryMapper licenseHistoryMapper;
 
@@ -150,6 +158,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
     @Value("${spring.web.resources.excel-generate-path}")
     private String excelGeneratePath;
 
+    @Value("${third-party-identification.base-url}")
+    private String identificationPath;
+
+    @Value("${third-party-applications.base-url}")
+    private String applicationsPath;
+
     /**
      * 懒得查, 留着导出Excel
      */
@@ -2389,5 +2403,101 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         }
     }
 
+    @Override
+    public void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo) {
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+        aiHeaders.set("Authorization", "Bearer " + aiAuthTokenUtil.getAccessToken());
+
+        HttpEntity<AiApproveStoreInfo> request = new HttpEntity<>(aiApproveStoreInfo, aiHeaders);
+        ResponseEntity<String> response = null;
+        try {
+            response = restTemplate.postForEntity(applicationsPath, request, String.class);
+            if (response.getStatusCodeValue() != 200) {
+                throw new RuntimeException("AI门店审核接口调用失败 http状态:" + response.getStatusCode());
+            }
+            if (StringUtils.isNotEmpty(response.getBody())) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                if (jsonObject.getInteger("code") == 200) {
+                    JSONObject data = jsonObject.getJSONObject("data");
+                    List<StoreInfo> storeInfos = storeInfoMapper.selectList(new LambdaQueryWrapper<StoreInfo>()
+                            .eq(StoreInfo::getCreatedUserId, aiApproveStoreInfo.getUserId()).eq(StoreInfo::getStoreApplicationStatus, 0).eq(StoreInfo::getDeleteFlag, 0));
+                    for (StoreInfo storeInfo : storeInfos) {
+                        if ("approved".equals(data.getString("status"))) {
+                            storeInfo.setStoreApplicationStatus(1);
+                        } else if ("rejected".equals(data.getString("status"))) {
+                            storeInfo.setStoreApplicationStatus(2);
+                        } else {
+                            System.out.println("未知状态");
+                        }
+                        storeInfoMapper.updateById(storeInfo);
+                    }
+                } else {
+                    throw new RuntimeException("AI门店审核接口调用失败 code:" + jsonObject.getInteger("code"));
+                }
+            }
+        } catch (Exception e){
+            throw new RuntimeException("调用门店审核接口异常", e);
+        }
+    }
+
+    @Override
+    public Map<String, Object> getStoreOcrData(String storeUserId, String imageUrl) {
+        Map<String, Object> map = new HashMap<>();
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(OcrImageUpload::getStoreUserId, storeUserId)
+                .eq(OcrImageUpload::getOcrType, OcrTypeEnum.BUSINESS_LICENSE.getCode());
+        OcrImageUpload ocrImageUploads = ocrImageUploadMapper.selectOne(queryWrapper);
+        if(ocrImageUploads== null){
+            throw new RuntimeException("未找到OCI识别数据!");
+        }
+        String accessToken = aiAuthTokenUtil.getAccessToken();
+
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+        aiHeaders.set("Authorization", "Bearer " + accessToken);
+        Map<String, Object> jsonBody = new HashMap<>();
+        List<String> imageUrls = new ArrayList<>();
+        imageUrls.add(imageUrl);
+        jsonBody.put("image_urls", imageUrls);
+        String ocrResult = ocrImageUploads.getOcrResult();
+        JSONObject jsonObject = JSONObject.parseObject(ocrResult);
+        jsonBody.put("merchant_name", jsonObject.get("companyName"));
+
+        HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
+        ResponseEntity<String> response = null;
+        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+        factory.setConnectTimeout(5000);
+        factory.setReadTimeout(60000);
+        restTemplate.setRequestFactory(factory);
+        try {
+            response = restTemplate.postForEntity(identificationPath, request, String.class);
+            if (response.getStatusCodeValue() != 200) {
+                throw new RuntimeException("AI内容审核接口调用失败 http状态:" + response.getStatusCode());
+            }
 
+        } catch (Exception e) {
+            throw new RuntimeException("AI接口调用失败");
+        }
+        com.alibaba.fastjson2.JSONObject responseNode = com.alibaba.fastjson2.JSONObject.parseObject(response.getBody());
+        if (responseNode == null) {
+            throw new RuntimeException("AI接口调用失败,响应内容为空");
+        } else {
+            Integer code = responseNode.getInteger("code");
+            if (code == 200) {
+                Map<String, Object> dataMap = (Map<String, Object>) responseNode.get("data");
+                if (Objects.nonNull(dataMap)) {
+                    // 获取match_results(JSON中是数组,反序列化为List<Map>)
+                    List<Map<String, Object>> matchResults = (List<Map<String, Object>>) dataMap.get("match_results");
+                    map.put("overall_match", dataMap.get("overall_match"));
+                    if (Objects.nonNull(matchResults) && !matchResults.isEmpty()) {
+                        map.put("match_reason", matchResults.get(0).get("match_reason"));
+                    }
+                }
+            } else {
+                throw new RuntimeException("AI接口调用失败,错误码: " + code);
+            }
+        }
+        return map;
+    }
 }

+ 71 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreVerificationCodeServiceImpl.java

@@ -0,0 +1,71 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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 org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.StoreVerificationCode;
+import shop.alien.mapper.StoreVerificationCodeMapper;
+import shop.alien.store.service.StoreVerificationCodeService;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * 短信验证码 service 实现
+ */
+@Service
+@RequiredArgsConstructor
+public class StoreVerificationCodeServiceImpl extends ServiceImpl<StoreVerificationCodeMapper, StoreVerificationCode>
+        implements StoreVerificationCodeService {
+
+    @Override
+    public IPage<StoreVerificationCode> pageList(String phone, Integer appType, Integer businessType, Integer pageNum, Integer pageSize) {
+        LambdaQueryWrapper<StoreVerificationCode> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StringUtils.isNotBlank(phone), StoreVerificationCode::getPhone, phone);
+        wrapper.eq(null != appType, StoreVerificationCode::getAppType, appType);
+        wrapper.eq(null != businessType, StoreVerificationCode::getBusinessType, businessType);
+        wrapper.eq(StoreVerificationCode::getDeleteFlag, 0);
+        wrapper.orderByDesc(StoreVerificationCode::getCreatedTime);
+        Page<StoreVerificationCode> page = new Page<>(null == pageNum ? 1 : pageNum, null == pageSize ? 10 : pageSize);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    public StoreVerificationCode create(StoreVerificationCode request) {
+        request.setId(null);
+        Date now = new Date();
+        request.setCreatedTime(now);
+        request.setUpdatedTime(now);
+        request.setDeleteFlag(0);
+        this.save(request);
+        return request;
+    }
+
+    @Override
+    public StoreVerificationCode updateRecord(StoreVerificationCode request) {
+        if (Objects.isNull(request.getId())) {
+            throw new IllegalArgumentException("id不能为空");
+        }
+        request.setUpdatedTime(new Date());
+        this.updateById(request);
+        return this.getById(request.getId());
+    }
+
+    @Override
+    public boolean removeRecord(Integer id) {
+        if (Objects.isNull(id)) {
+            return false;
+        }
+        StoreVerificationCode entity = new StoreVerificationCode();
+        entity.setId(id);
+        entity.setDeleteFlag(1);
+        entity.setUpdatedTime(new Date());
+        return this.updateById(entity);
+    }
+
+}
+

+ 261 - 7
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/AlipayPaymentStrategyImpl.java

@@ -10,8 +10,10 @@ import com.alipay.api.DefaultAlipayClient;
 import com.alipay.api.domain.AlipayTradeAppPayModel;
 import com.alipay.api.domain.AlipayTradeRefundModel;
 import com.alipay.api.request.AlipayTradeAppPayRequest;
+import com.alipay.api.request.AlipayTradeQueryRequest;
 import com.alipay.api.request.AlipayTradeRefundRequest;
 import com.alipay.api.response.AlipayTradeAppPayResponse;
+import com.alipay.api.response.AlipayTradeQueryResponse;
 import com.alipay.api.response.AlipayTradeRefundResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -19,7 +21,9 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.RefundRecord;
 import shop.alien.entity.store.StoreAliPayRefundLog;
+import shop.alien.store.service.RefundRecordService;
 import shop.alien.store.service.StoreAliPayRefundLogService;
 import shop.alien.store.strategy.payment.PaymentStrategy;
 import shop.alien.util.common.UniqueRandomNumGenerator;
@@ -111,6 +115,8 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
 
     private final StoreAliPayRefundLogService storeAliPayRefundLogService;
 
+    private final RefundRecordService refundRecordService;
+
 
     /**
      * 填写阿里配置
@@ -189,7 +195,51 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
 
     @Override
     public R<Object> searchOrderByOutTradeNoPath(String transactionId) throws Exception {
-        return null;
+        try {
+            AlipayClient alipayClient = new DefaultAlipayClient(setAlipayConfig(null));
+            // 2. 构造查询请求
+            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+
+            // 设置biz_content(JSON格式)
+            JSONObject bizContent = new JSONObject();
+            if (StringUtils.isNotBlank(transactionId)) {
+                bizContent.put("out_trade_no", transactionId);
+            }
+//            if (StringUtils.isNotBlank(tradeNo)) {
+//                bizContent.put("trade_no", tradeNo);
+//            }
+            request.setBizContent(bizContent.toJSONString());
+
+            // 3. 调用接口并获取响应
+            AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
+            if (response.isSuccess()) {
+                // 4. 解析支付状态
+                String tradeStatus = response.getTradeStatus();
+                log.info("订单查询成功,支付状态:" + tradeStatus);
+
+                // 支付状态说明:
+                // WAIT_BUYER_PAY:等待买家付款
+                // TRADE_CLOSED:交易关闭(超时未支付/已取消)
+                // TRADE_SUCCESS:支付成功(即时到账/普通转账)
+                // TRADE_FINISHED:交易完成(不可退款的场景,如即时到账)
+                if ("TRADE_SUCCESS".equals(tradeStatus)) {
+                    return R.success("支付成功");
+                } else if ("TRADE_CLOSED".equals(tradeStatus)) {
+                    return R.fail("交易已关闭");
+                } else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
+                    return R.fail("等待买家付款");
+                } else if ("TRADE_FINISHED".equals(tradeStatus)) {
+                    return R.success("交易完成");
+                }
+                return R.success("订单状态:" + tradeStatus);
+            } else {
+                return R.fail("订单查询失败:" + response.getMsg() + "(" + response.getSubMsg() + ")");
+            }
+        } catch (AlipayApiException e) {
+            log.error("支付宝订单查询异常", e);
+            return R.fail("查询异常:" + e.getMessage());
+        }
+
     }
 
     @Override
@@ -199,12 +249,12 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
             AlipayTradeRefundRequest request = buildRefundRequest(params);
             AlipayTradeRefundResponse response = refundRun(request);
             String refundReslut = "";
+            JSONObject responseBody = JSONObject.parseObject(response.getBody());
+            JSONObject refundResponse = responseBody.getJSONObject("alipay_trade_refund_response");
+            
             if (response.isSuccess()) {
                 refundReslut = "调用成功";
-                // 保存退款信息进入到退款记录表
-                JSONObject responseBody = JSONObject.parseObject(response.getBody());
-                JSONObject refundResponse = responseBody.getJSONObject("alipay_trade_refund_response");
-
+                // 保存退款信息进入到支付宝退款记录表(保留原有逻辑)
                 StoreAliPayRefundLog refundLog = new StoreAliPayRefundLog();
                 // 响应基本信息
                 refundLog.setResponseCode(refundResponse.getString("code"));
@@ -247,10 +297,48 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
                 refundLog.setDeleteFlag(0);
                 refundLog.setCreatedTime(new Date());
                 storeAliPayRefundLogService.save(refundLog);
+                
+                // 保存到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromAlipayResponse(refundResponse, responseBody, params);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("支付宝退款记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("支付宝退款记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存支付宝退款记录到RefundRecord表失败", e);
+                    // 不抛出异常,避免影响原有逻辑
+                }
             } else {
                 log.warn("AliPayConfig.processRefund ERROR Msg={}", response.getBody());
-                JSONObject jsonObject = JSONObject.parseObject(response.getBody()).getJSONObject("alipay_trade_refund_response");
-                refundReslut =  jsonObject.getString("sub_msg");
+                refundReslut = refundResponse.getString("sub_msg");
+                
+                // 保存失败记录到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromAlipayError(refundResponse, responseBody, params);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("支付宝退款失败记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("支付宝退款失败记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存支付宝退款失败记录到RefundRecord表失败", e);
+                }
             }
             return refundReslut;
         } catch (AlipayApiException e) {
@@ -324,5 +412,171 @@ public class AlipayPaymentStrategyImpl implements PaymentStrategy {
         return response;
     }
 
+    /**
+     * 从支付宝退款响应构建RefundRecord对象(成功情况)
+     */
+    private RefundRecord buildRefundRecordFromAlipayResponse(JSONObject refundResponse, JSONObject responseBody, Map<String, String> params) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.ALIPAY.getType());
+            record.setOutTradeNo(refundResponse.getString("out_trade_no"));
+            record.setTransactionId(refundResponse.getString("trade_no"));
+            // 部分退款编号作为商户退款单号,如果没有则生成一个
+            String outRefundNo = params.get("partialRefundCode");
+            if (StringUtils.isBlank(outRefundNo)) {
+                outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+            }
+            record.setOutRefundNo(outRefundNo);
+            record.setRefundId(null); // 支付宝没有单独的退款单号
+            record.setRefundStatus("SUCCESS");
+            
+            // 金额信息(支付宝返回的是元,需要转换为分)
+            String refundFeeStr = refundResponse.getString("refund_fee");
+            String sendBackFeeStr = refundResponse.getString("send_back_fee");
+            if (StringUtils.isNotBlank(refundFeeStr)) {
+                record.setRefundAmount(new BigDecimal(refundFeeStr).multiply(new BigDecimal(100)).longValue());
+            }
+            if (StringUtils.isNotBlank(sendBackFeeStr)) {
+                record.setActualRefundAmount(new BigDecimal(sendBackFeeStr).multiply(new BigDecimal(100)).longValue());
+            }
+            // 订单总金额从params获取,如果没有则从退款金额推算
+            String totalAmountStr = params.get("totalAmount");
+            if (StringUtils.isNotBlank(totalAmountStr)) {
+                record.setTotalAmount(new BigDecimal(totalAmountStr).multiply(new BigDecimal(100)).longValue());
+            }
+            record.setCurrency("CNY");
+            
+            // 退款原因
+            record.setRefundReason(params.get("refundReason"));
+            
+            // 退款详情
+            if (refundResponse.containsKey("refund_detail_item_list")) {
+                JSONArray refundDetailList = refundResponse.getJSONArray("refund_detail_item_list");
+                if (refundDetailList != null) {
+                    record.setRefundDetail(JSON.toJSONString(refundDetailList));
+                }
+            }
+            
+            // 用户收到退款账户
+            record.setUserReceivedAccount(refundResponse.getString("buyer_logon_id"));
+            
+            // 退款时间
+            String gmtRefundPayStr = refundResponse.getString("gmt_refund_pay");
+            if (StringUtils.isNotBlank(gmtRefundPayStr)) {
+                try {
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                    Date refundTime = sdf.parse(gmtRefundPayStr);
+                    record.setRefundSuccessTime(refundTime);
+                    record.setRefundCreateTime(refundTime); // 支付宝没有单独的创建时间
+                } catch (ParseException e) {
+                    log.warn("解析退款时间失败: {}", gmtRefundPayStr, e);
+                }
+            }
+            
+            // 业务信息(从params获取,如果有的话)
+            if (params.containsKey("userId")) {
+                try {
+                    record.setUserId(Integer.parseInt(params.get("userId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析userId失败: {}", params.get("userId"));
+                }
+            }
+            if (params.containsKey("orderId")) {
+                record.setOrderId(params.get("orderId"));
+            }
+            if (params.containsKey("storeId")) {
+                try {
+                    record.setStoreId(Integer.parseInt(params.get("storeId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析storeId失败: {}", params.get("storeId"));
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(responseBody.toJSONString());
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 从支付宝退款错误响应构建RefundRecord对象(失败情况)
+     */
+    private RefundRecord buildRefundRecordFromAlipayError(JSONObject refundResponse, JSONObject responseBody, Map<String, String> params) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.ALIPAY.getType());
+            record.setOutTradeNo(params.get("outTradeNo"));
+            // 部分退款编号作为商户退款单号,如果没有则生成一个
+            String outRefundNo = params.get("partialRefundCode");
+            if (StringUtils.isBlank(outRefundNo)) {
+                outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+            }
+            record.setOutRefundNo(outRefundNo);
+            record.setRefundStatus("ABNORMAL");
+            
+            // 金额信息
+            String refundAmountStr = params.get("refundAmount");
+            if (StringUtils.isNotBlank(refundAmountStr)) {
+                record.setRefundAmount(new BigDecimal(refundAmountStr).multiply(new BigDecimal(100)).longValue());
+            }
+            String totalAmountStr = params.get("totalAmount");
+            if (StringUtils.isNotBlank(totalAmountStr)) {
+                record.setTotalAmount(new BigDecimal(totalAmountStr).multiply(new BigDecimal(100)).longValue());
+            }
+            record.setCurrency("CNY");
+            
+            // 退款原因
+            record.setRefundReason(params.get("refundReason"));
+            
+            // 错误信息
+            record.setErrorCode(refundResponse.getString("code"));
+            record.setErrorMsg(refundResponse.getString("sub_msg"));
+            
+            // 业务信息
+            if (params.containsKey("userId")) {
+                try {
+                    record.setUserId(Integer.parseInt(params.get("userId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析userId失败: {}", params.get("userId"));
+                }
+            }
+            if (params.containsKey("orderId")) {
+                record.setOrderId(params.get("orderId"));
+            }
+            if (params.containsKey("storeId")) {
+                try {
+                    record.setStoreId(Integer.parseInt(params.get("storeId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析storeId失败: {}", params.get("storeId"));
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(responseBody.toJSONString());
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            record.setRefundCreateTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
 }
 

+ 384 - 10
alien-store/src/main/java/shop/alien/store/strategy/payment/impl/WeChatPaymentStrategyImpl.java

@@ -8,6 +8,8 @@ import okhttp3.*;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.RefundRecord;
+import shop.alien.store.service.RefundRecordService;
 import shop.alien.store.strategy.payment.PaymentStrategy;
 import shop.alien.store.util.WXPayUtility;
 import shop.alien.util.common.UniqueRandomNumGenerator;
@@ -22,7 +24,12 @@ import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.Signature;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Base64;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -39,6 +46,8 @@ import java.util.Map;
 @RequiredArgsConstructor
 public class WeChatPaymentStrategyImpl implements PaymentStrategy {
 
+    private final RefundRecordService refundRecordService;
+
     @Value("${payment.wechatPay.host}")
     private String wechatPayApiHost;
 
@@ -202,7 +211,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
             result.put("prepayId", response.prepayId);
             result.put("appId", appId);
             result.put("mchId", mchId);
-            result.put("outTradeNo", request.outTradeNo);
+            result.put("orderNo", request.outTradeNo);
             // 生成sign
 //            appId
 
@@ -251,6 +260,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
 
     @Override
     public String handleRefund(Map<String,String> params) throws Exception {
+
         CreateRequest request = new CreateRequest();
         // 微信支付订单号和商户订单号必须二选一,不能同时为空,查询的时候使用的是商户订单号
         //request.transactionId = "1217752501201407033233368018";
@@ -265,7 +275,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
         //request.fundsAccount = ReqFundsAccount.AVAILABLE;
         // 金额信息
         request.amount = new AmountReq();
-        request.amount.refund = new BigDecimal(params.get("refundAmount")).multiply(new BigDecimal(100)).longValue();
+        request.amount.refund = new BigDecimal(params.get("refundAmount")).longValue();
         // 退款出资账户及金额 目前不需要,需要的时候再看
         /*
         request.amount.from = new ArrayList<>();
@@ -276,7 +286,7 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
             request.amount.from.add(fromItem);
         };*/
         // 订单总金额
-        request.amount.total = new BigDecimal(params.get("totalAmount")).multiply(new BigDecimal(100)).longValue();
+        request.amount.total = new BigDecimal(params.get("totalAmount")).longValue();
         // 退款币种 目前默认人民币 CNY
         request.amount.currency = "CNY";
         // 退款商品信息 目前不需要,需要的时候再看
@@ -292,16 +302,119 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
             goodsDetailItem.refundQuantity = 1L;
             request.goodsDetail.add(goodsDetailItem);
         };*/
+        // 记录退款请求信息
+        log.info("开始处理微信支付退款,商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分,退款原因:{}",
+                request.outTradeNo, request.outRefundNo, request.amount.refund, request.amount.total, request.reason);
+        String refundResult = "";
         try {
             Refund response = refundRun(request);
-            String refundReslut = "";
-            // TODO: 请求成功,继续业务逻辑
-            System.out.println(response);
-        } catch (WXPayUtility.ApiException e) {
-            // TODO: 请求失败,根据状态码执行不同的逻辑
-            e.printStackTrace();
+            // 退款状态
+            String status = response.status != null ? response.status.name() : "UNKNOWN";
+            if ("SUCCESS".equals(status) || "PROCESSING".equals(status)) {
+                // refund_id 申请退款受理成功时,该笔退款单在微信支付侧生成的唯一标识。
+                String refundId = response.refundId;
+                // 商户申请退款时传的商户系统内部退款单号。
+                String outRefundNo = response.outRefundNo != null ? response.outRefundNo : request.outRefundNo;
+                // 微信支付订单号
+                String transactionId = response.transactionId;
+
+                // 退款金额信息
+                String refundAmount = response.amount != null && response.amount.refund != null
+                        ? String.valueOf(response.amount.refund) : String.valueOf(request.amount.refund);
+                String totalAmount = response.amount != null && response.amount.total != null
+                        ? String.valueOf(response.amount.total) : String.valueOf(request.amount.total);
+                // 退款成功时间
+                String successTime = response.successTime;
+                // 退款创建时间
+                String createTime = response.createTime;
+                // 退款渠道
+                String channel = response.channel != null ? response.channel.name() : "UNKNOWN";
+
+                // 记录退款成功详细信息
+                log.info("微信支付退款成功 - 商户订单号:{},微信支付订单号:{},商户退款单号:{},微信退款单号:{}," +
+                                "退款状态:{},退款金额:{}分,订单总金额:{}分,退款渠道:{},创建时间:{},成功时间:{}",
+                        request.outTradeNo, transactionId, outRefundNo, refundId, status, refundAmount,
+                        totalAmount, channel, createTime, successTime != null ? successTime : "未完成");
+
+                // 保存到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromWeChatResponse(response, request, params);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("微信支付退款记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("微信支付退款记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存微信支付退款记录到RefundRecord表失败", e);
+                    // 不抛出异常,避免影响原有逻辑
+                }
+
+                refundResult = "调用成功";
+                return refundResult;
+            } else {
+                log.error("微信支付退款失败 - 商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分," +
+                                "退款状态:{}",
+                        request.outTradeNo, request.outRefundNo, request.amount.refund,
+                        request.amount.total, status);
+                
+                // 保存失败记录到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromWeChatError(response, request, params, status);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("微信支付退款失败记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("微信支付退款失败记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存微信支付退款失败记录到RefundRecord表失败", e);
+                }
+                
+                return "退款失败";
+            }
+        }
+        catch (Exception e) {
+            // 记录其他异常
+            log.error("微信支付退款异常 - 商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分," +
+                    "退款原因:{},异常信息:{}",
+                    request.outTradeNo, request.outRefundNo, request.amount.refund, 
+                    request.amount.total, request.reason, e.getMessage(), e);
+            
+            // 保存异常记录到通用退款记录表
+            try {
+                RefundRecord refundRecord = buildRefundRecordFromWeChatException(request, params, e);
+                if (refundRecord != null) {
+                    // 检查是否已存在,避免重复插入
+                    long count = refundRecordService.lambdaQuery()
+                            .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                            .count();
+                    if (count == 0) {
+                        refundRecordService.save(refundRecord);
+                        log.info("微信支付退款异常记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                    } else {
+                        log.info("微信支付退款异常记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                    }
+                }
+            } catch (Exception ex) {
+                log.error("保存微信支付退款异常记录到RefundRecord表失败", ex);
+            }
+            
+            refundResult = "退款处理异常:" + e.getMessage();
+            return refundResult;
         }
-        return null;
     }
 
 
@@ -864,4 +977,265 @@ public class WeChatPaymentStrategyImpl implements PaymentStrategy {
         @SerializedName("UNAVAILABLE")
         UNAVAILABLE
     }
+
+    /**
+     * 从微信支付退款响应构建RefundRecord对象(成功情况)
+     */
+    private RefundRecord buildRefundRecordFromWeChatResponse(Refund response, CreateRequest request, Map<String, String> params) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.WECHAT_PAY.getType());
+            record.setOutTradeNo(response.outTradeNo != null ? response.outTradeNo : request.outTradeNo);
+            record.setTransactionId(response.transactionId);
+            record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
+            record.setRefundId(response.refundId);
+            record.setRefundStatus(response.status != null ? response.status.name() : "UNKNOWN");
+            
+            // 金额信息(微信返回的是分)
+            if (response.amount != null) {
+                record.setTotalAmount(response.amount.total);
+                record.setRefundAmount(response.amount.refund);
+                record.setActualRefundAmount(response.amount.refund); // 微信退款金额就是实际退款金额
+                if (response.amount.currency != null) {
+                    record.setCurrency(response.amount.currency);
+                } else {
+                    record.setCurrency("CNY");
+                }
+            } else {
+                record.setTotalAmount(request.amount.total);
+                record.setRefundAmount(request.amount.refund);
+                record.setActualRefundAmount(request.amount.refund);
+                record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
+            }
+            
+            // 退款原因
+            record.setRefundReason(request.reason);
+            
+            // 退款渠道
+            if (response.channel != null) {
+                record.setRefundChannel(response.channel.name());
+            }
+            
+            // 退款资金来源
+            if (response.fundsAccount != null) {
+                record.setFundsAccount(response.fundsAccount.name());
+            }
+            
+            // 用户收到退款账户
+            record.setUserReceivedAccount(response.userReceivedAccount);
+            
+            // 退款详情(如果有优惠信息)
+            if (response.promotionDetail != null && !response.promotionDetail.isEmpty()) {
+                record.setRefundDetail(WXPayUtility.toJson(response.promotionDetail));
+            }
+            
+            // 退款时间(微信返回的是ISO 8601格式字符串,需要转换为Date)
+            if (response.createTime != null) {
+                Date createTime = parseWeChatTime(response.createTime);
+                if (createTime != null) {
+                    record.setRefundCreateTime(createTime);
+                }
+            }
+            if (response.successTime != null) {
+                Date successTime = parseWeChatTime(response.successTime);
+                if (successTime != null) {
+                    record.setRefundSuccessTime(successTime);
+                }
+            }
+            
+            // 业务信息(从params获取,如果有的话)
+            if (params != null) {
+                if (params.containsKey("userId")) {
+                    try {
+                        record.setUserId(Integer.parseInt(params.get("userId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析userId失败: {}", params.get("userId"));
+                    }
+                }
+                if (params.containsKey("orderId")) {
+                    record.setOrderId(params.get("orderId"));
+                }
+                if (params.containsKey("storeId")) {
+                    try {
+                        record.setStoreId(Integer.parseInt(params.get("storeId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析storeId失败: {}", params.get("storeId"));
+                    }
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(WXPayUtility.toJson(response));
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 从微信支付退款错误响应构建RefundRecord对象(失败情况)
+     */
+    private RefundRecord buildRefundRecordFromWeChatError(Refund response, CreateRequest request, Map<String, String> params, String status) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.WECHAT_PAY.getType());
+            record.setOutTradeNo(response.outTradeNo != null ? response.outTradeNo : request.outTradeNo);
+            record.setTransactionId(response.transactionId);
+            record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
+            record.setRefundId(response.refundId);
+            record.setRefundStatus(status);
+            
+            // 金额信息
+            if (response.amount != null) {
+                record.setTotalAmount(response.amount.total);
+                record.setRefundAmount(response.amount.refund);
+                record.setCurrency(response.amount.currency != null ? response.amount.currency : "CNY");
+            } else {
+                record.setTotalAmount(request.amount.total);
+                record.setRefundAmount(request.amount.refund);
+                record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
+            }
+            
+            // 退款原因
+            record.setRefundReason(request.reason);
+            
+            // 退款时间
+            if (response.createTime != null) {
+                Date createTime = parseWeChatTime(response.createTime);
+                if (createTime != null) {
+                    record.setRefundCreateTime(createTime);
+                }
+            }
+            
+            // 业务信息
+            if (params != null) {
+                if (params.containsKey("userId")) {
+                    try {
+                        record.setUserId(Integer.parseInt(params.get("userId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析userId失败: {}", params.get("userId"));
+                    }
+                }
+                if (params.containsKey("orderId")) {
+                    record.setOrderId(params.get("orderId"));
+                }
+                if (params.containsKey("storeId")) {
+                    try {
+                        record.setStoreId(Integer.parseInt(params.get("storeId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析storeId失败: {}", params.get("storeId"));
+                    }
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(WXPayUtility.toJson(response));
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 从微信支付退款异常构建RefundRecord对象(异常情况)
+     */
+    private RefundRecord buildRefundRecordFromWeChatException(CreateRequest request, Map<String, String> params, Exception e) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.WECHAT_PAY.getType());
+            record.setOutTradeNo(request.outTradeNo);
+            record.setOutRefundNo(request.outRefundNo);
+            record.setRefundStatus("ABNORMAL");
+            
+            // 金额信息
+            record.setTotalAmount(request.amount.total);
+            record.setRefundAmount(request.amount.refund);
+            record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
+            
+            // 退款原因
+            record.setRefundReason(request.reason);
+            
+            // 错误信息
+            if (e instanceof WXPayUtility.ApiException) {
+                WXPayUtility.ApiException apiException = (WXPayUtility.ApiException) e;
+                record.setErrorCode(String.valueOf(apiException.getErrorCode()));
+                record.setErrorMsg(apiException.getMessage());
+            } else {
+                record.setErrorMsg(e.getMessage());
+            }
+            
+            // 业务信息
+            if (params != null) {
+                if (params.containsKey("userId")) {
+                    try {
+                        record.setUserId(Integer.parseInt(params.get("userId")));
+                    } catch (NumberFormatException ex) {
+                        log.warn("解析userId失败: {}", params.get("userId"));
+                    }
+                }
+                if (params.containsKey("orderId")) {
+                    record.setOrderId(params.get("orderId"));
+                }
+                if (params.containsKey("storeId")) {
+                    try {
+                        record.setStoreId(Integer.parseInt(params.get("storeId")));
+                    } catch (NumberFormatException ex) {
+                        log.warn("解析storeId失败: {}", params.get("storeId"));
+                    }
+                }
+            }
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            record.setRefundCreateTime(new Date());
+            
+            return record;
+        } catch (Exception ex) {
+            log.error("构建RefundRecord对象失败", ex);
+            return null;
+        }
+    }
+
+    /**
+     * 解析微信支付返回的时间字符串(ISO 8601格式)
+     * 例如:2018-06-08T10:34:56+08:00
+     */
+    private Date parseWeChatTime(String timeStr) {
+        if (timeStr == null || timeStr.trim().isEmpty()) {
+            return null;
+        }
+        try {
+            // 尝试解析ISO 8601格式
+            OffsetDateTime dateTime = OffsetDateTime.parse(timeStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+            return Date.from(dateTime.toInstant());
+        } catch (Exception e) {
+            // 如果ISO 8601解析失败,尝试其他格式
+            try {
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                return sdf.parse(timeStr);
+            } catch (ParseException ex) {
+                log.warn("解析微信支付时间失败: {}", timeStr, ex);
+                return null;
+            }
+        }
+    }
 }

+ 264 - 0
alien-store/src/main/java/shop/alien/store/util/AiUserViolationUtils.java

@@ -0,0 +1,264 @@
+package shop.alien.store.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.LifeUser;
+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 = "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;
+
+    @Value("${third-party-pass-word.base-url:123456}")
+    private String passWord;
+
+    private String userComplaintRecordUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/user_complaint_record/submit";
+
+
+    /**
+     * 登录 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 (lifeuserViolation.getViolationType().equals("1")){
+            analyzeRequest.put("complaint_type", "用户违规");
+        } else if (lifeuserViolation.getViolationType().equals("2")){
+            analyzeRequest.put("complaint_type", "色情低俗");
+        } else if (lifeuserViolation.getViolationType().equals("3")){
+            analyzeRequest.put("complaint_type", "违法违规");
+        } else if (lifeuserViolation.getViolationType().equals("5")){
+            analyzeRequest.put("complaint_type", "涉嫌诈骗");
+        } else if (lifeuserViolation.getViolationType().equals("6")){
+            analyzeRequest.put("complaint_type", "人身攻击");
+        } else if (lifeuserViolation.getViolationType().equals("7")){
+            analyzeRequest.put("complaint_type", "种族歧视");
+        } else if (lifeuserViolation.getViolationType().equals("4")){
+            analyzeRequest.put("complaint_type", "辱骂敏感");
+        } else if (lifeuserViolation.getViolationType().equals("12")){
+            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);
+    }
+}

+ 78 - 0
alien-store/src/main/java/shop/alien/store/util/ai/AiAuthTokenUtil.java

@@ -0,0 +1,78 @@
+package shop.alien.store.util.ai;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+/**
+ * AI 服务鉴权配置
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiAuthTokenUtil {
+
+    private final RestTemplate restTemplate;
+
+    @Value("${third-party-login.base-url}")
+    private String loginUrl;
+
+    @Value("${third-party-user-name.base-url}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url}")
+    private String passWord;
+
+    /**
+     * 登录 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;
+    }
+}
+
+

+ 26 - 0
alien-store/src/main/java/shop/alien/store/util/ali/AliSms.java

@@ -9,10 +9,14 @@ 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.store.config.BaseRedisService;
 import shop.alien.store.config.NacosConfig;
 import shop.alien.util.common.RandomCreateUtil;
 
+import java.util.Date;
+
 /**
  * 阿里云验证码配置
  *
@@ -28,6 +32,8 @@ public class AliSms {
 
     private final NacosConfig nacosConfig;
 
+    private final StoreVerificationCodeMapper storeVerificationCodeMapper;
+
     @Value("${ali.sms.accessKeyId}")
     private String accessKeyId;
 
@@ -99,6 +105,7 @@ public class AliSms {
             if (nacosConfig.getTestPhone().contains(phone)) {
                 // 验证码发送成功,将验证码保存到redis中 设置60秒过期
                 baseRedisService.setString("verification_" + appTypeStr + "_" + businessTypeStr + "_" + phone, "123456", codeTimeOut);
+                saveVerificationCode(appType, businessType, phone, "123456");
                 return 123456;
             }
             // -----------------测试用手机号--------------------------------------------------------------------------------------------
@@ -129,6 +136,8 @@ public class AliSms {
             }
             // 验证码发送成功,将验证码保存到redis中 设置60秒过期
             baseRedisService.setString("verification_" + appTypeStr + "_" + businessTypeStr + "_" + phone, code.toString(), codeTimeOut);
+            saveVerificationCode(appType, businessType, phone, code.toString());
+
             return code;
         } catch (Exception e) {
             log.error("AliSmsConfig.sendSms ERROR Msg={}", e.getMessage());
@@ -210,4 +219,21 @@ public class AliSms {
         }
     }
 
+    private void saveVerificationCode(Integer appType, Integer businessType, String phone, String code) {
+        try {
+            StoreVerificationCode entity = new StoreVerificationCode();
+            entity.setAppType(appType);
+            entity.setBusinessType(businessType);
+            entity.setPhone(phone);
+            entity.setCode(code);
+            Date now = new Date();
+            entity.setCreatedTime(now);
+            entity.setUpdatedTime(now);
+            entity.setDeleteFlag(0);
+            storeVerificationCodeMapper.insert(entity);
+        } catch (Exception ex) {
+            log.error("AliSms.saveVerificationCode ERROR Msg={}", ex.getMessage(), ex);
+        }
+    }
+
 }

+ 1 - 1
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java

@@ -93,7 +93,7 @@ public class BusinessLicenseOcrStrategy extends AbstractOcrStrategy {
                 ocrImageUpload.setStoreUserId(params.get("storeUserId"));
                 ocrImageUpload.setImageUrl(imageUrl);
                 ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
-                ocrImageUpload.setOcrType(OcrTypeEnum.ID_CARD.getCode());
+                ocrImageUpload.setOcrType(OcrTypeEnum.BUSINESS_LICENSE.getCode());
                 super.saveOcrImage(ocrImageUpload);
                 resultArray.add(jsonObject.getJSONObject("data"));
             } catch (TeaException error) {

+ 1 - 1
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java

@@ -93,7 +93,7 @@ public class FoodManageLicenseOcrStrategy extends AbstractOcrStrategy {
                 ocrImageUpload.setStoreUserId(params.get("storeUserId"));
                 ocrImageUpload.setImageUrl(imageUrl);
                 ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
-                ocrImageUpload.setOcrType(OcrTypeEnum.ID_CARD.getCode());
+                ocrImageUpload.setOcrType(OcrTypeEnum.FOOD_MANAGE_LICENSE.getCode());
                 super.saveOcrImage(ocrImageUpload);
                 resultArray.add(jsonObject.getJSONObject("data"));
             } catch (TeaException error) {

+ 1 - 1
alien-store/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test