diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICaseKnowledgeService.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICaseKnowledgeService.java index 3c32eb49..172e9bc5 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICaseKnowledgeService.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICaseKnowledgeService.java @@ -1,5 +1,6 @@ package com.xboe.module.boecase.service; +import com.xboe.module.boecase.entity.CaseDocumentLog; import com.xboe.module.boecase.entity.Cases; import org.springframework.transaction.annotation.Transactional; @@ -46,7 +47,7 @@ public interface ICaseKnowledgeService { * @param caseId 案例ID * @return 是否成功 */ - boolean updateCaseDocument(String caseId); + boolean retryCaseDocument(String caseId, CaseDocumentLog originalLog); /** * 更新案例文档 diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseDocumentLogServiceImpl.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseDocumentLogServiceImpl.java index 65b322d4..e6ba4a8f 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseDocumentLogServiceImpl.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseDocumentLogServiceImpl.java @@ -1,7 +1,6 @@ package com.xboe.module.boecase.service.impl; import com.xboe.common.utils.StringUtil; -import com.xboe.common.utils.IDGenerator; import com.xboe.common.OrderCondition; import com.xboe.common.PageList; import com.xboe.core.orm.FieldFilters; @@ -189,7 +188,7 @@ public class CaseDocumentLogServiceImpl implements ICaseDocumentLogService { String optType = originalLog.getOptType(); String caseId = originalLog.getCaseId(); - aiDocExecutor.execute(() -> executeRetryLogic(optType, caseId)); + aiDocExecutor.execute(() -> executeRetryLogic(optType, caseId, originalLog)); // 立即返回true表示重试请求已接受,具体结果通过日志异步处理 return true; @@ -200,7 +199,7 @@ public class CaseDocumentLogServiceImpl implements ICaseDocumentLogService { * @param optType 操作类型 * @param caseId 案例ID */ - private void executeRetryLogic(String optType, String caseId) { + private void executeRetryLogic(String optType, String caseId, CaseDocumentLog originalLog) { boolean retrySuccess = false; try { @@ -223,7 +222,7 @@ public class CaseDocumentLogServiceImpl implements ICaseDocumentLogService { } else if (CaseDocumentLogOptTypeEnum.UPDATE.getCode().equals(optType)) { // 更新案例文档 - retrySuccess = caseKnowledgeService.updateCaseDocument(caseId); + retrySuccess = caseKnowledgeService.retryCaseDocument(caseId, originalLog); log.info("[异步任务] 执行更新案例文档重试,caseId: {}, 结果: {}", caseId, retrySuccess); } else { diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseKnowledgeServiceImpl.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseKnowledgeServiceImpl.java index 5f03ad7e..902a2e43 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseKnowledgeServiceImpl.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CaseKnowledgeServiceImpl.java @@ -23,12 +23,11 @@ import com.xboe.module.boecase.properties.CaseAiProperties; import com.xboe.module.boecase.service.IAiAccessTokenService; import com.xboe.module.boecase.service.ICaseKnowledgeService; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.client.methods.*; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; @@ -42,12 +41,9 @@ import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.File; -import java.io.IOException; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.List; -import java.util.concurrent.TimeUnit; /** * 案例-知识库Service实现类 @@ -377,15 +373,148 @@ public class CaseKnowledgeServiceImpl implements ICaseKnowledgeService { } @Override - public boolean updateCaseDocument(String caseId) { + public boolean retryCaseDocument(String caseId, CaseDocumentLog originalLog) { if (StringUtil.isBlank(caseId)) { log.error("更新案例文档失败,案例ID不能为空"); return false; } // 1. 查询案例信息 - Cases caseEntity = casesDao.findOne(FieldFilters.eq("id", caseId)); - return updateCaseDocument(caseEntity); + Cases cases = casesDao.findOne(FieldFilters.eq("id", caseId)); + // 2. 查询待重试内容 + if (StringUtils.equals(originalLog.getRequestUrl(), CaseAiConstants.CASE_DOC_DELETE_INTERFACE_NAME)) { + return updateCaseDocument(cases); + } + // 如果需要上传 + JSONObject requestBody = new JSONObject(); + String userId = getCurrentUserId(); + requestBody.put("userId", userId); + requestBody.put("kId", caseAiProperties.getCaseKnowledgeId()); + requestBody.put("parseType", "AUTO"); + // 2. 检查文件路径 + if (StringUtil.isBlank(cases.getFilePath())) { + log.error("上传案例文档失败,案例文件路径为空,caseId: {}", caseId); + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), "上传案例文档失败,案例文件路径为空", + CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null); + return false; + } + + String savePath = fileUploader.getSavePath(); + String filePath = savePath + cases.getFilePath(); + File file = new File(filePath); + if (!file.exists()) { + log.error("上传案例文档失败,案例文件不存在,filePath: {}", filePath); + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), "上传案例文档失败,案例文件不存在,filePath: " + filePath, + CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null); + return false; + } + + // 4. 构建上传参数 + String fileName = cases.getFileName(); + if (StringUtil.isBlank(fileName)) { + fileName = file.getName(); + } + + String fileType = getFileType(fileName); + + // 5. 重试逻辑:最多3次机会 + int uploadMaxRetries = 3; + + // 构建multipart/form-data请求体 + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.addBinaryBody("file", file); + builder.addTextBody("userId", userId, ContentType.TEXT_PLAIN); + builder.addTextBody("kId", caseAiProperties.getCaseKnowledgeId(), ContentType.TEXT_PLAIN); + builder.addTextBody("fileName", fileName, ContentType.TEXT_PLAIN); + requestBody.put("fileName", fileName); + builder.addTextBody("fileType", fileType, ContentType.TEXT_PLAIN); + requestBody.put("fileType", fileType); + builder.addTextBody("parseType", "AUTO", ContentType.TEXT_PLAIN); + // 由于接口权限,目前采用不回调,而是通过批处理的方式,处理文件状态 + if (caseAiProperties.isFileUploadUseCallback()) { + builder.addTextBody("callbackUrl", caseAiProperties.getFileUploadCallbackUrl(), ContentType.TEXT_PLAIN); + requestBody.put("callbackUrl", caseAiProperties.getFileUploadCallbackUrl()); + } + + String uploadUrl = caseAiProperties.getBaseUrl() + "/apigateway/knowledge/v1/file/upload"; + + // 3. 获取access_token + String accessToken = aiAccessTokenService.getAccessToken(); + if (StringUtil.isBlank(accessToken)) { + log.error("上传案例文档失败,获取access_token失败"); + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), "上传案例文档失败,获取access_token失败", + CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null); + return false; + } + for (int attempt = 1; attempt <= uploadMaxRetries; attempt++) { + log.info("上传案例文档第{}次尝试,caseId: {}", attempt, caseId); + + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpPost httpPost = new HttpPost(uploadUrl); + httpPost.setHeader("X-AI-ApiCode", caseAiProperties.getAiApiCode()); + httpPost.setHeader("access_token", accessToken); + + HttpEntity multipart = builder.build(); + httpPost.setEntity(multipart); + + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + int statusCode = response.getStatusLine().getStatusCode(); + String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + + if (statusCode == 200) { + JSONObject result = JSON.parseObject(responseBody); + if (result.getBooleanValue("success")) { + // 业务处理成功 + JSONObject data = result.getJSONObject("data"); + String taskId = data.getString("taskId"); + + // 保存成功的CaseDocumentLog记录 + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), responseBody, + CaseDocumentLogRunStatusEnum.RUNNING.getCode(), null, null, taskId); + + log.info("上传案例文档成功,等待文档状态变更. caseId: {}, taskId: {}, 尝试次数: {}", caseId, taskId, attempt); + return true; + } else { + // 业务处理失败,不重试 + log.error("上传案例文档业务处理失败,不重试,response: {}", responseBody); + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), responseBody, + CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.SUCCESS.getCode(), CaseDocumentLogCaseStatusEnum.FAILED.getCode(), null); + return false; + } + } else { + // 接口调用失败 + log.error("上传案例文档接口调用失败,第{}次尝试,status: {}, response: {}", attempt, statusCode, responseBody); + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), responseBody, + CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null); + if (attempt == uploadMaxRetries) { + // 最后一次尝试仍然失败 + return false; + } + // 继续下一次重试 + } + } + } catch (Exception e) { + // 接口调用异常 + log.error("上传案例文档接口调用异常,第{}次尝试", attempt, e); + saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME, + requestBody.toJSONString(), "接口调用异常: " + e.getMessage(), + CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null); + if (attempt == uploadMaxRetries) { + // 最后一次尝试仍然异常 + log.error("上传案例文档最终失败,已重试{}次", uploadMaxRetries); + return false; + } + // 继续下一次重试 + } + } + + return false; } @Override