Bladeren bron

feat(store): 实现差评申诉分析与处理功能

- 新增差评申诉列表查询接口,支持多表关联查询
- 添加处理中申诉记录查询及状态更新逻辑
- 集成AI差评申诉辅助系统,实现自动分析任务调度
- 增加图片转Base64工具方法及URL合法性校验
- 完善申诉结果处理流程,包括评论软删除与状态变更
- 提供定时任务调用入口,对接XXL-JOB实现自动化处理
fcw 2 weken geleden
bovenliggende
commit
688d846e28

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

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

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

@@ -0,0 +1,205 @@
+package shop.alien.job.store;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.xxl.job.core.context.XxlJobHelper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import shop.alien.entity.store.StoreCommentAppeal;
+import shop.alien.entity.result.R;
+import shop.alien.mapper.StoreCommentAppealMapper;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 调用AI差评申诉辅助系统服务类
+ *
+ * @author fcw
+ * @date 2025/11/20 14:21
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class BadReviewAppealJob {
+
+    private final RestTemplate restTemplate;
+
+    private final StoreCommentAppealMapper storeCommentAppealMapper;
+
+    @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;
+
+    /**
+     * 差评申述置信度分析接口地址
+     * 例如: http://192.168.2.250:9004/api/v1/analyze 或通过网关: http://192.168.2.250:9000/ai/auto_review/api/v1/analyze
+     */
+    @Value("${bad-review-analyze.base-url}")
+    private String analyzeUrl;
+
+    /**
+     * 调用AI服务获取Token
+     */
+    @XxlJob("badReviewAppealJob")
+    public R<String> badReviewAppealJob() {
+        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 {
+            log.info("请求差评申诉辅助系统登录接口===================>");
+            postForEntity = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("调用差评申诉辅助系统登录接口异常", e);
+        }
+
+        if (postForEntity != null) {
+            if (postForEntity.getStatusCodeValue() == 200) {
+                log.info("差评申诉辅助系统登录成功 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");
+                // 调用差评申述置信度分析任务
+                return invokeAnalyzeTask(accessToken);
+            } else {
+                log.error("请求差评申诉辅助系统 登录接口失败 http状态:" + postForEntity.getStatusCode());
+                return R.fail("请求差评申诉辅助系统 登录接口失败 http状态:" + postForEntity.getStatusCode());
+            }
+        }
+        XxlJobHelper.handleFail("调用差评申诉辅助系统登录任务执行失败 返回异常");
+        return R.fail("调用差评申诉辅助系统登录任务执行失败 返回异常");
+
+    }
+
+    private R<String> invokeAnalyzeTask(String token) {
+        log.info("开始调用差评申述置信度分析接口, url: {}", analyzeUrl);
+
+        // 获取当前日期
+        LocalDate today = LocalDate.now();
+
+        // 获取当月第一天
+        LocalDate firstDayOfMonth = today.withDayOfMonth(1);
+
+        // 获取当月最后一天
+        LocalDate lastDayOfMonth = today.withDayOfMonth(today.lengthOfMonth());
+
+        // 格式化为字符串(可根据需要调整格式)
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        String startDate = firstDayOfMonth.format(formatter);
+        String endDate = lastDayOfMonth.format(formatter);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + token);
+
+        AnalyzeRequest analyzeRequest = new AnalyzeRequest();
+        analyzeRequest.setStart_time(startDate + "T00:00:00");
+        analyzeRequest.setEnd_time(endDate + "T23:59:59");
+
+        HttpEntity<AnalyzeRequest> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+        ResponseEntity<String> analyzeResp = null;
+        try {
+            analyzeResp = restTemplate.postForEntity(analyzeUrl, analyzeEntity, String.class);
+        } catch (Exception e) {
+            log.error("调用差评申述置信度分析接口异常", e);
+        }
+
+        if (analyzeResp != null) {
+            if (analyzeResp.getStatusCodeValue() == 200) {
+                log.info("postForEntity.getBody()\t" + analyzeResp.getStatusCode());
+                String analyzeBody = analyzeResp.getBody();
+                log.info("差评申述置信度分析成功, 返回: {}", analyzeBody);
+
+                JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+                JSONObject dataJsonObj = null;
+                if (analyzeJson != null) {
+                    dataJsonObj = analyzeJson.getJSONObject("data");
+                }
+                if (dataJsonObj == null) {
+                    log.error("差评申述置信度分析返回数据为空");
+                    XxlJobHelper.handleFail("差评申述置信度分析返回数据为空");
+                    return R.fail("差评申述置信度分析返回数据为空");
+                }
+
+                StoreCommentAppeal storeCommentAppeal = new StoreCommentAppeal();
+//                storeCommentAppeal.setStoreId(dataJsonObj.get());
+//                storeCommentAppeal.setCommentId(appealParam.getCommentId());
+//                storeCommentAppeal.setAppealReason(appealParam.getMerchantMaterial());
+//                storeCommentAppeal.setFinalResult(dataJsonObj.toJSONString());
+//                storeCommentAppeal.setAppealStatus(0);
+//                storeCommentAppeal.setDeleteFlag(0);
+//                storeCommentAppealMapper.insert(storeCommentAppeal);
+
+                XxlJobHelper.handleSuccess("差评申述置信度分析任务执行成功并写入 store_comment_appeal");
+                return R.success("差评申述置信度分析成功,已写入 store_comment_appeal 表");
+            } else {
+                log.error("调用差评申述置信度分析接口失败, http状态: {}", analyzeResp.getStatusCode());
+                XxlJobHelper.handleFail("调用差评申述置信度分析接口失败, http状态:" + analyzeResp.getStatusCode());
+                return R.fail("调用差评申述置信度分析接口失败 http状态:" + analyzeResp.getStatusCode());
+            }
+        }
+        log.error("调用差评申述置信度分析接口返回为空或异常");
+        XxlJobHelper.handleFail("调用差评申述置信度分析接口返回为空或异常");
+        return R.fail("调用差评申述置信度分析接口返回为空或异常");
+    }
+
+
+    /**
+     * 差评申述置信度分析请求体
+     *
+     * 对应文档中的:
+     * {
+     *   "merchant_material": "商家申述材料内容(必填)",
+     *   "user_material": "用户举证材料内容(必填)",
+     *   "order_id": "关联订单ID(可选)",
+     *   "case_id": "申述案件ID(可选)",
+     *   "merchant_images": [...],
+     *   "user_images": [...]
+     * }
+     */
+    class AnalyzeRequest {
+        private String start_time;
+        private String end_time;
+
+        public String getStart_time() {
+            return start_time;
+        }
+
+        public void setStart_time(String start_time) {
+            this.start_time = start_time;
+        }
+
+        public String getEnd_time() {
+            return end_time;
+        }
+
+        public void setEnd_time(String end_time) {
+            this.end_time = end_time;
+        }
+    }
+
+}

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

@@ -1,17 +1,41 @@
 package shop.alien.store.controller;
 
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.StoreCommentAppeal;
+import shop.alien.entity.store.vo.StoreCommentAppealVo;
+import shop.alien.mapper.StoreCommentAppealMapper;
+import shop.alien.mapper.StoreCommentMapper;
 import shop.alien.store.service.LifeUserStoreService;
 import shop.alien.util.common.AlipayTradeAppPay;
 import shop.alien.util.common.ListToPage;
 import shop.alien.util.common.UniqueRandomNumGenerator;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -25,6 +49,30 @@ public class UserStoreController {
 
     private final LifeUserStoreService lifeUserStoreService;
 
+    private final RestTemplate restTemplate;
+
+    private final StoreCommentAppealMapper storeCommentAppealMapper;
+
+    private final StoreCommentMapper storeCommentMapper;
+
+//    @Value("${third-party-login.base-url}")
+    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
+
+    private String resultUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/records/{record_id}/completed";
+
+//    @Value("${third-party-user-name.base-url}")
+    private String userName = "UdUser";
+
+//    @Value("${third-party-pass-word.base-url}")
+    private String passWord = "123456";
+
+    /**
+     * 差评申述置信度分析接口地址
+     * 例如: http://192.168.2.250:9004/api/v1/analyze 或通过网关: http://192.168.2.250:9000/ai/auto_review/api/v1/analyze
+     */
+//    @Value("${bad-review-analyze.base-url}")
+    private String analyzeUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/analyze";
+
     @ApiOperation("查询商铺列表")
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "分页页数", dataType = "String", paramType = "query", required = true),
@@ -118,4 +166,256 @@ public class UserStoreController {
         log.info("LifeUserStoreController.buyQuan?quanId={},couponId={},userId={},cnt={},payPrice={},orderNo={}", quanId, couponId, userId, cnt, payPrice, orderNo);
         return R.data(lifeUserStoreService.buyQuan(quanId, couponId, userId, cnt, payPrice, orderNo));
     }
+
+
+
+    @PostMapping("/getBadReviewAppealJob")
+    @ApiOperation("调用查询差评申诉")
+    public R<String> getBadReviewAppealJob() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用差评申诉辅助系统登录任务执行失败 返回异常");
+        }
+        return invokeAnalyzeTask(accessToken);
+    }
+
+    @ApiOperation("差评申述处理完成结果查询")
+    @GetMapping("/getAppealCompletedResult")
+    public R<String> getAppealCompletedResult() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用差评申诉辅助系统 登录接口失败");
+        }
+        return getNegativeReviewAppealCompletedResult(accessToken);
+    }
+
+    private R<String> getNegativeReviewAppealCompletedResult(String accessToken) {
+
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+        // 查询所有状态为处理中的申诉
+        List<StoreCommentAppeal> pendingAppeals = storeCommentAppealMapper.getPendingAppeals();
+        // 循环调用查询结果接口
+        for (StoreCommentAppeal appeal : pendingAppeals) {
+            String completedUrl = buildCompletedUrl(appeal.getRecordId());
+            Map<String, Object> analyzeRequest = new HashedMap<>();
+            analyzeRequest.put("record_id", appeal.getRecordId());
+
+            HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+            ResponseEntity<String> analyzeResp;
+            try {
+                analyzeResp = restTemplate.postForEntity(completedUrl, analyzeEntity, String.class);
+
+                if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+                    String analyzeBody = analyzeResp.getBody();
+                    log.info("差评申述置信度分析提交成功, 返回: {}", analyzeBody);
+
+                    JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+                    JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+                    if (dataJsonObj == null) {
+                        log.error("差评申述置信度分析返回数据为空");
+                        R.fail("差评申述置信度分析返回数据为空");
+                        continue;
+                    }
+
+                    // 获取record_id用于后续查询
+                    Integer recordId = dataJsonObj.getInteger("record_id");
+                    if (recordId == null) {
+                        log.error("差评申述置信度分析返回record_id为空");
+                        R.fail("差评申述置信度分析返回record_id为空");
+                        continue;
+                    }
+
+                    StoreCommentAppeal sCommentAppeal = new StoreCommentAppeal();
+                    sCommentAppeal.setRecordId(appeal.getRecordId());
+                    // 判断得分大小
+                    if (dataJsonObj.getDouble("user_confidence") > dataJsonObj.getDouble("merchant_confidence")){
+                        sCommentAppeal.setAppealStatus(1);
+                    } else {
+                        sCommentAppeal.setAppealStatus(2);
+                        //假删除评论
+                        StoreComment storeComment = new StoreComment();
+                        storeComment.setId(appeal.getCommentId());
+                        storeComment.setDeleteFlag(1);
+                        storeCommentMapper.updateById(storeComment);
+                    }
+                    sCommentAppeal.setFinalResult(dataJsonObj.toJSONString());
+
+                    sCommentAppeal.setRecordId(recordId);
+                    storeCommentAppealMapper.updateByRecordId(appeal.getRecordId(),
+                            sCommentAppeal.getAppealStatus(),
+                            sCommentAppeal.getFinalResult());
+                } else {
+                    if (analyzeResp != null) {
+                        log.error("调用差评申述置信度分析接口失败, http状态: {}", analyzeResp.getStatusCode());
+                        R.fail("调用差评申述置信度分析接口失败, http状态: " + analyzeResp.getStatusCode());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("调用差评申述查询结果接口异常", e);
+            }
+        }
+        return R.success("调用差评申述置信度分析结果接口完成");
+    }
+
+
+
+
+    private R<String> invokeAnalyzeTask(String token) {
+        log.info("开始调用差评申述置信度分析接口, url: {}", analyzeUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + token);
+
+        // 查询一段时间内的差评申述
+        // 循环插入
+        List<Map<String, Object>> appealList = storeCommentAppealMapper.getAppealList();
+
+        if (appealList == null || appealList.isEmpty()) {
+            log.info("没有需要处理的差评申述");
+            return R.success("没有需要处理的差评申述");
+        }
+
+        for (Map<String, Object> storeCommentAppeal : appealList) {
+            Map<String, Object> analyzeRequest = new HashedMap<>();
+            analyzeRequest.put("merchant_material", storeCommentAppeal.get("appeal_reason") == null ? "" :storeCommentAppeal.get("appeal_reason").toString());
+            analyzeRequest.put("user_material", storeCommentAppeal.get("comment_content") == null ? "" :storeCommentAppeal.get("comment_content").toString());
+            analyzeRequest.put("merchant_images", storeCommentAppeal.get("img_url") == null ? "" :convertImageToBase64(storeCommentAppeal.get("img_url").toString()));
+            analyzeRequest.put("user_images", storeCommentAppeal.get("user_img_url") == null ? "" :convertImageToBase64(storeCommentAppeal.get("user_img_url").toString()));
+            analyzeRequest.put("case_id", storeCommentAppeal.get("comment_id") == null ? "" :storeCommentAppeal.get("comment_id").toString());
+            analyzeRequest.put("order_id", storeCommentAppeal.get("business_id") == null ? "" :storeCommentAppeal.get("business_id").toString());
+
+            HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+            ResponseEntity<String> analyzeResp = null;
+            try {
+                analyzeResp = restTemplate.postForEntity(analyzeUrl, analyzeEntity, String.class);
+            } catch (Exception e) {
+                log.error("调用差评申述置信度分析接口异常", e);
+            }
+
+            if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+                String analyzeBody = analyzeResp.getBody();
+                log.info("差评申述置信度分析提交成功, 返回: {}", analyzeBody);
+
+                JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+                JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+                if (dataJsonObj == null) {
+                    log.error("差评申述置信度分析返回数据为空");
+                    R.fail("差评申述置信度分析返回数据为空");
+                    continue;
+                }
+
+                // 获取record_id用于后续查询
+                Integer recordId = dataJsonObj.getInteger("record_id");
+                if (recordId == null) {
+                    log.error("差评申述置信度分析返回record_id为空");
+                    R.fail("差评申述置信度分析返回record_id为空");
+                    continue;
+                }
+
+                //修改评论状态为"处理中"
+                StoreCommentAppeal sCommentAppeal = new StoreCommentAppeal();
+                sCommentAppeal.setId((Integer) storeCommentAppeal.get("id"));
+                sCommentAppeal.setAppealStatus(3);
+                sCommentAppeal.setRecordId(recordId);
+                storeCommentAppealMapper.updateById(sCommentAppeal);
+
+            } else {
+                if (analyzeResp != null) {
+                    log.error("调用差评申述置信度分析接口失败, http状态: {}", analyzeResp.getStatusCode());
+                    R.fail("调用差评申述置信度分析接口失败, http状态: " + analyzeResp.getStatusCode());
+                }
+            }
+        }
+        return R.success("调用差评申述置信度分析接口完成");
+    }
+
+    /**
+     * 将图片URL转换为Base64编码
+     *
+     * @param imageUrl 图片URL
+     * @return Base64编码字符串
+     */
+    private String convertImageToBase64(String imageUrl) {
+        // 1. 检查是否为空或非URL格式
+        if (!StringUtils.hasText(imageUrl) || !isValidUrl(imageUrl)) {
+            log.warn("无效的图片URL: {}", imageUrl);
+            return "";
+        }
+
+        try {
+            // 2. 下载图片并转换Base64
+            byte[] imageBytes = restTemplate.getForObject(imageUrl, byte[].class);
+            if (imageBytes != null) {
+                return java.util.Base64.getEncoder().encodeToString(imageBytes);
+            }
+        } catch (org.springframework.web.client.HttpClientErrorException.NotFound e) {
+            // 专门处理404错误
+            log.warn("图片不存在 (404), URL: {}", imageUrl);
+            return ""; // 返回空字符串而不是抛出异常
+        } catch (Exception e) {
+            log.error("图片转换为Base64失败, URL: {}", imageUrl, e);
+            return ""; // 其他异常也返回空字符串,避免中断整个流程
+        }
+        return "";
+    }
+
+
+    // 辅助方法:验证URL格式合法性
+    private boolean isValidUrl(String url) {
+        try {
+            // 尝试构造URI,验证格式
+            new URI(url);
+            // 进一步检查是否以http/https开头(可选,根据业务需求)
+            return url.startsWith("http://") || url.startsWith("https://");
+        } catch (URISyntaxException e) {
+            return false;
+        }
+    }
+
+    private String fetchAiServiceToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+            if (response != null && response.getStatusCodeValue() == 200 && response.getBody() != null) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                return dataJson != null ? dataJson.getString("access_token") : null;
+            }
+            log.error("请求差评申诉辅助系统 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        } catch (Exception e) {
+            log.error("调用差评申诉辅助系统登录接口异常", e);
+        }
+        return null;
+    }
+
+    private String buildCompletedUrl(Integer recordId) {
+        String baseUrl = analyzeUrl;
+        if (!StringUtils.hasText(baseUrl)) {
+            throw new IllegalStateException("差评申述分析接口地址未配置");
+        }
+        if (baseUrl.endsWith("/")) {
+            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+        }
+        String completedBase = baseUrl.replace("/api/v1/analyze", "");
+        if (!completedBase.endsWith("/")) {
+            completedBase = completedBase + "/";
+        }
+        return completedBase + "api/v1/records/" + recordId + "/completed";
+    }
 }