ソースを参照

Merge remote-tracking branch 'origin/sit' into uat-20260202

dujian 2 ヶ月 前
コミット
08d19b72ff
16 ファイル変更595 行追加233 行削除
  1. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreMainInfoVo.java
  2. 29 60
      alien-entity/src/main/resources/mapper/StoreInfoMapper.xml
  3. 4 1
      alien-store-platform/src/main/java/shop/alien/storeplatform/util/AiContentModerationUtil.java
  4. 2 2
      alien-store/src/main/java/shop/alien/store/controller/AiAuditController.java
  5. 2 0
      alien-store/src/main/java/shop/alien/store/controller/AiSearchController.java
  6. 28 1
      alien-store/src/main/java/shop/alien/store/controller/AiUserSessionController.java
  7. 3 31
      alien-store/src/main/java/shop/alien/store/controller/AliController.java
  8. 113 6
      alien-store/src/main/java/shop/alien/store/controller/StoreCuisineController.java
  9. 107 16
      alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java
  10. 13 5
      alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java
  11. 3 22
      alien-store/src/main/java/shop/alien/store/service/impl/LicenseAuditAsyncService.java
  12. 2 41
      alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java
  13. 30 26
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  14. 51 5
      alien-store/src/main/java/shop/alien/store/service/impl/StorePlatformMenuServiceImpl.java
  15. 188 7
      alien-store/src/main/java/shop/alien/store/util/ai/AiReportReviewUtil.java
  16. 17 10
      alien-store/src/main/java/shop/alien/store/util/ai/AiUserSessionUtil.java

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

@@ -57,6 +57,9 @@ public class StoreMainInfoVo extends StoreInfo {
     @ApiModelProperty(value = "门店头像")
     String headImgUrl;
 
+    @ApiModelProperty(value = "商家头像")
+    String storeUserHeadImg;
+
     @ApiModelProperty(value = "门店地址")
     String storeAddress;
 

+ 29 - 60
alien-entity/src/main/resources/mapper/StoreInfoMapper.xml

@@ -5,68 +5,43 @@
 <mapper namespace="shop.alien.mapper.StoreInfoMapper">
 
     <!--
-        门店证照查询
-        证照类型与状态、提交时间、到期时间映射关系:
-          - img_type = 14: 营业执照
-          - img_type = 35: 其他资质证明
+        门店证照查询(存档审计)
+        以 store_license_history 为驱动表,展示所有上传记录
+        证照类型映射:license_status 1→营业执照(img_type=14)  2→其他资质证明(img_type=35)
+        审核状态:license_execute_status 1→审核通过  2→审核中  3→审核拒绝
     -->
     <select id="getStoreLicensePage"
             resultType="shop.alien.entity.store.vo.StoreLicenseInfoVo">
         SELECT
             CASE
-                WHEN si.img_type = 14 THEN '营业执照'
-                WHEN si.img_type = 35 THEN '其他资质证明'
+                WHEN slh.license_status = 1 THEN '营业执照'
+                WHEN slh.license_status = 2 THEN '其他资质证明'
                 ELSE ''
             END AS img_description,
-            si.img_type AS img_type,
+            CASE
+                WHEN slh.license_status = 1 THEN 14
+                WHEN slh.license_status = 2 THEN 35
+                ELSE 0
+            END AS img_type,
             s.id AS id,
             s.store_name AS store_name,
             s.store_tel AS store_tel,
             su.name AS name,
-            si.img_url,
+            slh.img_url,
             s.business_section_name,
             s.business_classify_name,
             s.business_types_name,
+            slh.license_execute_status AS states,
+            slh.created_time AS submit_date,
             CASE
-                WHEN si.img_type = 14 THEN s.business_license_status
-                WHEN si.img_type = 35 THEN slh.license_execute_status
-                ELSE ''
-            END AS states,
-            CASE
-                WHEN si.img_type = 14 THEN s.update_business_license_time
-                WHEN si.img_type = 35 THEN slh.created_time
-                ELSE NULL
-            END AS submit_date,
-            CASE
-                WHEN si.img_type = 14 THEN s.business_license_expiration_time
-                WHEN si.img_type = 35 THEN NULL
+                WHEN slh.license_status = 1 THEN s.business_license_expiration_time
                 ELSE NULL
             END AS expiration_time,
-            CASE
-                WHEN si.img_type = 14 THEN s.business_license_reason
-                WHEN si.img_type = 35 THEN slh.reason_refusal
-                ELSE NULL
-            END AS expiration_reason
-        FROM store_info s
-                 LEFT JOIN store_img si
-                           ON s.id = si.store_id
-                               AND si.img_type IN (14, 35)
-                               AND si.delete_flag = 0
-                 LEFT JOIN store_user su ON s.id = su.store_id
-                 LEFT JOIN (
-                     SELECT slh.store_id, slh.license_execute_status, slh.created_time, slh.reason_refusal, slh.img_url
-                     FROM store_license_history slh
-                     INNER JOIN (
-                         SELECT store_id, MAX(created_time) AS max_time
-                         FROM store_license_history
-                         WHERE license_status = 2 AND delete_flag = 0
-                         GROUP BY store_id
-                     ) slh2 ON slh.store_id = slh2.store_id 
-                             AND slh.created_time = slh2.max_time
-                     WHERE slh.license_status = 2 AND slh.delete_flag = 0
-                 ) slh ON s.id = slh.store_id AND si.img_type = 35
-        WHERE s.delete_flag = 0
-            AND si.img_type IS NOT NULL
+            slh.reason_refusal AS expiration_reason
+        FROM store_license_history slh
+                 INNER JOIN store_info s ON slh.store_id = s.id AND s.delete_flag = 0
+                 LEFT JOIN store_user su ON s.id = su.store_id AND su.delete_flag = 0
+        WHERE slh.delete_flag = 0
         <if test="storeName != null and storeName != ''">
             AND s.store_name LIKE CONCAT('%', #{storeName}, '%')
         </if>
@@ -79,28 +54,22 @@
         <if test="businessSection != null and businessSection != ''">
             AND s.business_section = #{businessSection}
         </if>
-        <if test="imgType != null">
-            AND si.img_type = #{imgType}
+        <if test="imgType != null and imgType == 14">
+            AND slh.license_status = 1
+        </if>
+        <if test="imgType != null and imgType == 35">
+            AND slh.license_status = 2
         </if>
         <if test="states != null and states != ''">
-            AND (
-                (si.img_type = 14 AND s.business_license_status = #{states})
-                OR (si.img_type = 35 AND slh.license_execute_status = #{states})
-            )
+            AND slh.license_execute_status = #{states}
         </if>
         <if test="startSubmitDate != null and startSubmitDate != ''">
-            AND (
-                (si.img_type = 14 AND s.update_business_license_time &gt;= #{startSubmitDate})
-                OR (si.img_type = 35 AND slh.created_time &gt;= #{startSubmitDate})
-            )
+            AND slh.created_time &gt;= #{startSubmitDate}
         </if>
         <if test="endSubmitDate != null and endSubmitDate != ''">
-            AND (
-                (si.img_type = 14 AND s.update_business_license_time &lt;= #{endSubmitDate})
-                OR (si.img_type = 35 AND slh.created_time &lt;= #{endSubmitDate})
-            )
+            AND slh.created_time &lt;= #{endSubmitDate}
         </if>
-        ORDER BY submit_date DESC, s.store_name ASC
+        ORDER BY slh.created_time DESC, s.store_name ASC
     </select>
 
 </mapper>

+ 4 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/util/AiContentModerationUtil.java

@@ -13,7 +13,9 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 
 import java.util.ArrayList;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 通用图文审核工具类
@@ -167,7 +169,8 @@ public class AiContentModerationUtil {
             }
 
             // 检查是否有任何项目被标记为违规
-            List<String> violationReasons = new ArrayList<>();
+            Set<String> violationReasons = new LinkedHashSet<>();
+//            List<String> violationReasons = new ArrayList<>();
             boolean hasViolations = false;
 
             for (int i = 0; i < results.size(); i++) {

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

@@ -118,8 +118,8 @@ public class AiAuditController {
         requestBody.put("text", text);
         HttpHeaders aiHeaders = new HttpHeaders();
         aiHeaders.setContentType(MediaType.APPLICATION_JSON);
-//        aiHeaders.set("Authorization", "Bearer " + accessToken);
-        aiHeaders.set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1cHN0b3JlQGFkbWluLmNvbSIsImlkIjo2LCJ0aW1lIjoxNzYyOTI1NDAzLjY1MTY5MjZ9.07lz8Ox2cGC28UCmqcKCt5R6Rfwtgs-Eiu0ttgWRxws");
+        aiHeaders.set("Authorization", "Bearer " + accessToken);
+//        aiHeaders.set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1cHN0b3JlQGFkbWluLmNvbSIsImlkIjo2LCJ0aW1lIjoxNzYyOTI1NDAzLjY1MTY5MjZ9.07lz8Ox2cGC28UCmqcKCt5R6Rfwtgs-Eiu0ttgWRxws");
 
         HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
 

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

@@ -88,6 +88,7 @@ public class AiSearchController {
 
             ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(aiSearchExactUrl, request, String.class);
             String body = stringResponseEntity.getBody();
+            log.info("调用AI检索 处理前v 接口返回------{}", body);
             JSONObject jsonObject = JSONObject.parseObject(body);
             JSONObject jsonObject1 = new JSONObject();
             // 生活服务类别:转换为StoreInfoVo,确保返回的字段名按照StoreInfoVo定义
@@ -136,6 +137,7 @@ public class AiSearchController {
         try {
             ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(aiSearchFuzzyUrl, request, String.class);
             String body = stringResponseEntity.getBody();
+            log.info("调用AI列表接口 处理前v 接口返回------{}", body);
             JSONObject jsonObject = JSONObject.parseObject(body);
             JSONObject jsonObject1 = new JSONObject();
             // 模糊搜索:从related_results和matched_results字段获取数据

+ 28 - 1
alien-store/src/main/java/shop/alien/store/controller/AiUserSessionController.java

@@ -35,6 +35,10 @@ public class AiUserSessionController {
             if (request == null || !request.containsKey("user_id")) {
                 return R.fail("用户ID不能为空");
             }
+            //type: 0是ai客服,1是运营客服
+            if (!request.containsKey("type")) {
+                return R.fail("type不能为空");
+            }
 
             Object userIdObj = request.get("user_id");
             if (userIdObj == null) {
@@ -58,7 +62,30 @@ public class AiUserSessionController {
                 return R.fail("用户ID必须大于0");
             }
 
-            JSONObject result = aiUserSessionUtil.getUserSessions(userId);
+            Object typeObj = request.get("type");
+            if (typeObj == null) {
+                return R.fail("type不能为空");
+            }
+
+            Integer type;
+            if (typeObj instanceof Integer) {
+                type = (Integer) typeObj;
+            } else if (typeObj instanceof Number) {
+                type = ((Number) typeObj).intValue();
+            } else {
+                try {
+                    type = Integer.parseInt(typeObj.toString());
+                } catch (NumberFormatException e) {
+                    return R.fail("type格式错误");
+                }
+            }
+
+            // type: 0是ai客服,1是运营客服
+            if (type != 0 && type != 1) {
+                return R.fail("type必须为0或1,0是ai客服,1是运营客服");
+            }
+
+            JSONObject result = aiUserSessionUtil.getUserSessions(userId, type);
             if (result != null) {
                 Boolean success = result.getBoolean("success");
                 if (Boolean.TRUE.equals(success)) {

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

@@ -10,12 +10,8 @@ import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.StoreAliPayLog;
-import shop.alien.entity.store.StoreInfo;
-import shop.alien.entity.store.StoreUser;
 import shop.alien.store.service.AliService;
 import shop.alien.store.service.LifeUserService;
-import shop.alien.store.service.StoreInfoService;
-import shop.alien.store.service.StoreUserService;
 import shop.alien.store.util.ali.AliApi;
 import shop.alien.store.util.ali.AliSms;
 import shop.alien.util.ali.AliOSSUtil;
@@ -26,10 +22,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.text.SimpleDateFormat;
 import java.util.Date;
-import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
 
 /**
  * @author ssk
@@ -57,12 +50,8 @@ public class AliController {
 
     private final AliApi aliApi;
 
-    private final StoreUserService storeUserService;
-
     private final LifeUserService lifeUserService;
 
-    private final StoreInfoService storeInfoService;
-
     @ApiOperation("阿里回调")
     @ApiOperationSupport(order = 1)
     @GetMapping("/notify")
@@ -83,32 +72,15 @@ public class AliController {
         String normalizedName = name == null ? null : name.trim();
         String normalizedIdCard = idCard == null ? null : idCard.trim().toUpperCase();
         log.info("AliController.getIdInfo?name={}&idCard={}", normalizedName, normalizedIdCard);
-        int size = 0;
         if (appType == 0) {
             // 根据身份证查询未注销用户:同一身份证只能实名一个账号
-            size = lifeUserService.count(new LambdaQueryWrapper<LifeUser>()
+            int size = lifeUserService.count(new LambdaQueryWrapper<LifeUser>()
                     .eq(LifeUser::getIdCard, normalizedIdCard)
                     .eq(LifeUser::getLogoutFlag, 0));
-        } else {
-            // 根据身份证查询已入驻或审核中的商家:同一身份证只能实名一个账号
-            List<StoreUser> storeUserList = storeUserService
-                    .list(new LambdaQueryWrapper<StoreUser>()
-                            .eq(StoreUser::getIdCard, normalizedIdCard));
-            List<Integer> storeIds = storeUserList.stream()
-                    .map(StoreUser::getStoreId)
-                    .filter(Objects::nonNull)
-                    .collect(Collectors.toList());
-            if (!storeIds.isEmpty()) {
-                size = storeInfoService
-                        .list(new LambdaQueryWrapper<StoreInfo>()
-                                .in(StoreInfo::getId, storeIds)
-                                .notIn(StoreInfo::getStoreApplicationStatus, 2))
-                        .size();
+            if (size > 0) {
+                return R.fail("该身份证已实名认证过");
             }
         }
-        if (size > 0) {
-            return R.fail("该身份证已实名认证过");
-        }
         if (aliPayConfig.getIdInfo(normalizedName, normalizedIdCard)) {
             return R.success("身份验证成功");
         }

+ 113 - 6
alien-store/src/main/java/shop/alien/store/controller/StoreCuisineController.java

@@ -155,14 +155,13 @@ public class StoreCuisineController {
             // 执行AI审核
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
                 AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
-                boolean allPassed = (auditResult != null);
                 
                 LambdaUpdateWrapper<StoreCuisine> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 auditUpdateWrapper.eq(StoreCuisine::getId, savedCuisine.getId());
                 auditUpdateWrapper.set(StoreCuisine::getRejectionReason, null);
                 auditUpdateWrapper.set(StoreCuisine::getAuditTime, new Date());
                 
-                if (allPassed) {
+                if (auditResult.isPassed()) {
                     // 审核通过 审核状态为1 上架状态为1 已上架
                     auditUpdateWrapper.set(StoreCuisine::getStatus, 1);
                     auditUpdateWrapper.set(StoreCuisine::getShelfStatus, 1);
@@ -198,10 +197,118 @@ public class StoreCuisineController {
     @PostMapping("/updateCuisineCombo")
     public R<String> updateCuisineCombo(@RequestBody CuisineComboDto cuisineComboDto) {
         log.info("StoreCuisineController.updateCuisineCombo?dto={}", cuisineComboDto);
-        if (storeCuisineService.updateCuisineCombo(cuisineComboDto)) {
+        
+        // 参数验证
+        if (cuisineComboDto == null) {
+            log.error("修改美食套餐或单品失败:参数不能为空");
+            return R.fail("参数不能为空");
+        }
+        
+        // 校验ID不能为空
+        if (cuisineComboDto.getId() == null) {
+            log.error("修改美食套餐或单品失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+        
+        try {
+            // 更新数据
+            boolean updateResult = storeCuisineService.updateCuisineCombo(cuisineComboDto);
+            if (!updateResult) {
+                log.error("修改美食套餐或单品失败:更新操作返回false");
+                return R.fail("修改失败");
+            }
+            
+            // 查询更新后的记录
+            StoreCuisine updatedCuisine = storeCuisineService.getById(cuisineComboDto.getId());
+            if (updatedCuisine == null) {
+                log.error("修改美食套餐或单品失败:更新后查询不到数据,ID={}", cuisineComboDto.getId());
+                return R.fail("修改失败:数据更新异常");
+            }
+            
+            // 将状态置为"审核中"(0)
+            LambdaUpdateWrapper<StoreCuisine> auditingWrapper = new LambdaUpdateWrapper<>();
+            auditingWrapper.eq(StoreCuisine::getId, updatedCuisine.getId());
+            auditingWrapper.set(StoreCuisine::getStatus, 0);
+            auditingWrapper.set(StoreCuisine::getRejectionReason, null);
+            auditingWrapper.set(StoreCuisine::getAuditTime, new Date());
+            storeCuisineMapper.update(null, auditingWrapper);
+            
+            // 组装 AI 审核文本和图片
+            StringBuilder textContent = new StringBuilder();
+            if (StringUtils.isNotEmpty(cuisineComboDto.getName())) {
+                textContent.append(cuisineComboDto.getName()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(cuisineComboDto.getDetailContent())) {
+                textContent.append(cuisineComboDto.getDetailContent()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(cuisineComboDto.getDescription())) {
+                textContent.append(cuisineComboDto.getDescription()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(cuisineComboDto.getDishReview())) {
+                textContent.append(cuisineComboDto.getDishReview()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(cuisineComboDto.getExtraNote())) {
+                textContent.append(cuisineComboDto.getExtraNote()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(cuisineComboDto.getReserveRule())) {
+                textContent.append(cuisineComboDto.getReserveRule()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(cuisineComboDto.getUsageRule())) {
+                textContent.append(cuisineComboDto.getUsageRule()).append(" ");
+            }
+
+            List<String> imageUrls = new ArrayList<>();
+
+            if (StringUtils.isNotEmpty(cuisineComboDto.getImages())) {
+                String[] urls = cuisineComboDto.getImages().split(",");
+                for (String url : urls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        String trimmedUrl = url.trim();
+                        imageUrls.add(trimmedUrl);
+                    }
+                }
+            }
+
+            if (StringUtils.isNotEmpty(cuisineComboDto.getImageContent())) {
+                String[] urls = cuisineComboDto.getImageContent().split(",");
+                for (String url : urls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        String trimmedUrl = url.trim();
+                        imageUrls.add(trimmedUrl);
+                    }
+                }
+            }
+
+            // 执行AI审核
+            if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
+                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
+//                boolean allPassed = (auditResult != null);
+                
+                LambdaUpdateWrapper<StoreCuisine> auditUpdateWrapper = new LambdaUpdateWrapper<>();
+                auditUpdateWrapper.eq(StoreCuisine::getId, updatedCuisine.getId());
+                auditUpdateWrapper.set(StoreCuisine::getRejectionReason, null);
+                auditUpdateWrapper.set(StoreCuisine::getAuditTime, new Date());
+                
+                if (auditResult.isPassed()) {
+                    // 审核通过 审核状态为1 上架状态为1 已上架
+                    auditUpdateWrapper.set(StoreCuisine::getStatus, 1);
+                    auditUpdateWrapper.set(StoreCuisine::getShelfStatus, 1);
+                    log.info("AI审核通过,ID: {}", updatedCuisine.getId());
+                } else {
+                    // 审核拒绝 审核状态为2 上架状态为0 没有上下架状态
+                    auditUpdateWrapper.set(StoreCuisine::getStatus, 2);
+                    auditUpdateWrapper.set(StoreCuisine::getShelfStatus, 0);
+                    log.info("AI审核拒绝,ID: {}", updatedCuisine.getId());
+                }
+                storeCuisineMapper.update(null, auditUpdateWrapper);
+            }
+            
             return R.success("修改成功");
+            
+        } catch (Exception e) {
+            log.error("修改美食套餐或单品异常", e);
+            return R.fail("修改失败:" + e.getMessage());
         }
-        return R.fail("修改失败");
     }
 
     @ApiOperation("获取所有美食单品名称,用于添加套餐")
@@ -325,7 +432,7 @@ public class StoreCuisineController {
             if(null==origin){
                 queryWrapper.eq(StoreCuisine::getShelfStatus, 1);
             }else if(origin == 0){
-                queryWrapper.in(StoreCuisine::getShelfStatus, 1,2);
+                queryWrapper.in(StoreCuisine::getShelfStatus, 0,1,2);
             }
             else{
                 queryWrapper.eq(StoreCuisine::getShelfStatus, origin);
@@ -377,7 +484,7 @@ public class StoreCuisineController {
             if(null==origin){
                 queryWrapper.eq(StorePrice::getShelfStatus, 1);
             }else if(origin == 0){
-                queryWrapper.in(StorePrice::getShelfStatus, 1,2);
+                queryWrapper.in(StorePrice::getShelfStatus, 0,1,2);
             }
             else{
                 queryWrapper.eq(StorePrice::getShelfStatus, origin);

+ 107 - 16
alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java

@@ -188,14 +188,14 @@ public class StorePriceController {
             // 执行AI审核
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
                 AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
-                boolean allPassed = (auditResult != null);
+//                boolean allPassed = (auditResult != null);
                 
                 LambdaUpdateWrapper<StorePrice> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 auditUpdateWrapper.eq(StorePrice::getId, savedPrice.getId());
                 auditUpdateWrapper.set(StorePrice::getRejectionReason, null);
                 auditUpdateWrapper.set(StorePrice::getAuditTime, new Date());
                 
-                if (allPassed) {
+                if (auditResult.isPassed()) {
                     // 审核通过 审核状态为1 上架状态为1 已上架
                     auditUpdateWrapper.set(StorePrice::getStatus, 1);
                     auditUpdateWrapper.set(StorePrice::getShelfStatus, 1);
@@ -232,27 +232,118 @@ public class StorePriceController {
     public R<String> update(@RequestBody StorePrice storePrice) {
         log.info("StorePriceController.update?storePrice={}", storePrice);
         
+        // 参数验证
+        if (storePrice == null) {
+            log.error("修改通用价目失败:参数不能为空");
+            return R.fail("参数不能为空");
+        }
+        
         // 校验ID不能为空
         if (storePrice.getId() == null) {
             log.error("修改通用价目失败:ID不能为空");
             return R.fail("ID不能为空");
         }
         
-        // 检查记录是否存在
-        StorePrice existingPrice = storePriceService.getById(storePrice.getId());
-        if (existingPrice == null) {
-            log.error("修改通用价目失败:ID={} 的记录不存在", storePrice.getId());
-            return R.fail("记录不存在,无法修改");
-        }
-        
-        // 执行更新
-        boolean result = storePriceService.updateById(storePrice);
-        if (result) {
+        try {
+            // 检查记录是否存在
+            StorePrice existingPrice = storePriceService.getById(storePrice.getId());
+            if (existingPrice == null) {
+                log.error("修改通用价目失败:ID={} 的记录不存在", storePrice.getId());
+                return R.fail("记录不存在,无法修改");
+            }
+            
+            // 执行更新
+            boolean updateResult = storePriceService.updateById(storePrice);
+            if (!updateResult) {
+                log.error("修改通用价目失败:ID={},更新操作返回false", storePrice.getId());
+                return R.fail("修改失败,请检查数据是否正确");
+            }
+            
+            // 查询更新后的记录
+            StorePrice updatedPrice = storePriceService.getById(storePrice.getId());
+            if (updatedPrice == null) {
+                log.error("修改通用价目失败:更新后查询不到数据,ID={}", storePrice.getId());
+                return R.fail("修改失败:数据更新异常");
+            }
+            
+            // 将状态置为"审核中"(0)
+            LambdaUpdateWrapper<StorePrice> auditingWrapper = new LambdaUpdateWrapper<>();
+            auditingWrapper.eq(StorePrice::getId, updatedPrice.getId());
+            auditingWrapper.set(StorePrice::getStatus, 0);
+            auditingWrapper.set(StorePrice::getRejectionReason, null);
+            auditingWrapper.set(StorePrice::getAuditTime, new Date());
+            storePriceMapper.update(null, auditingWrapper);
+            
+            // 组装 AI 审核文本和图片
+            StringBuilder textContent = new StringBuilder();
+            if (StringUtils.isNotEmpty(storePrice.getName())) {
+                textContent.append(storePrice.getName()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(storePrice.getDetailContent())) {
+                textContent.append(storePrice.getDetailContent()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(storePrice.getExtraNote())) {
+                textContent.append(storePrice.getExtraNote()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(storePrice.getReserveRule())) {
+                textContent.append(storePrice.getReserveRule()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(storePrice.getUsageRule())) {
+                textContent.append(storePrice.getUsageRule()).append(" ");
+            }
+
+            List<String> imageUrls = new ArrayList<>();
+
+            if (StringUtils.isNotEmpty(storePrice.getImages())) {
+                String[] urls = storePrice.getImages().split(",");
+                for (String url : urls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        String trimmedUrl = url.trim();
+                        imageUrls.add(trimmedUrl);
+                    }
+                }
+            }
+
+            if (StringUtils.isNotEmpty(storePrice.getImageContent())) {
+                String[] urls = storePrice.getImageContent().split(",");
+                for (String url : urls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        String trimmedUrl = url.trim();
+                        imageUrls.add(trimmedUrl);
+                    }
+                }
+            }
+
+            // 执行AI审核
+            if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
+                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
+//                boolean allPassed = (auditResult != null);
+                
+                LambdaUpdateWrapper<StorePrice> auditUpdateWrapper = new LambdaUpdateWrapper<>();
+                auditUpdateWrapper.eq(StorePrice::getId, updatedPrice.getId());
+                auditUpdateWrapper.set(StorePrice::getRejectionReason, null);
+                auditUpdateWrapper.set(StorePrice::getAuditTime, new Date());
+                
+                if (auditResult.isPassed()) {
+                    // 审核通过 审核状态为1 上架状态为1 已上架
+                    auditUpdateWrapper.set(StorePrice::getStatus, 1);
+                    auditUpdateWrapper.set(StorePrice::getShelfStatus, 1);
+                    log.info("AI审核通过,ID: {}", updatedPrice.getId());
+                } else {
+                    // 审核拒绝 审核状态为2 上架状态为0 没有上下架状态
+                    auditUpdateWrapper.set(StorePrice::getStatus, 2);
+                    auditUpdateWrapper.set(StorePrice::getShelfStatus, 0);
+                    log.info("AI审核拒绝,ID: {}", updatedPrice.getId());
+                }
+                storePriceMapper.update(null, auditUpdateWrapper);
+            }
+            
             return R.success("修改成功");
+            
+        } catch (Exception e) {
+            log.error("修改通用价目异常", e);
+            return R.fail("修改失败:" + e.getMessage());
         }
-        
-        log.error("修改通用价目失败:ID={},更新操作返回false", storePrice.getId());
-        return R.fail("修改失败,请检查数据是否正确");
     }
 
     @TrackEvent(
@@ -361,7 +452,7 @@ public class StorePriceController {
         if(null==origin){
             queryWrapper.eq(StorePrice::getShelfStatus, 1);
         }else if(origin==0){
-            queryWrapper.in(StorePrice::getShelfStatus, 1,2);
+            queryWrapper.in(StorePrice::getShelfStatus, 0,1,2);
         }else{
             queryWrapper.eq(StorePrice::getShelfStatus, origin);
         }

+ 13 - 5
alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java

@@ -388,11 +388,19 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
         wrapper.eq(CommonRating::getIsShow, 1);
         List<CommonRating> commonRatings = commonRatingMapper.selectList(wrapper);
         List<Long> collect = commonRatings.stream().map(x -> x.getId()).collect(Collectors.toList());
-        // 查询没有回复的评价数量
-        wrapper.eq(CommonRating::getDeleteFlag,0);
-        wrapper.ge(CommonRating::getScore,0.5);
-        wrapper.le(CommonRating::getScore,2.5);
-        Integer noReplyCount = commonRatingMapper.getRatingWithNoReply(wrapper);
+        // 查询没有回复的差评数量(使用新 wrapper,避免复用导致条件累积问题)
+        // 注意:因为 deleteFlag 字段有 @TableLogic 注解,LambdaQueryWrapper.eq() 对该字段可能失效
+        // 而 getRatingWithNoReply 是自定义 XML 查询,不会自动补充逻辑删除条件
+        // 所以这里使用 .apply() 直接写 SQL 条件,确保 delete_flag = 0 生效
+        LambdaQueryWrapper<CommonRating> noReplyWrapper = new LambdaQueryWrapper<>();
+        noReplyWrapper.eq(CommonRating::getBusinessId, businessId);
+        noReplyWrapper.eq(CommonRating::getBusinessType, businessType);
+        noReplyWrapper.eq(CommonRating::getAuditStatus, 1);
+        noReplyWrapper.eq(CommonRating::getIsShow, 1);
+        noReplyWrapper.apply("delete_flag = 0");
+        noReplyWrapper.ge(CommonRating::getScore, 0.5);
+        noReplyWrapper.le(CommonRating::getScore, 2.5);
+        Integer noReplyCount = commonRatingMapper.getRatingWithNoReply(noReplyWrapper);
 
         // 如果为空直接返回
         Map<String, Object> ratingCount = new HashMap<>();

+ 3 - 22
alien-store/src/main/java/shop/alien/store/service/impl/LicenseAuditAsyncService.java

@@ -311,28 +311,9 @@ public class LicenseAuditAsyncService {
                             .set(StoreInfo::getBusinessLicenseStatus, 3)
                             .set(StoreInfo::getBusinessLicenseReason, rejectReason)
                     );
-                    // 审核拒绝时,删除store_img表中的记录(逻辑删除),避免前端展示审核拒绝的图片 ->之前都没插入。。。应该没用这段
-                    LambdaUpdateWrapper<StoreImg> deleteImgWrapper = new LambdaUpdateWrapper<>();
-                    deleteImgWrapper.eq(StoreImg::getStoreId, storeId)
-                            .eq(StoreImg::getImgType, 14) // 营业执照对应14
-                            .eq(StoreImg::getImgUrl, imageUrl)
-                            .eq(StoreImg::getDeleteFlag, 0)
-                            .set(StoreImg::getDeleteFlag, 1);
-                    storeImgMapper.update(null, deleteImgWrapper);
-                    // 查询最新的type=14的记录,更新imgSort
-                    StoreImg latestImg = storeImgMapper.selectDeleImg(
-                            new LambdaQueryWrapper<StoreImg>()
-                                    .eq(StoreImg::getStoreId, storeId)
-                                    .eq(StoreImg::getImgType, 14)
-                                    .eq(StoreImg::getDeleteFlag, 1)
-                                    .last("limit 1")
-                                    .orderByDesc(StoreImg::getId)
-                    );
-                    if (latestImg != null) {
-                        int update = storeImgMapper.updateDelete(String.valueOf(latestImg.getId()));
-                        log.info("{}AI审核拒绝,已更新store_img记录,门店ID:{},图片URL:{},更新记录数:{}", licenseTypeName, storeId, imageUrl, update);
-                    }
-                    log.info("{}AI审核拒绝,已删除store_img记录,门店ID:{},图片URL:{}", licenseTypeName, storeId, imageUrl);
+                    // 审核拒绝时,不需要操作store_img表
+                    // 因为uploadBusinessLicense方法不会插入新图片到store_img,旧的营业执照图片仍然有效
+                    log.info("{}AI审核拒绝,保留原有营业执照图片不变,门店ID:{},图片URL:{}", licenseTypeName, storeId, imageUrl);
                 } else if (needApprove) {
                     // 审核通过
                     updateWrapper.set(StoreLicenseHistory::getLicenseExecuteStatus, 1); // 1-审核通过

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

@@ -116,48 +116,9 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 if (null != goodsRecord) lifeuserViolation.setBusinessId(goodsRecord.getId());
             }
             int result = lifeUserViolationMapper.insert(lifeuserViolation);
-            aiReportReviewUtil.reviewReport(lifeuserViolation.getId(), lifeuserViolation.getReportContextType());
             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);
-
-
-                // 举报人消息
-                    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());
-                        }
-                    }
+                // 调用AI异步审核,审核完成后会自动发送审核结果通知
+                aiReportReviewUtil.reviewReport(lifeuserViolation.getId(), lifeuserViolation.getReportContextType());
                 return result;
             }
         } catch (Exception e) {

+ 30 - 26
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -318,10 +318,24 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         }
         // 设置返回的审核状态(以实际的头图数据为准)
         storeMainInfoVo.setHeadImgStatus(headImgStatus);
-        List<StoreUser> storeUsers = storeUserMapper.selectList(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeInfo.getId()));
+        List<StoreUser> storeUsers = storeUserMapper.selectList(new LambdaQueryWrapper<StoreUser>()
+                .eq(StoreUser::getStoreId, storeInfo.getId())
+                .eq(StoreUser::getDeleteFlag, 0));
+        
+        // 设置商家头像(优先取主账号头像,accountType=1)
+        String storeUserHeadImg = null;
         for (StoreUser storeUser : storeUsers) {
             storeMainInfoVo.setLogoutFlagUser(storeUser.getLogoutFlag());
+            // 优先取主账号头像
+            if (storeUser.getAccountType() != null && storeUser.getAccountType() == 1 && storeUser.getHeadImg() != null) {
+                storeUserHeadImg = storeUser.getHeadImg();
+                break; // 找到主账号头像后直接退出
+            } else if (storeUserHeadImg == null && storeUser.getHeadImg() != null) {
+                // 如果没有主账号或主账号没有头像,则取第一个有头像的用户
+                storeUserHeadImg = storeUser.getHeadImg();
+            }
         }
+        storeMainInfoVo.setStoreUserHeadImg(storeUserHeadImg);
         // 计算店铺到最近地铁站的距离
         JSONObject nearbySubway = gaoDeMapUtil.getNearbySubway(storeMainInfoVo.getStorePosition().split(",")[0], storeMainInfoVo.getStorePosition().split(",")[1]);
         // 地铁名
@@ -4247,32 +4261,22 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                                                          String endSubmitDate) {
         IPage<StoreLicenseInfoVo> page = new Page<>(pageNum, pageSize);
         IPage<StoreLicenseInfoVo> storeLicensePage = storeInfoMapper.getStoreLicensePage(page, storeName, storeContact, storeTel, businessSection, imgType, states, startSubmitDate, endSubmitDate);
+        // 所有记录统一来自 store_license_history,状态值为 license_execute_status: 1-审核通过, 2-审核中, 3-审核拒绝
         for (StoreLicenseInfoVo record : storeLicensePage.getRecords()) {
-            if(record.getStates() != null){
-                if(record.getImgType() != null && record.getImgType() == 35){
-                    // 其他资质证明:直接使用状态值描述
-                    // license_execute_status: 1-审核通过, 2-审核中, 3-审核拒绝
-                    switch (record.getStates().toString()) {
-                        case "1":
-                            record.setStatesName("审核通过");
-                            break;
-                        case "2":
-                            record.setStatesName("审核中");
-                            break;
-                        case "3":
-                            record.setStatesName("审核拒绝");
-                            break;
-                        default:
-                            record.setStatesName("未知");
-                            break;
-                    }
-                } else {
-                    // 营业执照:从字典表获取状态名称
-                    StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getDictId, record.getStates())
-                            .eq(StoreDictionary::getTypeName, "foodLicenceStatus"));
-                    if(storeDictionary != null){
-                        record.setStatesName(storeDictionary.getDictDetail());
-                    }
+            if (record.getStates() != null) {
+                switch (record.getStates().toString()) {
+                    case "1":
+                        record.setStatesName("审核通过");
+                        break;
+                    case "2":
+                        record.setStatesName("审核中");
+                        break;
+                    case "3":
+                        record.setStatesName("审核拒绝");
+                        break;
+                    default:
+                        record.setStatesName("未知");
+                        break;
                 }
             }
         }

+ 51 - 5
alien-store/src/main/java/shop/alien/store/service/impl/StorePlatformMenuServiceImpl.java

@@ -20,8 +20,10 @@ import shop.alien.store.service.StorePlatformMenuService;
 
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -53,17 +55,61 @@ public class StorePlatformMenuServiceImpl extends ServiceImpl<StorePlatformMenuM
         queryWrapper.orderByDesc(StorePlatformMenu::getCreatedTime);
         
         // 查询所有符合条件的菜单
-        List<StorePlatformMenu> allMenus = storePlatformMenuMapper.selectList(queryWrapper);
+        List<StorePlatformMenu> matchedMenus = storePlatformMenuMapper.selectList(queryWrapper);
         
-        // 转换为VO对象
+        // 2. 如果提供了菜单名称查询条件,需要向上查找所有父菜单
+        Set<Long> menuIdSet = new HashSet<>();
+        List<StorePlatformMenu> allMenus = new ArrayList<>(matchedMenus);
+        
+        if (StringUtils.hasText(menuName) && !CollectionUtils.isEmpty(matchedMenus)) {
+            // 收集所有匹配菜单的ID
+            for (StorePlatformMenu menu : matchedMenus) {
+                menuIdSet.add(menu.getMenuId());
+            }
+            
+            // 递归向上查找所有父菜单
+            Set<Long> parentIds = new HashSet<>();
+            for (StorePlatformMenu menu : matchedMenus) {
+                Long parentId = menu.getParentId();
+                if (parentId != null && parentId != 0) {
+                    parentIds.add(parentId);
+                }
+            }
+            
+            // 递归向上查找所有父菜单,直到根节点
+            while (!parentIds.isEmpty()) {
+                Set<Long> nextLevelParentIds = new HashSet<>();
+                for (Long parentId : parentIds) {
+                    if (!menuIdSet.contains(parentId)) {
+                        StorePlatformMenu parentMenu = storePlatformMenuMapper.selectById(parentId);
+                        if (parentMenu != null && "0".equals(parentMenu.getDelFlag())) {
+                            allMenus.add(parentMenu);
+                            menuIdSet.add(parentId);
+                            
+                            // 继续向上查找
+                            Long grandParentId = parentMenu.getParentId();
+                            if (grandParentId != null && grandParentId != 0) {
+                                nextLevelParentIds.add(grandParentId);
+                            }
+                        }
+                    }
+                }
+                parentIds = nextLevelParentIds;
+            }
+            
+            log.info("查询到匹配菜单{}条,向上查找到父菜单{}条,总计{}条", 
+                    matchedMenus.size(), allMenus.size() - matchedMenus.size(), allMenus.size());
+        }
+        
+        // 3. 转换为VO对象
         List<StorePlatformMenuVo> allMenuVos = allMenus.stream()
                 .map(this::convertToVo)
                 .collect(Collectors.toList());
         
-        // 2. 构建树形结构(根据parent_id构建)
+        // 4. 构建树形结构(根据parent_id构建)
         List<StorePlatformMenuVo> treeMenus = buildMenuTreeByParentId(allMenuVos);
         
-        // 3. 对一级菜单(parent_id = 0 或 null)进行分页
+        // 5. 对一级菜单(parent_id = 0 或 null)进行分页
         List<StorePlatformMenuVo> level1Menus = treeMenus.stream()
                 .filter(menu -> menu.getParentId() == null || menu.getParentId() == 0)
                 .collect(Collectors.toList());
@@ -75,7 +121,7 @@ public class StorePlatformMenuServiceImpl extends ServiceImpl<StorePlatformMenuM
         
         List<StorePlatformMenuVo> pageMenus = start < total ? level1Menus.subList(start, end) : new ArrayList<>();
         
-        // 4. 构建分页结果
+        // 6. 构建分页结果
         IPage<StorePlatformMenuVo> pageResult = new Page<>(page, size, total);
         pageResult.setRecords(pageMenus);
         

+ 188 - 7
alien-store/src/main/java/shop/alien/store/util/ai/AiReportReviewUtil.java

@@ -13,11 +13,13 @@ import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
-import shop.alien.entity.store.LifeUserViolation;
-import shop.alien.mapper.CommonCommentMapper;
-import shop.alien.mapper.LifeUserDynamicsMapper;
-import shop.alien.mapper.LifeUserViolationMapper;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.*;
+import shop.alien.store.config.WebSocketProcess;
 
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -42,7 +44,12 @@ public class AiReportReviewUtil {
 
     private final LifeUserViolationMapper lifeUserViolationMapper;
     private final CommonCommentMapper commonCommentMapper;
-    private final LifeUserDynamicsMapper  lifeUserDynamicsMapper;
+    private final LifeUserDynamicsMapper lifeUserDynamicsMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final WebSocketProcess webSocketProcess;
+    private final LifeUserMapper lifeUserMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final StoreCommentMapper storeCommentMapper;
 
     @Async("taskExecutor")
     public void reviewReport(Integer reportId, String reportType) {
@@ -77,10 +84,19 @@ public class AiReportReviewUtil {
         if(response.getStatusCode().isError()){
             log.error("调用ai举报接口失败,URL:{},requestBody: {},response: {}", aiReportReviewUrl, requestBody, response);
         } else {
+            // 解析AI审核结果
+            String processingStatus = JSONObject.parseObject(response.getBody()).getJSONObject("data").getJSONObject("result").getString("processing_status");
+            boolean success = "1".equals(processingStatus);
+
+            LifeUserViolation lifeUserViolation = lifeUserViolationMapper.selectById(reportId);
+
+            // 更新举报处理状态
+            lifeUserViolation.setProcessingStatus(processingStatus);
+            lifeUserViolation.setProcessingTime(new Date());
+            lifeUserViolationMapper.updateById(lifeUserViolation);
+
             // 如果举报成功删除对应数据,动态/评论
-            boolean success = JSONObject.parseObject(response.getBody()).getJSONObject("data").getJSONObject("result").getString("processing_status").equals("1");
             if(success){
-                LifeUserViolation lifeUserViolation = lifeUserViolationMapper.selectById(reportId);
                 int i = 0;
                 if(reportType.equals("2")){
                     // 删除动态
@@ -94,6 +110,171 @@ public class AiReportReviewUtil {
                     log.info("删除动态/评论失败,type:{},举报id:{}", reportType, reportId);
                 }
             }
+
+            // 发送AI审核结果通知(举报人 + 被举报人)
+            sendReviewResultNotification(lifeUserViolation, success);
+        }
+    }
+
+    /**
+     * 发送AI审核结果通知给举报人和被举报人
+     *
+     * @param v       举报记录
+     * @param success 审核是否通过
+     */
+    private void sendReviewResultNotification(LifeUserViolation v, boolean success) {
+        try {
+            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            SimpleDateFormat simpleDateFormats = new SimpleDateFormat("yyyy-MM-dd");
+            String violationTime = simpleDateFormat.format(v.getCreatedTime());
+
+            // 获取被举报人名称
+            String reportedUserName = "";
+            if (StringUtils.hasText(v.getReportedUserId())) {
+                if ("1".equals(v.getReportedUserType())) {
+                    StoreUser storeUser = storeUserMapper.selectById(v.getReportedUserId());
+                    if (storeUser != null) reportedUserName = storeUser.getNickName();
+                } else {
+                    LifeUser lifeUser = lifeUserMapper.selectById(v.getReportedUserId());
+                    if (lifeUser != null) reportedUserName = lifeUser.getUserName();
+                }
+            }
+
+            // 获取被举报动态日期
+            String dynamicsDate = simpleDateFormats.format(new Date());
+            if (StringUtils.hasText(v.getDynamicsId())) {
+                LifeUserDynamics dynamics = lifeUserDynamicsMapper.selectById(v.getDynamicsId());
+                if (dynamics != null) dynamicsDate = simpleDateFormats.format(dynamics.getCreatedTime());
+            }
+
+            // 获取被举报评论日期
+            String commonDate = simpleDateFormats.format(new Date());
+            if (StringUtils.hasText(v.getCommentId())) {
+                StoreComment storeComment = storeCommentMapper.selectById(v.getCommentId());
+                if (storeComment != null) commonDate = simpleDateFormats.format(storeComment.getCreatedTime());
+            }
+
+            String message = "";
+            String reportedMessage = "";
+            String title = "";
+            String reportContextType = v.getReportContextType();
+
+            if (success) {
+                // 审核通过
+                switch (reportContextType) {
+                    case "1":
+                        message = "您在" + violationTime + "举报用户\u201C" + reportedUserName + "\u201D,涉嫌违法违规,经核实,确实存在违规行为,平台已将用户禁用,感谢您为此做出的贡献。";
+                        title = "用户举报成功通知";
+                        break;
+                    case "2":
+                        message = "您在" + violationTime + "举报用户\u201C" + reportedUserName + "\u201D在" + dynamicsDate + "发布的动态,涉嫌违法违规,经核实,确实存在违规行为,平台已将此动态下架,感谢您为此做出的贡献";
+                        reportedMessage = "您在" + dynamicsDate + "发布的动态,经核实,确实存在违规行为,平台已将此动态下架,应用内的环境需要我们共同维护";
+                        title = "动态举报成功通知";
+                        break;
+                    case "3":
+                        message = "您在" + violationTime + "举报用户\u201C" + reportedUserName + "\u201D在" + commonDate + "发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
+                        reportedMessage = "您在" + commonDate + "发布的评论,经核实,确实存在违规行为,平台已将此评论下架,应用内的环境需要我们共同维护。";
+                        title = "评论举报成功通知";
+                        break;
+                    default:
+                        message = "您的举报经平台审核,确实存在违规行为,感谢您为此做出的贡献。";
+                        title = "举报成功通知";
+                        break;
+                }
+            } else {
+                // 审核驳回
+                switch (reportContextType) {
+                    case "1":
+                        message = "您在" + violationTime + "举报用户\u201C" + reportedUserName + "\u201D,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                        title = "用户举报失败通知";
+                        break;
+                    case "2":
+                        message = "您在" + violationTime + "举报用户\u201C" + reportedUserName + "\u201D在" + dynamicsDate + "发布的动态,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                        title = "动态举报失败通知";
+                        break;
+                    case "3":
+                        message = "您在" + violationTime + "举报用户\u201C" + reportedUserName + "\u201D在" + commonDate + "发布的评论,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                        title = "评论举报失败通知";
+                        break;
+                    default:
+                        message = "您的举报经平台审核,不存在违规行为,感谢您为此做出的贡献。";
+                        title = "举报失败通知";
+                        break;
+                }
+            }
+
+            // 发送通知给举报人
+            String reporterPhoneId = getPhoneId(v.getReportingUserType(), v.getReportingUserId());
+            if (StringUtils.hasText(reporterPhoneId)) {
+                LifeNotice notice = new LifeNotice();
+                notice.setSenderId("system");
+                notice.setBusinessId(v.getId());
+                notice.setTitle(title);
+                notice.setNoticeType(1);
+                notice.setIsRead(0);
+                notice.setReceiverId(reporterPhoneId);
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("message", message);
+                notice.setContext(jsonObject.toJSONString());
+                lifeNoticeMapper.insert(notice);
+
+                WebSocketVo websocketVo = new WebSocketVo();
+                websocketVo.setSenderId("system");
+                websocketVo.setReceiverId(reporterPhoneId);
+                websocketVo.setCategory("notice");
+                websocketVo.setNoticeType("1");
+                websocketVo.setIsRead(0);
+                websocketVo.setText(JSONObject.from(notice).toJSONString());
+                webSocketProcess.sendMessage(reporterPhoneId, JSONObject.from(websocketVo).toJSONString());
+                log.info("AI审核结果通知已发送给举报人,reportId={}, phoneId={}, success={}", v.getId(), reporterPhoneId, success);
+            }
+
+            // 发送通知给被举报人(仅审核通过且有被举报通知内容时)
+            if (StringUtils.hasText(reportedMessage)) {
+                String reportedPhoneId = getPhoneId(v.getReportedUserType(), v.getReportedUserId());
+                if (StringUtils.hasText(reportedPhoneId)) {
+                    LifeNotice reportedNotice = new LifeNotice();
+                    reportedNotice.setSenderId("system");
+                    reportedNotice.setBusinessId(v.getId());
+                    reportedNotice.setTitle(title);
+                    reportedNotice.setNoticeType(1);
+                    reportedNotice.setIsRead(0);
+                    reportedNotice.setReceiverId(reportedPhoneId);
+                    JSONObject reportedJson = new JSONObject();
+                    reportedJson.put("message", reportedMessage);
+                    reportedNotice.setContext(reportedJson.toJSONString());
+                    lifeNoticeMapper.insert(reportedNotice);
+
+                    WebSocketVo reportedWsVo = new WebSocketVo();
+                    reportedWsVo.setSenderId("system");
+                    reportedWsVo.setReceiverId(reportedPhoneId);
+                    reportedWsVo.setCategory("notice");
+                    reportedWsVo.setNoticeType("1");
+                    reportedWsVo.setIsRead(0);
+                    reportedWsVo.setText(JSONObject.from(reportedNotice).toJSONString());
+                    webSocketProcess.sendMessage(reportedPhoneId, JSONObject.from(reportedWsVo).toJSONString());
+                    log.info("AI审核结果通知已发送给被举报人,reportId={}, phoneId={}", v.getId(), reportedPhoneId);
+                }
+            }
+        } catch (Exception e) {
+            log.error("AI审核结果通知发送失败,reportId={}, error={}", v.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 根据用户类型和用户ID获取通知接收ID(phoneId)
+     *
+     * @param userType 用户类型: "1"-商户, "2"-用户
+     * @param userId   用户ID
+     * @return phoneId,格式为 "store_{phone}" 或 "user_{phone}"
+     */
+    private String getPhoneId(String userType, String userId) {
+        if ("1".equals(userType)) {
+            StoreUser storeUser = storeUserMapper.selectById(userId);
+            return storeUser != null ? "store_" + storeUser.getPhone() : null;
+        } else {
+            LifeUser lifeUser = lifeUserMapper.selectById(userId);
+            return lifeUser != null ? "user_" + lifeUser.getUserPhone() : null;
         }
     }
 }

+ 17 - 10
alien-store/src/main/java/shop/alien/store/util/ai/AiUserSessionUtil.java

@@ -54,14 +54,20 @@ public class AiUserSessionUtil {
      * 获取用户会话列表
      *
      * @param userId 用户ID
+     * @param type 会话类型,0是ai客服,1是运营客服
      * @return 用户会话列表响应,包含 success, user_id, total, limit, offset, sessions
      */
-    public JSONObject getUserSessions(Integer userId) {
+    public JSONObject getUserSessions(Integer userId, Integer type) {
         if (userId == null || userId <= 0) {
             log.warn("用户ID为空或无效,无法查询会话列表");
             return buildErrorResponse("用户ID不能为空");
         }
 
+        if (type == null) {
+            log.warn("type参数为空,无法查询会话列表");
+            return buildErrorResponse("type不能为空");
+        }
+
         // 获取AI服务访问令牌
         String accessToken = aiAuthTokenUtil.getAccessToken();
         if (!StringUtils.hasText(accessToken)) {
@@ -73,6 +79,7 @@ public class AiUserSessionUtil {
             // 构建请求体
             Map<String, Object> requestBody = new HashMap<>();
             requestBody.put("user_id", userId);
+            requestBody.put("type", type);
 
             // 构建请求头,添加Authorization
             HttpHeaders headers = new HttpHeaders();
@@ -81,7 +88,7 @@ public class AiUserSessionUtil {
 
             HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
 
-            log.info("调用AI用户会话接口,用户ID: {}", userId);
+            log.info("调用AI用户会话接口,用户ID: {}, type: {}", userId, type);
             ResponseEntity<String> response = restTemplate.postForEntity(userSessionUrl, request, String.class);
 
             if (response != null && response.getStatusCode() == HttpStatus.OK) {
@@ -93,12 +100,12 @@ public class AiUserSessionUtil {
                     if (jsonObject != null) {
                         Boolean success = jsonObject.getBoolean("success");
                         if (Boolean.TRUE.equals(success)) {
-                            log.info("成功获取用户会话列表,用户ID: {}, 总会话数: {}", 
-                                    userId, jsonObject.getInteger("total"));
+                            log.info("成功获取用户会话列表,用户ID: {}, type: {}, 总会话数: {}", 
+                                    userId, type, jsonObject.getInteger("total"));
                             return jsonObject;
                         } else {
                             String message = jsonObject.getString("message");
-                            log.warn("AI接口返回失败,用户ID: {}, message: {}", userId, message);
+                            log.warn("AI接口返回失败,用户ID: {}, type: {}, message: {}", userId, type, message);
                             return jsonObject;
                         }
                     }
@@ -109,15 +116,15 @@ public class AiUserSessionUtil {
                 return buildErrorResponse("AI用户会话接口调用失败");
             }
         } catch (HttpClientErrorException e) {
-            log.error("调用AI用户会话接口失败,HTTP客户端错误,状态码: {}, 响应体: {}, 用户ID: {}", 
-                    e.getStatusCode(), e.getResponseBodyAsString(), userId, e);
+            log.error("调用AI用户会话接口失败,HTTP客户端错误,状态码: {}, 响应体: {}, 用户ID: {}, type: {}", 
+                    e.getStatusCode(), e.getResponseBodyAsString(), userId, type, e);
             return buildErrorResponse("调用AI用户会话接口失败: " + e.getMessage());
         } catch (HttpServerErrorException e) {
-            log.error("调用AI用户会话接口失败,HTTP服务器错误,状态码: {}, 响应体: {}, 用户ID: {}", 
-                    e.getStatusCode(), e.getResponseBodyAsString(), userId, e);
+            log.error("调用AI用户会话接口失败,HTTP服务器错误,状态码: {}, 响应体: {}, 用户ID: {}, type: {}", 
+                    e.getStatusCode(), e.getResponseBodyAsString(), userId, type, e);
             return buildErrorResponse("调用AI用户会话接口失败: " + e.getMessage());
         } catch (Exception e) {
-            log.error("调用AI用户会话接口异常,用户ID: {}", userId, e);
+            log.error("调用AI用户会话接口异常,用户ID: {}, type: {}", userId, type, e);
             return buildErrorResponse("调用AI用户会话接口异常: " + e.getMessage());
         }