feat(web): 增强任务治理与系统诊断能力
新增任务持久化、筛选分页、取消任务、健康检查与 AI 输入校验,并完善前端历史管理交互与容错重试机制。补充对应单元测试,提升系统稳定性和可运维性。
This commit is contained in:
@@ -51,15 +51,22 @@ public class AiWorkflowService {
|
||||
|
||||
private final OutputFileService outputFileService;
|
||||
private final SettingsService settingsService;
|
||||
private final AiInputValidator aiInputValidator;
|
||||
private final RetrySupport retrySupport = new RetrySupport();
|
||||
|
||||
public AiWorkflowService(OutputFileService outputFileService, SettingsService settingsService) {
|
||||
public AiWorkflowService(OutputFileService outputFileService,
|
||||
SettingsService settingsService,
|
||||
AiInputValidator aiInputValidator) {
|
||||
this.outputFileService = outputFileService;
|
||||
this.settingsService = settingsService;
|
||||
this.aiInputValidator = aiInputValidator;
|
||||
}
|
||||
|
||||
public TaskResult analyzeAndExport(AiAnalyzeRequest request, TaskContext context) throws Exception {
|
||||
context.setProgress(10, "正在读取 Markdown 文件");
|
||||
final String content = readMarkdownFiles(request.getFilePaths());
|
||||
final List<Path> markdownFiles = resolveUserFiles(request.getFilePaths());
|
||||
aiInputValidator.validate(markdownFiles);
|
||||
final String content = readMarkdownFiles(markdownFiles);
|
||||
|
||||
context.setProgress(35, "正在请求 DeepSeek 分析");
|
||||
final String period = request.getPeriod() != null && !request.getPeriod().trim().isEmpty()
|
||||
@@ -88,10 +95,9 @@ public class AiWorkflowService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private String readMarkdownFiles(List<String> filePaths) throws IOException {
|
||||
private String readMarkdownFiles(List<Path> filePaths) throws IOException {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (String filePath : filePaths) {
|
||||
final Path path = resolveUserFile(filePath);
|
||||
for (Path path : filePaths) {
|
||||
final String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||
builder.append("\n\n=== 文件: ").append(path.getFileName().toString()).append(" ===\n");
|
||||
builder.append(content);
|
||||
@@ -99,6 +105,17 @@ public class AiWorkflowService {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private List<Path> resolveUserFiles(List<String> userPaths) throws IOException {
|
||||
java.util.ArrayList<Path> files = new java.util.ArrayList<Path>();
|
||||
if (userPaths == null) {
|
||||
return files;
|
||||
}
|
||||
for (String userPath : userPaths) {
|
||||
files.add(resolveUserFile(userPath));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
private Path resolveUserFile(String userPath) throws IOException {
|
||||
if (userPath == null || userPath.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("文件路径不能为空");
|
||||
@@ -135,6 +152,16 @@ public class AiWorkflowService {
|
||||
}
|
||||
|
||||
private String callDeepSeek(String apiKey, String prompt) throws IOException {
|
||||
try {
|
||||
return retrySupport.execute(() -> callDeepSeekOnce(apiKey, prompt), 3, 1000L);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String callDeepSeekOnce(String apiKey, String prompt) throws Exception {
|
||||
final JsonObject message = new JsonObject();
|
||||
message.addProperty("role", "user");
|
||||
message.addProperty("content", prompt);
|
||||
@@ -161,10 +188,14 @@ public class AiWorkflowService {
|
||||
if (response.body() != null) {
|
||||
errorBody = response.body().string();
|
||||
}
|
||||
throw new IllegalStateException("DeepSeek API 调用失败: " + response.code() + " " + errorBody);
|
||||
String detail = "DeepSeek API 调用失败: " + response.code() + " " + errorBody;
|
||||
if (response.code() == 429 || response.code() >= 500) {
|
||||
throw new RetrySupport.RetryableException(detail);
|
||||
}
|
||||
throw new IllegalStateException(detail);
|
||||
}
|
||||
if (response.body() == null) {
|
||||
throw new IllegalStateException("DeepSeek API 返回空响应体");
|
||||
throw new RetrySupport.RetryableException("DeepSeek API 返回空响应体");
|
||||
}
|
||||
|
||||
final String raw = response.body().string();
|
||||
|
||||
Reference in New Issue
Block a user