Quellcode durchsuchen

Merge branch 'sit' into uat

# Conflicts:
#	alien-job/src/main/java/shop/alien/job/feign/SecondGoodsFeign.java
#	alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java
#	alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java
#	alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java
#	alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java
#	alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java
#	alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java
#	alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java
lutong vor 1 Woche
Ursprung
Commit
f45bd428fd
34 geänderte Dateien mit 3346 neuen und 463 gelöschten Zeilen
  1. 19 41
      alien-entity/src/main/java/shop/alien/entity/store/DrinkInfo.java
  2. 0 4
      alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java
  3. 2 2
      alien-entity/src/main/java/shop/alien/entity/store/vo/DrinkInfoVo.java
  4. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategoryVo.java
  5. 0 14
      alien-entity/src/main/java/shop/alien/mapper/DrinkInfoMapper.java
  6. 0 34
      alien-entity/src/main/resources/mapper/DrinkInfoMapper.xml
  7. 18 0
      alien-job/src/main/java/shop/alien/job/feign/SecondGoodsFeign.java
  8. 5 41
      alien-job/src/main/java/shop/alien/job/jobhandler/VideoModerationJobHandler.java
  9. 373 0
      alien-job/src/main/java/shop/alien/job/second/AiCheckXxlJob.java
  10. 8 2
      alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java
  11. 36 0
      alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java
  12. 336 0
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java
  13. 40 0
      alien-second/src/main/java/shop/alien/second/controller/SecondGoodsController.java
  14. 98 0
      alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java
  15. 6 0
      alien-second/src/main/java/shop/alien/second/service/SecondGoodsService.java
  16. 6 0
      alien-second/src/main/java/shop/alien/second/service/VideoModerationService.java
  17. 763 0
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java
  18. 18 0
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java
  19. 78 40
      alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java
  20. 72 1
      alien-second/src/main/java/shop/alien/second/service/impl/VideoModerationServiceImpl.java
  21. 271 0
      alien-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java
  22. 654 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java
  23. 5 4
      alien-store/src/main/java/shop/alien/store/controller/DrinkInfoController.java
  24. 18 0
      alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java
  25. 13 7
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  26. 6 31
      alien-store/src/main/java/shop/alien/store/service/DrinkInfoService.java
  27. 11 0
      alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java
  28. 5 2
      alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
  29. 248 141
      alien-store/src/main/java/shop/alien/store/service/impl/DrinkInfoServiceImpl.java
  30. 7 7
      alien-store/src/main/java/shop/alien/store/service/impl/LifeBlacklistServiceImpl.java
  31. 100 81
      alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java
  32. 30 0
      alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java
  33. 79 10
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  34. 18 1
      alien-store/src/main/java/shop/alien/store/util/FileUploadUtil.java

+ 19 - 41
alien-entity/src/main/java/shop/alien/entity/store/DrinkInfo.java

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

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

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

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

@@ -21,8 +21,8 @@ public class DrinkInfoVo {
     @ApiModelProperty(value = "主键ID")
     private Integer id;
 
-    @ApiModelProperty(value = "类型(枚举值:酒水/餐食)")
-    private String type;
+    @ApiModelProperty(value = "类型(1:餐食,2:酒水)")
+    private Integer type;
 
     @ApiModelProperty(value = "商品名称")
     private String name;

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

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

+ 0 - 14
alien-entity/src/main/java/shop/alien/mapper/DrinkInfoMapper.java

@@ -1,14 +0,0 @@
-package shop.alien.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import shop.alien.entity.store.DrinkInfo;
-
-/**
- * 酒水餐食信息表 Mapper 接口
- *
- * @author ssk
- * @since 2025-06-13
- */
-public interface DrinkInfoMapper extends BaseMapper<DrinkInfo> {
-
-}

+ 0 - 34
alien-entity/src/main/resources/mapper/DrinkInfoMapper.xml

@@ -1,34 +0,0 @@
-<?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.DrinkInfoMapper">
-
-    <!-- 通用查询映射结果 -->
-    <resultMap id="BaseResultMap" type="shop.alien.entity.store.DrinkInfo">
-        <id column="id" property="id" />
-        <result column="type" property="type" />
-        <result column="name" property="name" />
-        <result column="price" property="price" />
-        <result column="cost_price" property="costPrice" />
-        <result column="category" property="category" />
-        <result column="alcohol_volume" property="alcoholVolume" />
-        <result column="pic_url" property="picUrl" />
-        <result column="description" property="description" />
-        <result column="flavor" property="flavor" />
-        <result column="store_id" property="storeId" />
-        <result column="is_recommended" property="isRecommended" />
-        <result column="status" property="status" />
-        <result column="sort" property="sort" />
-        <result column="is_deleted" property="isDeleted" />
-        <result column="create_user_id" property="createUserId" />
-        <result column="create_time" property="createTime" />
-        <result column="update_user_id" property="updateUserId" />
-        <result column="update_time" property="updateTime" />
-    </resultMap>
-
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-        id, type, name, price, cost_price, category, alcohol_volume, pic_url, description, flavor, store_id, 
-        is_recommended, status, sort, is_deleted, create_user_id, create_time, update_user_id, update_time
-    </sql>
-
-</mapper>

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

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

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

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

@@ -0,0 +1,373 @@
+package shop.alien.job.second;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.xxl.job.core.context.XxlJobHelper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.ResourceAccessException;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.http.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.SecondRiskControlRecord;
+import shop.alien.entity.store.*;
+import shop.alien.mapper.*;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+import shop.alien.util.common.Constants;
+
+import java.net.SocketException;
+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
+//@RestController
+//@RequestMapping("/job")
+//@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;
+
+    @Autowired
+    private final StoreImgMapper storeImgMapper;
+
+    // 第三方接口地址 登录接口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;
+
+    // 第三方接口地址 登录接口URL
+    @Value("${third-party-goods.base-url}")
+    private String goodsUrl;
+
+    // 第三方接口地址 登录接口URL
+    @Value("${third-party-goodsresult.base-url}")
+    private String goodsResultUrl;
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    /**
+     * AI自动审核任务处理器
+     * <p>
+     * 定时任务方法,用于批量处理用户违规举报记录,通过AI接口进行自动审核。
+     * 主要流程:
+     * 1. 查询所有待处理的用户违规记录
+     * 2. 遍历每条记录,组装审核请求数据
+     * 3. 调用AI审核接口进行自动审核(当前代码中已准备请求体,待实现接口调用)
+     * </p>
+     *
+     * @author Lhaibo
+     * @date 2025/11/28
+     * @since 1.0.0
+     */
+    @XxlJob("aiCheckJobHandler")
+//    @PostMapping("/aiCheckJobHandler")
+    public void aiCheckJobHandler() {
+        log.info("开始执行AI自动审核任务");
+
+        try {
+            log.info("登录Ai服务获取token..." + loginUrl);
+            //构建请求参数
+            MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+            formData.add("username", userName);    // 表单字段 1:用户名
+            formData.add("password", passWord);    // 表单字段 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(loginUrl, 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::getReportContextType, "4")
+                            .eq(LifeUserViolation::getProcessingStatus, "0"));
+
+                    // 遍历每条违规记录,组装AI审核请求数据
+                    for (LifeUserViolation violation : lifeUserViolations) {
+                        // 初始化请求体Map
+                        Map<String, Object> requestBody = new HashMap<>();
+
+                        // 设置投诉记录ID
+                        requestBody.put("complaint_id", violation.getId().toString());
+
+                        // 查询投诉类型字典信息
+                        StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>()
+                                .eq(StoreDictionary::getDictId, 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().toString());
+                            requestBody.put("reported_user_type", "");
+                            requestBody.put("reported_info", lifeUserMapper.selectById(secondGoodsRecord.getUserId()));
+                            requestBody.put("product_name", secondGoodsRecord.getTitle());
+                        }
+                        // 设置商品相关信息
+                        requestBody.put("product_info", secondGoodsRecord);
+                        requestBody.put("product_id", violation.getGoodsId().toString());
+
+                        // 设置投诉文本内容
+                        requestBody.put("complaint_text", violation.getOtherReasonContent());
+
+                        LambdaQueryWrapper<StoreImg> wrapper = new LambdaQueryWrapper<>();
+                        wrapper.eq(StoreImg::getStoreId, violation.getId());
+                        wrapper.eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_REPORT);
+                        List<StoreImg> imgList = storeImgMapper.selectList(wrapper);
+
+                        // 将 imgList 中的 ImgUrl 提取为 String[] 数组
+                        String[] imgUrls = imgList.stream()
+                                .map(StoreImg::getImgUrl)
+                                .toArray(String[]::new);
+
+                        // 设置证据图片数组
+                        requestBody.put("evidence_images", imgUrls);
+
+                        HttpHeaders aiHeaders = new HttpHeaders();
+                        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+                        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(goodsUrl, request, String.class);
+                            log.info("AI自动审核结果:{}", response.getBody());
+                            if (response.getStatusCodeValue() != 200) {
+                                throw new RuntimeException("AI门店审核接口调用失败 http状态:" + response.getStatusCode());
+                            }
+                            if (StringUtils.isNotEmpty(response.getBody())) {
+                                com.alibaba.fastjson.JSONObject taskObject = com.alibaba.fastjson.JSONObject.parseObject(response.getBody());
+                                if (taskObject.getInteger("code") == 200) {
+                                    com.alibaba.fastjson.JSONObject data = taskObject.getJSONObject("data");
+                                    if (data != null) {
+                                        String taskId = data.getString("task_id");
+                                        violation.setAiTaskId(taskId);
+                                        lifeUserViolationMapper.updateById(violation);
+                                    }
+                                }
+                            }
+                        } catch (ResourceAccessException e) {
+                            if (e.getCause() instanceof SocketException) {
+                                log.error("AI自动审核请求网络异常,连接被中断: {}", e.getMessage());
+                            } else {
+                                log.error("AI自动审核请求资源访问异常", e);
+                            }
+                        } catch (Exception e) {
+                            log.error("AI自动审核请求异常", e);
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("AI自动审核任务执行异常", e);
+        }
+
+        log.info("AI自动审核任务执行完成");
+    }
+
+    @XxlJob("aiCheckJobHandlerResult")
+    public void aiCheckJobHandlerResult() {
+        log.info("登录Ai服务获取token..." + loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+        ResponseEntity<String> postForEntity = null;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            postForEntity = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+        }
+
+        if (postForEntity != null) {
+            if (postForEntity.getStatusCodeValue() == 200) {
+                log.info("请求Ai服务登录成功 postForEntity.getBody()\t" + postForEntity.getBody());
+                String responseBody = postForEntity.getBody();
+                JSONObject jsonObject = JSONObject.parseObject(responseBody);
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                String accessToken = dataJson.getString("access_token");
+
+                // 查询所有待处理的用户违规记录
+                List<LifeUserViolation> lifeUserViolations = lifeUserViolationMapper.selectList(new LambdaQueryWrapper<LifeUserViolation>()
+                        .eq(LifeUserViolation::getReportContextType, "4")
+                        .eq(LifeUserViolation::getProcessingStatus, "0"));
+
+                for (LifeUserViolation violation : lifeUserViolations) {
+                    // 初始化请求体Map
+                    Map<String, Object> requestBody = new HashMap<>();
+
+                    // 设置投诉记录ID
+                    requestBody.put("task_id", violation.getAiTaskId());
+                    HttpHeaders aiHeaders = new HttpHeaders();
+                    aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+                    aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+                    System.out.println(requestBody);
+                    HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+                    ResponseEntity<String> response = null;
+                    try {
+                        response = restTemplate.postForEntity(goodsResultUrl, request, String.class);
+                        if (response.getStatusCodeValue() != 200) {
+                            throw new RuntimeException("AI门店审核接口调用失败 http状态:" + response.getStatusCode());
+                        }
+                        if (StringUtils.isNotEmpty(response.getBody())) {
+                            com.alibaba.fastjson.JSONObject taskObject = com.alibaba.fastjson.JSONObject.parseObject(response.getBody());
+                            if (taskObject.getInteger("code") == 200) {
+                                com.alibaba.fastjson.JSONObject data = taskObject.getJSONObject("data");
+                                if (data != null) {
+                                    boolean isValid = data.getBoolean("is_valid");
+                                    String processingStatus;
+                                    if (isValid) {
+                                         processingStatus = "1";
+                                    } else {
+                                        processingStatus = "2";
+                                    }
+                                    violation.setReportResult(data.getString("matched_type"));
+                                    violation.setProcessingStatus(processingStatus);
+                                    lifeUserViolationMapper.updateById(violation);
+
+                                    // 发送通知
+                                    LifeNotice lifeMessage = new LifeNotice();
+                                    LifeUser lifeUser = lifeUserMapper.selectById(violation.getReportingUserId());
+                                    lifeMessage.setReceiverId("user_" + lifeUser.getUserPhone());
+                                    String text = "您的举报用户结果为" + (isValid ? "违规" : "未违规");
+                                    com.alibaba.fastjson.JSONObject lifeMessagejson = new com.alibaba.fastjson.JSONObject();
+                                    lifeMessagejson.put("title", "平台已受理");
+                                    lifeMessagejson.put("message", text);
+                                    lifeMessage.setContext(lifeMessagejson.toJSONString());
+
+                                    lifeMessage.setTitle("举报通知");
+                                    lifeMessage.setBusinessId(violation.getId());
+                                    lifeMessage.setSenderId("system");
+                                    lifeMessage.setIsRead(0);
+                                    lifeMessage.setNoticeType(1);
+                                    lifeNoticeMapper.insert(lifeMessage);
+                                }
+                            }
+                        }
+                    } catch (ResourceAccessException e) {
+                        if (e.getCause() instanceof SocketException) {
+                            log.error("AI自动审核结果查询网络异常,连接被中断: {}", e.getMessage());
+                        } else {
+                            log.error("AI自动审核结果查询资源访问异常", e);
+                        }
+                    } catch (Exception e) {
+                        log.error("AI自动审核结果查询异常", e);
+                    }
+
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 二手商品风控记录审核任务
+     * <p>
+     * 定时查询风控记录表中待处理的数据(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("二手商品风控记录审核任务执行完成");
+    }
+}

+ 8 - 2
alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java

@@ -146,7 +146,7 @@ public class AiUserViolationJob {
                             aiTask.setProcessingStatus("1");
                             aiTask.setReportResult(dataJsonObj.getString("decision_reason"));
                         } else {
-                            aiTask.setProcessingStatus("0");
+                            aiTask.setProcessingStatus("2");
                             aiTask.setReportResult(dataJsonObj.getString("decision_reason"));
                         }
                     }
@@ -156,7 +156,13 @@ public class AiUserViolationJob {
                     LifeUser lifeUser = lifeUserMapper.selectById(task.getReportingUserId());
                     lifeMessage.setReceiverId("user_" + lifeUser.getUserPhone());
                     String text = "您的举报用户结果为" + (aiTask.getProcessingStatus().equals("1") ? "违规" : "未违规");
-                    lifeMessage.setContext(text);
+
+                    com.alibaba.fastjson.JSONObject lifeMessagejson = new com.alibaba.fastjson.JSONObject();
+                    lifeMessagejson.put("title", "平台已受理");
+                    lifeMessagejson.put("message", text);
+                    lifeMessage.setContext(lifeMessagejson.toJSONString());
+                    lifeMessage.setTitle("举报通知");
+                    lifeMessage.setBusinessId(task.getId());
                     lifeMessage.setSenderId("system");
                     lifeMessage.setIsRead(0);
                     lifeMessage.setNoticeType(1);

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

@@ -0,0 +1,36 @@
+package shop.alien.job.second;
+
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.job.feign.SecondGoodsFeign;
+
+/**
+ * 商品审核定时任务
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class goodsCheckJob {
+
+    private final SecondGoodsFeign secondGoodsFeign;
+
+    /**
+     * 获取AI商品审核结果
+     * 通过Feign调用alien-second服务处理AI审核结果
+     */
+    @XxlJob("getAiGoodsCheckResult")
+    public R<String> getAiGoodsCheckResult() {
+        log.info("开始执行AI商品审核结果获取任务");
+        try {
+            R<String> result = secondGoodsFeign.getAiGoodsCheckResult();
+            log.info("AI商品审核结果获取任务执行完成: {}", result.getData());
+            return result;
+        } catch (Exception e) {
+            log.error("AI商品审核结果获取任务执行异常", e);
+            return R.fail("任务执行异常: " + e.getMessage());
+        }
+    }
+}

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

@@ -0,0 +1,336 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.dto.LawyerRecommendedDto;
+import shop.alien.entity.store.vo.LawyerUserVo;
+import shop.alien.lawyer.service.LawyerUserService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 律师用户 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-律师用户"})
+@ApiSort(10)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/user")
+@RequiredArgsConstructor
+public class LawyerUserController {
+
+    private final LawyerUserService lawyerUserService;
+
+    @ApiOperation("新增律师用户")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addLawyerUser")
+    public R<LawyerUser> addLawyerUser(@RequestBody LawyerUser lawyerUser) {
+
+        log.info("LawyerUserController.addLawyerUser?lawyerUser={}", lawyerUser);
+        return lawyerUserService.addLawyerUser(lawyerUser);
+    }
+
+    @ApiOperation("编辑律师用户")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editLawyerUser")
+    public R<LawyerUser> editLawyerUser(@RequestBody LawyerUser lawyerUser) {
+        log.info("LawyerUserController.editLawyerUser?lawyerUser={}", lawyerUser);
+        return lawyerUserService.editLawyerUser(lawyerUser);
+    }
+
+    @ApiOperation("删除律师用户")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteLawyerUser")
+    public R<Boolean> deleteLawyerUser(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerUserController.deleteLawyerUser?id={}", id);
+        return lawyerUserService.deleteLawyerUser(id);
+    }
+
+    @ApiOperation("保存或更新律师用户")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerUser> saveOrUpdate(@RequestBody LawyerUser lawyerUser) {
+        log.info("LawyerUserController.saveOrUpdate?lawyerUser={}", lawyerUser);
+        boolean result = lawyerUserService.saveOrUpdate(lawyerUser);
+        if (result) {
+            return R.data(lawyerUser);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "用户状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "certificationStatus", value = "资质认证状态, 0:未认证, 1:认证中, 2:已认证, 3:认证失败", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerUser>> getList(@ModelAttribute LawyerUser lawyerUser) {
+        log.info("LawyerUserController.getList?lawyerUser={}", lawyerUser);
+        List<LawyerUser> list = QueryBuilder.of(lawyerUser)
+                .likeFields("name", "phone")  // 姓名和手机号支持模糊查询
+                .build()
+                .list(lawyerUserService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "用户状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "certificationStatus", value = "资质认证状态, 0:未认证, 1:认证中, 2:已认证, 3:认证失败", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerUser>> getPage(@ModelAttribute LawyerUser lawyerUser,
+                                        @RequestParam(defaultValue = "1") int page,
+                                        @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerUserController.getPage?lawyerUser={},page={},size={}", lawyerUser, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerUser> pageResult = QueryBuilder.of(lawyerUser)
+                .likeFields("name", "phone")  // 姓名和手机号支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(lawyerUserService);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("获取律师详情")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<Map<String, Object>> getLawyerDetail(@RequestParam Integer lawyerId) {
+        log.info("LawyerUserController.getLawyerDetail?lawyerId={}", lawyerId);
+        return lawyerUserService.getLawyerDetail(lawyerId);
+    }
+
+    @ApiOperation("获取律师在线状态")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerIds", value = "律师ID数组(逗号分隔)", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/onlineStatus")
+    public R<Map<Integer, Boolean>> getLawyerOnlineStatus(@RequestParam String lawyerIds) {
+        log.info("LawyerUserController.getLawyerOnlineStatus?lawyerIds={}", lawyerIds);
+        return lawyerUserService.getLawyerOnlineStatus(lawyerIds);
+    }
+
+    @ApiOperation("获取推荐律师列表")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "categoryId", value = "分类ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/recommend/list")
+    public R<IPage<LawyerUser>> getRecommendedLawyerList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer categoryId) {
+        log.info("LawyerUserController.getRecommendedLawyerList?page={},pageSize={},categoryId={}",
+                page, pageSize, categoryId);
+        return lawyerUserService.getRecommendedLawyerList(page, pageSize, categoryId);
+    }
+
+    @ApiOperation("根据会话获取推荐律师列表")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sessionId", value = "会话ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "messageId", value = "消息ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/recommend/bySession")
+    public R<IPage<LawyerUser>> getRecommendedLawyersBySession(
+            @RequestParam String sessionId,
+            @RequestParam(required = false) Integer messageId) {
+        log.info("LawyerUserController.getRecommendedLawyersBySession?sessionId={},messageId={}", sessionId, messageId);
+        return lawyerUserService.getRecommendedLawyersBySession(sessionId, messageId);
+    }
+
+    @ApiOperation("通过昵称模糊查询律师(并保存搜索历史)")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "nickName", value = "律师昵称(支持模糊查询,为空时返回所有律师)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID(可选,用于保存搜索历史)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/searchByName")
+    public R<IPage<LawyerUser>> searchLawyerByName(
+            @RequestParam(required = false) String nickName,
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer clientUserId) {
+        log.info("LawyerUserController.searchLawyerByName?nickName={},page={},pageSize={},clientUserId={}", nickName, page, pageSize, clientUserId);
+        return lawyerUserService.searchLawyerByName(nickName, page, pageSize, clientUserId);
+    }
+
+
+    @ApiOperation("根据AI返回场景获取推荐律师列表")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认5)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "categoryId", value = "场景ID(必传)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/aiRecommendList")
+    public R<IPage<LawyerUser>> getAiRecommendList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "5") int size,
+            @RequestParam(required = true) Integer categoryId) {
+        log.info("LawyerUserController.aiRecommendList?page={},pageSize={},categoryId={}",
+                page, size, categoryId);
+        return lawyerUserService.getAiRecommendList(page, size, categoryId);
+    }
+
+
+    @ApiOperation("注销律师用户")
+    @ApiOperationSupport(order = 13)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/logOutLawyerUser")
+    public R<Map<String, Object>> logOutLawyerUser(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerUserController.logOutLawyerUser?id={}", id);
+        return lawyerUserService.logOutLawyerUser(id);
+    }
+
+
+    @ApiOperation("律师信息详情")
+    @ApiOperationSupport(order = 14)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerInfo")
+    public R<LawyerUserVo> getLawyerInfo(@RequestParam Integer lawyerId) {
+        log.info("LawyerUserController.getLawyerInfo?lawyerId={}", lawyerId);
+        return lawyerUserService.getLawyerInfo(lawyerId);
+    }
+
+
+    @ApiOperation("中台律师列表")
+    @ApiOperationSupport(order = 17)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startTime", value = "开始日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "endTime", value = "结束日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "每页数量(默认10)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/getLawyerList")
+    public R<IPage<LawyerUserVo>> getLawyerList(@RequestParam(required = false) String name,
+                                                @RequestParam(required = false) String phone,
+                                                @RequestParam(required = false) Integer firmId,
+                                                @RequestParam(required = false) String startTime,
+                                                @RequestParam(required = false) String endTime,
+                                                @RequestParam int page,
+                                                @RequestParam int size) {
+        log.info("LawyerUserController.getLawyerList?name={},phone={},firmId={},startTime={},endTime={},page={},size={}",
+                name, phone, firmId, startTime, endTime, page, size);
+        return lawyerUserService.getLawyerList(name, phone, firmId, startTime, endTime, page, size);
+    }
+
+    @ApiOperation("导出中台律师列表")
+    @ApiOperationSupport(order = 19)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startTime", value = "开始日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "endTime", value = "结束日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "pageNum", value = "页码(可选,不传或传0则导出全部,传值则导出本页)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(可选,与pageNum配合使用)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/exportLawyerList", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+    public void exportLawyerList(
+            HttpServletResponse response,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String phone,
+            @RequestParam(required = false) Integer firmId,
+            @RequestParam(required = false) String startTime,
+            @RequestParam(required = false) String endTime,
+            @RequestParam(required = false) Integer pageNum,
+            @RequestParam(required = false) Integer pageSize) throws Exception {
+        log.info("LawyerUserController.exportLawyerList?name={},phone={},firmId={},startTime={},endTime={},pageNum={},pageSize={}",
+                name, phone, firmId, startTime, endTime, pageNum, pageSize);
+        try {
+            lawyerUserService.exportLawyerList(response, name, phone, firmId, startTime, endTime, pageNum, pageSize);
+        } catch (Exception e) {
+            log.error("导出律师列表异常", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
+            }
+            throw e;
+        }
+    }
+
+
+
+
+    @ApiOperation("编辑新律师用户")
+    @ApiOperationSupport(order = 15)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "name", value = "律师姓名", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "律师手机号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "address", value = "联系地址", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "introduction", value = "律师简介", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "headImg", value = "律师头像", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "certificationTime", value = "认证时间(从业时间??待确认)", dataType = "Data", paramType = "query"),
+    })
+    @PostMapping("/updateLawyerUser")
+    public R<LawyerUserVo> updateLawyerUser(@RequestBody LawyerUserVo lawyerUserVo) {
+        log.info("LawyerUserController.updateLawyerUser?lawyerUserVo={}", lawyerUserVo);
+        return lawyerUserService.updateLawyerUser(lawyerUserVo);
+    }
+
+    @ApiOperation("通过手机号查询律师信息")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerInfoByPhone")
+    public R<LawyerUser> getLawyerInfoByPhone(@RequestParam String phone) {
+        log.info("LawyerUserController.getLawyerInfoByPhone?phone={}", phone);
+        return lawyerUserService.getLawyerInfoByPhone(phone);
+    }
+
+    @ApiOperation("设置律师推荐状态")
+    @ApiOperationSupport(order = 18)
+    @PostMapping("/isRecommended")
+    public R<Boolean> isRecommended(@RequestBody LawyerRecommendedDto request) {
+        log.info("LawyerUserController.isRecommended?request={}", request);
+        return lawyerUserService.isRecommended(request.getLawyerId(), request.getIsRecommended());
+    }
+
+}
+

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

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

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

@@ -0,0 +1,98 @@
+package shop.alien.second.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);
+
+    boolean performSecondRoundReview(SecondGoods goods, SecondGoodsVo goodsDTO);
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    String getAiGoodsCheckResult();
+
+    /**
+     * 商品审核通过后的上架处理(包含风控检查)
+     * @param goods 商品信息
+     */
+    void handleGoodsApprovalSuccess(SecondGoods goods);
+}
+

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

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

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

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

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

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

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

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

+ 78 - 40
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java

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

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

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

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

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

+ 654 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java

@@ -0,0 +1,654 @@
+package shop.alien.storeplatform.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.excelVo.util.ExcelImage;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.storeplatform.entity.StorePlatformBathFacility;
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
+import shop.alien.storeplatform.feign.AlienStoreFeign;
+import shop.alien.storeplatform.mapper.StorePlatformStoreMenuMapper;
+import shop.alien.storeplatform.service.StorePlatformBarMenuService;
+import shop.alien.storeplatform.util.FileUploadUtil;
+import shop.alien.storeplatform.vo.StorePlatformStoreMenuImportVo;
+import shop.alien.util.ali.AliOSSUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 商户平台-酒吧菜单服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StorePlatformBarMenuServiceImpl extends ServiceImpl<StorePlatformStoreMenuMapper, StorePlatformStoreMenu> implements StorePlatformBarMenuService {
+
+    private final StorePlatformStoreMenuMapper storeMenuMapper;
+
+    private final StoreImgMapper storeImgMapper;
+
+    private final AliOSSUtil aliOSSUtil;
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    @Override
+    public StorePlatformStoreMenu getById(Integer id) {
+        log.info("StorePlatformBarMenuServiceImpl.getById id={}", id);
+        if (id == null) {
+            throw new RuntimeException("菜单ID不能为空");
+        }
+        return super.getById(id);
+    }
+
+    @Override
+    public List<StorePlatformStoreMenu> getListByStoreId(Integer storeId) {
+        log.info("StorePlatformBarMenuServiceImpl.getListByStoreId storeId={}", storeId);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0)
+                .orderByAsc(StorePlatformStoreMenu::getSort);
+        List<StorePlatformStoreMenu> list = this.list(queryWrapper);
+        return list.stream()
+                .sorted(Comparator.comparing(StorePlatformStoreMenu::getSort))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<StorePlatformStoreMenu> getListByStoreIdAndType(Integer storeId, String dishMenuType, String dishType) {
+        log.info("StorePlatformBarMenuServiceImpl.getListByStoreIdAndType storeId={}, dishMenuType={}", storeId, dishMenuType);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
+        if (StringUtils.isNotEmpty(dishMenuType)) {
+            queryWrapper.eq(StorePlatformStoreMenu::getDishMenuType, dishMenuType);
+        }
+        if (StringUtils.isNotEmpty(dishType) && dishType.equals("1")) {
+            queryWrapper.eq(StorePlatformStoreMenu::getDishType, dishType);
+        }
+        queryWrapper.orderByAsc(StorePlatformStoreMenu::getSort);
+        List<StorePlatformStoreMenu> list = this.list(queryWrapper);
+
+        // 查询图片
+        List<Integer> ids = list.stream().map(StorePlatformStoreMenu::getId).collect(Collectors.toList());
+        if (CollectionUtil.isNotEmpty(ids)) {
+            List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                    .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 7).in(StoreImg::getBusinessId, ids));
+
+            if (CollectionUtil.isNotEmpty(imgList)) {
+                list.forEach(menu -> {
+                    imgList.stream()
+                            .filter(img -> img.getBusinessId().equals(menu.getId()))
+                            .findFirst()
+                            .ifPresent(img -> menu.setImgUrl(img.getImgUrl()));
+                });
+            }
+        }
+
+//        // 查询图片
+//        List<Integer> ids = list.stream().map(StorePlatformStoreMenu::getImgId).collect(Collectors.toList());
+//        List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+//                .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 7).in(StoreImg::getId, ids));
+//
+//        if (CollectionUtil.isNotEmpty(imgList)) {
+//            list.forEach(menu -> {
+//                imgList.stream()
+//                        .filter(img -> img.getId().equals(menu.getImgId()))
+//                        .findFirst()
+//                        .ifPresent(img -> menu.setImgUrl(img.getImgUrl()));
+//            });
+//        }
+
+        return list.stream()
+                .sorted(Comparator.comparing(StorePlatformStoreMenu::getSort))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public R<StorePlatformStoreMenu> saveMenu(StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuServiceImpl.saveMenu storeMenu={}", storeMenu);
+        if (storeMenu == null) {
+            return R.fail("菜单信息不能为空");
+        }
+        if (storeMenu.getStoreId() == null) {
+            return R.fail("门店ID不能为空");
+        }
+        if (StringUtils.isEmpty(storeMenu.getDishName())) {
+            return R.fail("菜单名称不能为空");
+        }
+
+        // 设置排序
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeMenu.getStoreId())
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
+        List<StorePlatformStoreMenu> menuList = this.list(queryWrapper);
+        if (CollectionUtil.isNotEmpty(menuList)) {
+            int maxSort = menuList.stream()
+                    .map(StorePlatformStoreMenu::getSort)
+                    .max(Integer::compareTo)
+                    .orElse(0);
+            storeMenu.setSort(maxSort + 1);
+        } else {
+            storeMenu.setSort(1);
+        }
+
+        boolean flag = this.save(storeMenu);
+        if (!flag) {
+            return R.fail("新增菜单失败");
+        }
+        return R.data(storeMenu);
+    }
+
+    @Override
+    public R<StorePlatformStoreMenu> updateMenu(StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuServiceImpl.updateMenu storeMenu={}", storeMenu);
+        if (storeMenu == null || storeMenu.getId() == null) {
+            return R.fail("菜单信息或菜单ID不能为空");
+        }
+        if (StringUtils.isEmpty(storeMenu.getDishName())) {
+            return R.fail("菜单名称不能为空");
+        }
+
+        boolean flag = this.updateById(storeMenu);
+        if (!flag) {
+            return R.fail("修改菜单失败");
+        }
+        return R.data(storeMenu);
+    }
+
+    @Override
+    public R<StorePlatformStoreMenu> saveOrUpdateMenu(StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuServiceImpl.saveOrUpdateMenu storeMenu={}", storeMenu);
+        if (storeMenu == null) {
+            return R.fail("菜单信息不能为空");
+        }
+
+        if (storeMenu.getId() != null) {
+            // 修改
+            return updateMenu(storeMenu);
+        } else {
+            // 新增
+            return saveMenu(storeMenu);
+        }
+    }
+
+    @Override
+    public R<String> deleteMenu(Integer id) {
+        log.info("StorePlatformBarMenuServiceImpl.deleteMenu id={}", id);
+        if (id == null) {
+            return R.fail("菜单ID不能为空");
+        }
+
+        boolean flag = this.removeById(id);
+        if (!flag) {
+            return R.fail("删除菜单失败");
+        }
+        return R.success("删除菜单成功");
+    }
+
+    @Override
+    public R<String> deleteMenuBatch(List<Integer> ids) {
+        log.info("StorePlatformBarMenuServiceImpl.deleteMenuBatch ids={}", ids);
+        if (CollectionUtil.isEmpty(ids)) {
+            return R.fail("菜单ID列表不能为空");
+        }
+
+        boolean flag = this.removeByIds(ids);
+        if (!flag) {
+            return R.fail("批量删除菜单失败");
+        }
+        return R.success("批量删除菜单成功");
+    }
+
+    /**
+     * Excel导入酒吧菜单
+     *
+     * @param file     Excel文件
+     * @param storeId  门店id
+     * @return 导入结果
+     */
+    @Override
+    public R<String> importMenuFromExcel(MultipartFile file, Integer storeId) {
+        log.info("StorePlatformBarMenuServiceImpl.importMenuFromExcel storeId={}", storeId);
+
+        if (file == null || file.isEmpty()) {
+            return R.fail("上传文件为空");
+        }
+
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
+            return R.fail("文件格式不正确,请上传Excel文件");
+        }
+
+        if (storeId == null) {
+            return R.fail("门店ID不能为空");
+        }
+
+        List<String> errorMessages = new ArrayList<>();
+        int successCount = 0;
+        int totalCount = 0;
+
+        try (InputStream inputStream = file.getInputStream();
+             XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 获取表头(假设表头在第5行,索引为5)
+            Row headerRow = sheet.getRow(5);
+            if (headerRow == null) {
+                return R.fail("Excel文件格式不正确,缺少表头");
+            }
+
+            // 构建字段映射(表头名称 -> 列索引)
+            Map<String, Integer> headerMap = new HashMap<>();
+            Field[] fields = StorePlatformStoreMenuImportVo.class.getDeclaredFields();
+            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell != null) {
+                    String headerName = getCellValueAsString(cell);
+                    if (StringUtils.isNotEmpty(headerName)) {
+                        headerMap.put(headerName.trim(), i);
+                    }
+                }
+            }
+
+            // 获取图片映射(行索引 -> 图片字节数组)
+            Map<Integer, byte[]> imageMap = extractImagesFromSheet(sheet);
+
+            // 读取数据行(从第6行开始,索引为6)
+            for (int rowIndex = 6; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
+                Row row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+
+                // 检查是否为空行
+                boolean isEmptyRow = true;
+                for (int i = 0; i < row.getLastCellNum(); i++) {
+                    Cell cell = row.getCell(i);
+                    if (cell != null && cell.getCellType() != CellType.BLANK) {
+                        String cellValue = getCellValueAsString(cell);
+                        if (StringUtils.isNotEmpty(cellValue)) {
+                            isEmptyRow = false;
+                            break;
+                        }
+                    }
+                }
+                if (isEmptyRow) {
+                    continue;
+                }
+
+                totalCount++;
+                StorePlatformStoreMenuImportVo excelVo = new StorePlatformStoreMenuImportVo();
+
+                // 读取每个字段
+                for (Field field : fields) {
+                    field.setAccessible(true);
+                    String fieldName = field.getName();
+                    Integer colIndex = null;
+
+                    // 根据字段名查找对应的表头
+                    for (Map.Entry<String, Integer> entry : headerMap.entrySet()) {
+                        String headerName = entry.getKey();
+                        if (isFieldMatch(fieldName, headerName)) {
+                            colIndex = entry.getValue();
+                            break;
+                        }
+                    }
+
+                    if (colIndex == null) {
+                        continue;
+                    }
+
+                    Cell cell = row.getCell(colIndex);
+                    if (cell == null && !field.isAnnotationPresent(ExcelImage.class)) {
+                        continue;
+                    }
+
+                    try {
+                        if (field.isAnnotationPresent(ExcelImage.class)) {
+                            // 处理图片字段
+                            byte[] imageBytes = imageMap.get(rowIndex);
+                            if (imageBytes != null && imageBytes.length > 0) {
+                                String imageName = "barMenu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+                                MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, imageName);
+                                JSONObject jsonObject = alienStoreFeign.uploadFile(multipartFile);
+                                if (200 == jsonObject.getIntValue("code")) {
+                                    field.set(excelVo, jsonObject.getJSONArray("data").get(0));
+                                } else {
+                                    field.set(excelVo, "");
+                                }
+//                                // 上传图片到OSS
+//                                String imageUrl = uploadImageToOSS(imageBytes, storeId, rowIndex);
+//                                field.set(excelVo, imageUrl);
+                            }
+                        } else {
+                            // 处理普通字段
+                            String cellValue = getCellValueAsString(cell);
+                            if (StringUtils.isNotEmpty(cellValue)) {
+                                setFieldValue(excelVo, field, cellValue.trim());
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("读取字段{}失败:{}", fieldName, e.getMessage());
+                    }
+                }
+
+                // 处理导入数据
+                try {
+                    validateAndSaveMenu(excelVo, storeId, rowIndex + 1);
+                    successCount++;
+                } catch (Exception e) {
+                    errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
+                    log.error("导入第{}行数据失败", rowIndex + 1, e);
+                }
+            }
+        } catch (Exception e) {
+            log.error("导入Excel失败", e);
+            return R.fail("导入失败:" + e.getMessage());
+        }
+
+        // 构建返回消息
+        StringBuilder message = new StringBuilder();
+        message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
+        if (!errorMessages.isEmpty()) {
+            message.append("\n失败详情:\n");
+            for (int i = 0; i < Math.min(errorMessages.size(), 10); i++) {
+                message.append(errorMessages.get(i)).append("\n");
+            }
+            if (errorMessages.size() > 10) {
+                message.append("...还有").append(errorMessages.size() - 10).append("条错误信息");
+            }
+        }
+
+        return R.success(message.toString());
+    }
+
+    /**
+     * 从Sheet中提取图片
+     */
+    private Map<Integer, byte[]> extractImagesFromSheet(Sheet sheet) {
+        Map<Integer, byte[]> imageMap = new HashMap<>();
+        if (sheet instanceof XSSFSheet) {
+            XSSFSheet xssfSheet = (XSSFSheet) sheet;
+            XSSFDrawing drawing = xssfSheet.getDrawingPatriarch();
+            if (drawing != null) {
+                List<XSSFShape> shapes = drawing.getShapes();
+                for (XSSFShape shape : shapes) {
+                    if (shape instanceof XSSFPicture) {
+                        XSSFPicture picture = (XSSFPicture) shape;
+                        XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();
+                        int rowIndex = anchor.getRow1();
+                        try {
+                            // 直接使用 getData() 方法获取图片字节数组
+                            byte[] imageBytes = picture.getPictureData().getData();
+                            imageMap.put(rowIndex, imageBytes);
+                        } catch (Exception e) {
+                            log.warn("提取第{}行图片失败:{}", rowIndex, e.getMessage());
+                        }
+                    }
+                }
+            }
+        }
+        return imageMap;
+    }
+
+    /**
+     * 上传图片到OSS
+     */
+    private String uploadImageToOSS(byte[] imageBytes, Integer storeId, int rowIndex) {
+        try {
+            // 生成文件名
+            String fileName = "bar_menu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+            String prefix = "image/";
+
+            // 创建临时MultipartFile
+            MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, fileName);
+
+            // 上传到OSS
+            return aliOSSUtil.uploadFile(multipartFile, prefix + fileName);
+        } catch (Exception e) {
+            log.error("上传图片失败", e);
+            throw new RuntimeException("上传图片失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 校验并保存菜单
+     */
+    private void validateAndSaveMenu(StorePlatformStoreMenuImportVo excelVo, Integer storeId, int rowNum) {
+        // 校验必填字段
+        if (StringUtils.isEmpty(excelVo.getDishName())) {
+            throw new RuntimeException("名称不能为空");
+        }
+
+        if (excelVo.getDishPrice() == null || excelVo.getDishPrice().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new RuntimeException("价格必须大于0");
+        }
+
+        // 创建StorePlatformStoreMenu对象
+        StorePlatformStoreMenu storeMenu = new StorePlatformStoreMenu();
+        storeMenu.setStoreId(storeId);
+        storeMenu.setDishMenuType(excelVo.getDishMenuType() != null ? excelVo.getDishMenuType() : 0);
+        storeMenu.setDishName(excelVo.getDishName());
+        storeMenu.setDishPrice(excelVo.getDishPrice());
+        storeMenu.setCostPrice(excelVo.getCostPrice());
+        storeMenu.setCategory(excelVo.getCategory());
+        storeMenu.setAlcoholVolume(excelVo.getAlcoholVolume());
+        storeMenu.setFlavor(excelVo.getFlavor());
+        storeMenu.setDescription(excelVo.getDescription());
+        storeMenu.setDishType(excelVo.getDishType() != null ? excelVo.getDishType() : 0);
+
+        // 处理图片
+        if (StringUtils.isNotEmpty(excelVo.getImg())) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeId);
+            storeImg.setImgType(7); // 菜单图片类型
+            storeImg.setImgUrl(excelVo.getImg());
+            storeImg.setImgDescription(excelVo.getDishName());
+            storeImgMapper.insert(storeImg);
+            storeMenu.setImgId(storeImg.getId());
+        }
+
+        // 设置排序
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
+        List<StorePlatformStoreMenu> menuList = this.list(queryWrapper);
+        if (CollectionUtil.isNotEmpty(menuList)) {
+            int maxSort = menuList.stream()
+                    .map(StorePlatformStoreMenu::getSort)
+                    .max(Integer::compareTo)
+                    .orElse(0);
+            storeMenu.setSort(maxSort + 1);
+        } else {
+            storeMenu.setSort(1);
+        }
+
+        // 保存菜单
+        boolean flag = this.save(storeMenu);
+        if (!flag) {
+            throw new RuntimeException("保存菜单失败");
+        }
+    }
+
+    /**
+     * 判断字段名是否匹配表头
+     */
+    private boolean isFieldMatch(String fieldName, String headerName) {
+        // 字段映射表
+        Map<String, String> fieldMapping = new HashMap<>();
+        fieldMapping.put("dishMenuType", "类型(酒水/餐食)");
+        fieldMapping.put("dishName", "名称");
+        fieldMapping.put("dishPrice", "价格(¥)");
+        fieldMapping.put("costPrice", "成本价(¥)");
+        fieldMapping.put("category", "品类");
+        fieldMapping.put("alcoholVolume", "酒精度(%vol)");
+        fieldMapping.put("flavor", "风味");
+        fieldMapping.put("img", "图片");
+        fieldMapping.put("description", "描述");
+        fieldMapping.put("dishType", "是否设为推荐(是/否)");
+
+        String expectedHeader = fieldMapping.get(fieldName);
+        return expectedHeader != null && expectedHeader.equals(headerName);
+    }
+
+    /**
+     * 设置字段值
+     */
+    private void setFieldValue(StorePlatformStoreMenuImportVo excelVo, Field field, String cellValue) throws Exception {
+        Class<?> fieldType = field.getType();
+        String fieldName = field.getName();
+
+        if (fieldType == BigDecimal.class) {
+            try {
+                BigDecimal value = new BigDecimal(cellValue);
+                if (value.scale() > 2) {
+                    throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
+                }
+                field.set(excelVo, value);
+            } catch (NumberFormatException e) {
+                throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
+            }
+        } else if (fieldType == Integer.class) {
+            // 处理 dishType 字段:将"是"/"否"转换为1/0
+            if ("dishType".equals(fieldName)) {
+                String trimmedValue = cellValue.trim();
+                if ("是".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("否".equals(trimmedValue)) {
+                    field.set(excelVo, 0);
+                } else {
+                    throw new RuntimeException("是否推荐字段格式错误,请输入'是'或'否'");
+                }
+            } else if ("dishMenuType".equals(fieldName)) {
+                String trimmedValue = cellValue.trim();
+                if ("餐食".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("酒水".equals(trimmedValue)) {
+                    field.set(excelVo, 2);
+                } else {
+                    throw new RuntimeException("类型字段格式错误,请输入'酒水'或'餐食'");
+                }
+            } else {
+                try {
+                    field.set(excelVo, Integer.parseInt(cellValue));
+                } catch (NumberFormatException e) {
+                    throw new RuntimeException("数字格式错误:" + cellValue);
+                }
+            }
+        } else {
+            // String类型
+            field.set(excelVo, cellValue);
+        }
+    }
+
+    /**
+     * 获取单元格值(字符串格式)
+     */
+    private String getCellValueAsString(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getDateCellValue().toString();
+                } else {
+                    // 处理数字,避免科学计数法
+                    double numericValue = cell.getNumericCellValue();
+                    if (numericValue == (long) numericValue) {
+                        return String.valueOf((long) numericValue);
+                    } else {
+                        return String.valueOf(numericValue);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                return cell.getCellFormula();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 临时MultipartFile实现类,用于上传字节数组
+     */
+    private static class ByteArrayMultipartFile implements MultipartFile {
+        private final byte[] content;
+        private final String fileName;
+
+        public ByteArrayMultipartFile(byte[] content, String fileName) {
+            this.content = content;
+            this.fileName = fileName;
+        }
+
+        @Override
+        public String getName() {
+            return "file";
+        }
+
+        @Override
+        public String getOriginalFilename() {
+            return fileName;
+        }
+
+        @Override
+        public String getContentType() {
+            return "image/jpeg";
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return content == null || content.length == 0;
+        }
+
+        @Override
+        public long getSize() {
+            return content.length;
+        }
+
+        @Override
+        public byte[] getBytes() throws IOException {
+            return content;
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return new ByteArrayInputStream(content);
+        }
+
+        @Override
+        public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
+            java.nio.file.Files.write(dest.toPath(), content);
+        }
+    }
+}

+ 5 - 4
alien-store/src/main/java/shop/alien/store/controller/DrinkInfoController.java

@@ -13,7 +13,8 @@ import shop.alien.store.service.DrinkInfoService;
 import java.util.List;
 
 /**
- * 酒水餐食信息表Controller
+ * 酒水餐食信息 Controller
+ * 基于 store_menu 表实现(dish_menu_type: 1=餐食,2=酒水)
  *
  * @author ssk
  * @since 2025-06-13
@@ -35,7 +36,7 @@ public class DrinkInfoController {
             @ApiImplicitParam(name = "page", value = "页码", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "size", value = "页容", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "type", value = "类型(枚举值:酒水/餐食)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "type", value = "类型(1:餐食,2:酒水)", dataType = "Integer", paramType = "query"),
             @ApiImplicitParam(name = "category", value = "分类", dataType = "String", paramType = "query"),
             @ApiImplicitParam(name = "name", value = "名称关键词", dataType = "String", paramType = "query"),
             @ApiImplicitParam(name = "createUserId", value = "创建人ID(只查询自己创建的)", dataType = "Integer", paramType = "query")
@@ -45,7 +46,7 @@ public class DrinkInfoController {
             @RequestParam("page") int page,
             @RequestParam("size") int size,
             @RequestParam(value = "storeId", required = false) Integer storeId,
-            @RequestParam(value = "type", required = false) String type,
+            @RequestParam(value = "type", required = false) Integer type,
             @RequestParam(value = "category", required = false) String category,
             @RequestParam(value = "name", required = false) String name,
             @RequestParam(value = "createUserId", required = false) Integer createUserId) {
@@ -154,7 +155,7 @@ public class DrinkInfoController {
     @GetMapping("/listByStoreId")
     public R<List<DrinkInfoVo>> getDrinkInfoListByStoreId(
             @RequestParam("storeId") Integer storeId,
-            @RequestParam(value = "type", required = false) String type) {
+            @RequestParam(value = "type", required = false) Integer type) {
         log.info("DrinkInfoController.getDrinkInfoListByStoreId?storeId={}, type={}", storeId, type);
         List<DrinkInfoVo> drinkInfoList = drinkInfoService.getDrinkInfoListByStoreId(storeId, type);
         return R.data(drinkInfoList);

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

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

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

@@ -875,6 +875,8 @@ public class StoreInfoController {
             @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeName", value = "店铺名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "storeType", value = "美食3 休闲娱乐1  健身2 ", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
     })
@@ -886,10 +888,12 @@ public class StoreInfoController {
             @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
             @RequestParam(value = "businessType", required = false) Integer businessType,
             @RequestParam(value = "categoryId", required = false) Integer categoryId,
+            @RequestParam(value = "storeName", required = false) String storeName,
+            @RequestParam(value = "storeType", required = false) Integer storeType,
             @RequestParam(defaultValue = "1") int pageNum,
             @RequestParam(defaultValue = "10") int pageSize) {
-        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
-                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},storeName={},storeType{},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, storeName,storeType, pageNum, pageSize);
 
         try {
             // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
@@ -910,7 +914,7 @@ public class StoreInfoController {
             }
 
             // 调用服务层获取筛选结果
-            IPage<StoreInfoVo> result = storeInfoService.getSpecialTypeStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+            IPage<StoreInfoVo> result = storeInfoService.getSpecialTypeStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, storeName,storeType, pageNum, pageSize);
 
             // 记录响应日志
             log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",
@@ -1071,7 +1075,7 @@ public class StoreInfoController {
      * @param pageSize    页容
      * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
      */
-    @ApiOperation("查询种类型店铺(丽人美发、运动健身)并按距离筛选")
+    @ApiOperation("查询种类型店铺(丽人美发、运动健身)并按距离筛选")
     @ApiOperationSupport(order = 16)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query", required = true),
@@ -1080,6 +1084,7 @@ public class StoreInfoController {
             @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
             @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeName", value = "商家名称(模糊查询)", dataType = "String", paramType = "query"),
             @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
     })
@@ -1091,10 +1096,11 @@ public class StoreInfoController {
             @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
             @RequestParam(value = "businessType", required = false) Integer businessType,
             @RequestParam(value = "categoryId", required = false) Integer categoryId,
+            @RequestParam(value = "storeName", required = false) String storeName,
             @RequestParam(defaultValue = "1") int pageNum,
             @RequestParam(defaultValue = "10") int pageSize) {
-        log.info("StoreInfoController.getLifeServicesByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
-                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+        log.info("StoreInfoController.getLifeServicesByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},storeName={},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, storeName, pageNum, pageSize);
 
         try {
             // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
@@ -1115,7 +1121,7 @@ public class StoreInfoController {
             }
 
             // 调用服务层获取筛选结果
-            IPage<StoreInfoVo> result = storeInfoService.getLifeServicesByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+            IPage<StoreInfoVo> result = storeInfoService.getLifeServicesByDistance(lon, lat, distance, sortType, businessType, categoryId, storeName, pageNum, pageSize);
 
             // 记录响应日志
             log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",

+ 6 - 31
alien-store/src/main/java/shop/alien/store/service/DrinkInfoService.java

@@ -1,7 +1,6 @@
 package shop.alien.store.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.store.DrinkInfo;
 import shop.alien.entity.store.vo.DrinkInfoVo;
 
@@ -9,11 +8,12 @@ import java.util.List;
 
 /**
  * 酒水餐食信息表 服务类
+ * 注意:drink_info 表已废弃,现在基于 store_menu 表实现
  *
  * @author ssk
  * @since 2025-06-13
  */
-public interface DrinkInfoService extends IService<DrinkInfo> {
+public interface DrinkInfoService {
 
     /**
      * 分页查询酒水餐食列表
@@ -21,13 +21,13 @@ public interface DrinkInfoService extends IService<DrinkInfo> {
      * @param page         页码
      * @param size         页容
      * @param storeId      门店ID
-     * @param type         类型(枚举值:酒水/餐食
+     * @param type         类型(1:菜品,2:酒水
      * @param category     分类
      * @param name         名称关键词
      * @param createUserId 创建人ID(只查询指定用户创建的)
      * @return IPage<DrinkInfoVo>
      */
-    IPage<DrinkInfoVo> getDrinkInfoPage(int page, int size, Integer storeId, String type, String category, String name, Integer createUserId);
+    IPage<DrinkInfoVo> getDrinkInfoPage(int page, int size, Integer storeId, Integer type, String category, String name, Integer createUserId);
 
     /**
      * 根据ID查询酒水餐食详情
@@ -92,10 +92,10 @@ public interface DrinkInfoService extends IService<DrinkInfo> {
      * 根据门店ID查询所有酒水餐食
      *
      * @param storeId 门店ID
-     * @param type    类型(枚举值:酒水/餐食
+     * @param type    类型(1:菜品,2:酒水
      * @return List<DrinkInfoVo>
      */
-    List<DrinkInfoVo> getDrinkInfoListByStoreId(Integer storeId, String type);
+    List<DrinkInfoVo> getDrinkInfoListByStoreId(Integer storeId, Integer type);
 
     /**
      * 查询推荐酒水餐食列表
@@ -113,29 +113,4 @@ public interface DrinkInfoService extends IService<DrinkInfo> {
      * @return boolean
      */
     boolean batchUpdateSort(List<DrinkInfo> drinkInfoList);
-
-    /**
-     * 获取酒水餐食类型统计
-     *
-     * @param storeId 门店ID
-     * @return Map<String, Long> 类型统计信息
-     */
-    java.util.Map<String, Long> getStatistics(Integer storeId);
-
-    /**
-     * 批量删除酒水餐食
-     *
-     * @param ids 主键ID列表
-     * @return boolean
-     */
-    boolean batchDelete(List<Integer> ids);
-
-    /**
-     * 批量上下架
-     *
-     * @param ids    主键ID列表
-     * @param status 状态(0:下架, 1:上架)
-     * @return boolean
-     */
-    boolean batchUpdateStatus(List<Integer> ids, Integer status);
 }

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

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

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

@@ -365,11 +365,12 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
      * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选
      * @param categoryId  分类ID(二级或三级分类的dictId,从getAllBusinessSection接口获取),可选
+     * @param storeName   店铺名称(模糊查询),可选
      * @param pageNum     页码
      * @param pageSize    页容
      * @return IPage<StoreInfoVo> 分页的门店信息列表
      */
-    IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
+    IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName,Integer storeType, int pageNum, int pageSize);
 
     /**
      * web端查询门店明细
@@ -438,9 +439,11 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @param distance    距离范围(单位:公里)
      * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
      * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7),可选,如果指定则只查询该类型
+     * @param categoryId  字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺
+     * @param storeName   商家名称(模糊查询)
      * @param pageNum     页码
      * @param pageSize    页容
      * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
      */
-    IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
+    IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName, int pageNum, int pageSize);
 }

+ 248 - 141
alien-store/src/main/java/shop/alien/store/service/impl/DrinkInfoServiceImpl.java

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

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

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

+ 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;
     }
 

+ 30 - 0
alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java

@@ -303,5 +303,35 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
 
         return result;
     }
+
+    @Override
+    public boolean saveCategoryImages(Integer storeId, Integer facilityCategory, List<String> imageList) {
+        // 参数校验
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        if (facilityCategory == null) {
+            throw new RuntimeException("设施分类不能为空");
+        }
+
+        // 校验图片数量(最多20张)
+        if (!CollectionUtils.isEmpty(imageList) && imageList.size() > 20) {
+            throw new RuntimeException("实景图片最多上传20张");
+        }
+
+        // 1. 删除该分类下的旧图片
+        LambdaQueryWrapper<StoreImg> deleteImageWrapper = new LambdaQueryWrapper<>();
+        deleteImageWrapper.eq(StoreImg::getStoreId, storeId)
+                .eq(StoreImg::getBusinessId, facilityCategory)
+                .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+        storeImgMapper.delete(deleteImageWrapper);
+
+        // 2. 保存新图片(如果有)
+        if (!CollectionUtils.isEmpty(imageList)) {
+            saveImages(facilityCategory, storeId, imageList);
+        }
+
+        return true;
+    }
 }
 

+ 79 - 10
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -886,22 +886,38 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                     Date expirationTime = parseFoodLicenceExpirationDate(ocrResult);
                     if (expirationTime != null) {
                         storeInfo.setFoodLicenceExpirationTime(expirationTime);
+                    } else if (storeInfoDto.getFoodLicenceExpirationTime() != null) {
+                        // OCR解析结果为空时,使用DTO中传入的值
+                        storeInfo.setFoodLicenceExpirationTime(storeInfoDto.getFoodLicenceExpirationTime());
+                        log.info("使用DTO中的食品经营许可证到期时间:{}", storeInfoDto.getFoodLicenceExpirationTime());
                     }
                     // 设置食品经营许可证状态为"待审核"(字典值2)
                     storeInfo.setFoodLicenceStatus(2);
                     storeInfo.setUpdateFoodLicenceTime(new Date());
-                    log.info("食品经营许可证OCR数据解析成功,到期时间:{}", expirationTime);
+                    log.info("食品经营许可证OCR数据解析成功,到期时间:{}", storeInfo.getFoodLicenceExpirationTime());
                 } catch (Exception e) {
                     log.error("解析食品经营许可证OCR数据失败", e);
-                    // 解析失败时仍设置状态为待审核
+                    // 解析失败时使用DTO中传入的值
+                    if (storeInfoDto.getFoodLicenceExpirationTime() != null) {
+                        storeInfo.setFoodLicenceExpirationTime(storeInfoDto.getFoodLicenceExpirationTime());
+                        log.info("OCR解析失败,使用DTO中的食品经营许可证到期时间:{}", storeInfoDto.getFoodLicenceExpirationTime());
+                    }
                     storeInfo.setFoodLicenceStatus(2);
                     storeInfo.setUpdateFoodLicenceTime(new Date());
                 }
             } else {
-                // 没有OCR记录时,设置状态为待审核
+                // 没有OCR记录时,使用DTO中传入的值
+                if (storeInfoDto.getFoodLicenceExpirationTime() != null) {
+                    storeInfo.setFoodLicenceExpirationTime(storeInfoDto.getFoodLicenceExpirationTime());
+                    log.info("无OCR记录,使用DTO中的食品经营许可证到期时间:{}", storeInfoDto.getFoodLicenceExpirationTime());
+                }
                 storeInfo.setFoodLicenceStatus(2);
                 storeInfo.setUpdateFoodLicenceTime(new Date());
             }
+        } else if (storeInfoDto.getFoodLicenceExpirationTime() != null) {
+            // 没有食品经营许可证URL,但有传入到期时间时直接使用
+            storeInfo.setFoodLicenceExpirationTime(storeInfoDto.getFoodLicenceExpirationTime());
+            log.info("无食品经营许可证URL,使用DTO中的到期时间:{}", storeInfoDto.getFoodLicenceExpirationTime());
         }
 
         // 处理娱乐经营许可证OCR数据(复用营业执照OCR类型,数据库中ocr_type存的是BUSINESS_LICENSE)
@@ -921,22 +937,57 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                     Date expirationTime = parseEntertainmentLicenceExpirationDate(ocrResult);
                     if (expirationTime != null) {
                         storeInfo.setEntertainmentLicenceExpirationTime(expirationTime);
+                    } else if (storeInfoDto.getEntertainmentLicenceExpirationTime() != null) {
+                        // OCR解析结果为空时,使用DTO中传入的值
+                        storeInfo.setEntertainmentLicenceExpirationTime(storeInfoDto.getEntertainmentLicenceExpirationTime());
+                        log.info("使用DTO中的娱乐经营许可证到期时间:{}", storeInfoDto.getEntertainmentLicenceExpirationTime());
                     }
                     // 设置娱乐经营许可证状态为"待审核"(字典值2)
                     storeInfo.setEntertainmentLicenceStatus(2);
                     storeInfo.setUpdateEntertainmentLicenceTime(new Date());
-                    log.info("娱乐经营许可证OCR数据解析成功,到期时间:{}", expirationTime);
+                    log.info("娱乐经营许可证OCR数据解析成功,到期时间:{}", storeInfo.getEntertainmentLicenceExpirationTime());
                 } catch (Exception e) {
                     log.error("解析娱乐经营许可证OCR数据失败", e);
-                    // 解析失败时仍设置状态为待审核
+                    // 解析失败时使用DTO中传入的值
+                    if (storeInfoDto.getEntertainmentLicenceExpirationTime() != null) {
+                        storeInfo.setEntertainmentLicenceExpirationTime(storeInfoDto.getEntertainmentLicenceExpirationTime());
+                        log.info("OCR解析失败,使用DTO中的娱乐经营许可证到期时间:{}", storeInfoDto.getEntertainmentLicenceExpirationTime());
+                    }
                     storeInfo.setEntertainmentLicenceStatus(2);
                     storeInfo.setUpdateEntertainmentLicenceTime(new Date());
                 }
             } else {
-                // 没有OCR记录时,设置状态为待审核
+                // 没有OCR记录时,使用DTO中传入的值
+                if (storeInfoDto.getEntertainmentLicenceExpirationTime() != null) {
+                    storeInfo.setEntertainmentLicenceExpirationTime(storeInfoDto.getEntertainmentLicenceExpirationTime());
+                    log.info("无OCR记录,使用DTO中的娱乐经营许可证到期时间:{}", storeInfoDto.getEntertainmentLicenceExpirationTime());
+                }
                 storeInfo.setEntertainmentLicenceStatus(2);
                 storeInfo.setUpdateEntertainmentLicenceTime(new Date());
             }
+        } else if (storeInfoDto.getEntertainmentLicenceExpirationTime() != null) {
+            // 没有娱乐经营许可证URL,但有传入到期时间时直接使用
+            storeInfo.setEntertainmentLicenceExpirationTime(storeInfoDto.getEntertainmentLicenceExpirationTime());
+            log.info("无娱乐经营许可证URL,使用DTO中的到期时间:{}", storeInfoDto.getEntertainmentLicenceExpirationTime());
+        }
+
+        // 计算并设置到期时间为三个过期时间的最小值
+        List<Date> expirationTimeList = new ArrayList<>();
+        // 收集所有非空的过期时间
+        if (storeInfoDto.getExpirationTime() != null) {
+            expirationTimeList.add(storeInfoDto.getExpirationTime());
+        }
+        if (storeInfo.getFoodLicenceExpirationTime() != null) {
+            expirationTimeList.add(storeInfo.getFoodLicenceExpirationTime());
+        }
+        if (storeInfo.getEntertainmentLicenceExpirationTime() != null) {
+            expirationTimeList.add(storeInfo.getEntertainmentLicenceExpirationTime());
+        }
+        // 取最小值设置为门店到期时间
+        if (!expirationTimeList.isEmpty()) {
+            Date minExpirationTime = Collections.min(expirationTimeList);
+            storeInfo.setExpirationTime(minExpirationTime);
+            log.info("设置门店到期时间为三个过期时间的最小值:{}", minExpirationTime);
         }
 
         storeInfoMapper.insert(storeInfo);
@@ -2958,7 +3009,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
 
     @Override
-    public IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize) {
+    public IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName, int pageNum, int pageSize) {
         // 参数校验
         if (lon == null || lat == null) {
             throw new IllegalArgumentException("经纬度参数不能为空");
@@ -3095,7 +3146,11 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         // 过滤永久关门的店铺
         queryWrapper.ne("a.business_status", 99);
         // 过滤过期的经营许可证
-//        queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
+        queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
+        // 添加商家名称模糊查询
+        if (StringUtils.isNotEmpty(storeName)) {
+            queryWrapper.like("a.store_name", storeName);
+        }
 
         // 距离优先模式:只显示10公里内且3.5星以上的店铺
         final Double finalDistance;
@@ -3744,7 +3799,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
 
     @Override
-    public IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize) {
+    public IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName,Integer storeType, int pageNum, int pageSize) {
         // 参数校验
         if (lon == null || lat == null) {
             throw new IllegalArgumentException("经纬度参数不能为空");
@@ -3848,6 +3903,16 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             // 如果没有指定businessType,则查询所有四种类型的店铺
             // 需要查询字典表获取所有四种类型的dictId
             List<String> storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
+//            List<String> storeTypeNames=new ArrayList<>();
+//            if (storeType !=null &&   storeType ==1 ){
+//                 storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
+//            } else if (storeType !=null &&   storeType==2) {
+//                 storeTypeNames = Arrays.asList("丽人美发", "运动健身");
+//            }else if ( storeType !=null && storeType==3) {
+//                 storeTypeNames = Arrays.asList("特色美食");
+//            }
+
+
             List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
                     new LambdaQueryWrapper<StoreDictionary>()
                             .eq(StoreDictionary::getTypeName, "business_section")
@@ -3881,7 +3946,11 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         // 过滤永久关门的店铺
         queryWrapper.ne("a.business_status", 99);
         // 过滤过期的经营许可证
-//        queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
+        queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
+        // 添加一个模糊查询,根据店铺名称进行查询
+        if (StringUtils.isNotEmpty(storeName)) {
+            queryWrapper.like("a.store_name", storeName);
+        }
 
         // 距离优先模式:只显示10公里内且3.5星以上的店铺
         final Double finalDistance;

+ 18 - 1
alien-store/src/main/java/shop/alien/store/util/FileUploadUtil.java

@@ -7,7 +7,6 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartRequest;
-import shop.alien.entity.result.R;
 import shop.alien.entity.store.vo.StoreImgVo;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.util.ali.AliOSSUtil;
@@ -40,6 +39,9 @@ public class FileUploadUtil {
 
     List<String> imageFileType = Arrays.asList("jpg", "jpeg", "png", "bmp", "webp", "gif", "svg");
     List<String> videoFileType = Arrays.asList("mp4", "avi", "flv", "mkv", "rmvb", "wmv", "3gp", "mov");
+    List<String> voiceFileType = Arrays.asList("wav");
+    List<String> privacyFileType = Arrays.asList("htm","html");
+    List<String> ohterFileType = Arrays.asList("xls","xlsx");
     List<String> appFileType = Arrays.asList("apk", "ipk", "wgt");
 
     /**
@@ -132,6 +134,21 @@ public class FileUploadUtil {
                     } else {
                         throw new RuntimeException("视频截图失败");
                     }
+                } else if (voiceFileType.contains(fileNameAndType.get("type").toLowerCase())) {
+                    uploadDir += "/voice";
+                    prefix = "voice/";
+                    log.info("FileUpload.uploadMoreFile 获取到语音文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
+                } else if (privacyFileType.contains(fileNameAndType.get("type").toLowerCase())) {
+                    uploadDir += "/privacy/";
+                    prefix = "privacy/";
+                    log.info("FileUpload.uploadMoreFile 获取到隐私文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
+                } else if (ohterFileType.contains(fileNameAndType.get("type").toLowerCase())) {
+                    uploadDir += "/other/";
+                    prefix = "other/";
+                    log.info("FileUpload.uploadMoreFile 获取到其他文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
+                    filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + fileNameAndType.get("name") + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
                 }
             }
             return filePathList;