完善多店铺开启多窗口、外观进度条等
This commit is contained in:
+4
@@ -20,6 +20,10 @@ public class AppearancePatentHistoryItemVo {
|
||||
private String fileStatus;
|
||||
private String fileError;
|
||||
private Boolean fileReady;
|
||||
private Integer fileProgressPercent;
|
||||
private Integer fileProgressCurrent;
|
||||
private Integer fileProgressTotal;
|
||||
private String fileProgressMessage;
|
||||
@Schema(description = "任务状态:PENDING=已解析待推送,RUNNING=执行中,SUCCESS=成功,FAILED=失败。", example = "SUCCESS")
|
||||
private String taskStatus;
|
||||
@Schema(description = "结果是否成功。true 表示任务完成并生成结果文件;false 表示失败或未完成。", example = "true")
|
||||
|
||||
+134
-11
@@ -34,8 +34,10 @@ import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskFileJobEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskChunkEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskProgressSnapshotEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskScopeStateEntity;
|
||||
import com.nanri.aiimage.modules.task.service.TaskFileJobService;
|
||||
import com.nanri.aiimage.modules.task.service.TaskProgressSnapshotService;
|
||||
import com.nanri.aiimage.modules.task.service.TransientPayloadStorageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -59,6 +61,7 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
@@ -106,6 +109,7 @@ public class AppearancePatentTaskService {
|
||||
private final AppearancePatentTaskCacheService taskCacheService;
|
||||
private final AppearancePatentProperties properties;
|
||||
private final TaskFileJobService taskFileJobService;
|
||||
private final TaskProgressSnapshotService taskProgressSnapshotService;
|
||||
private final TransientPayloadStorageService transientPayloadStorageService;
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
|
||||
@@ -678,10 +682,10 @@ public class AppearancePatentTaskService {
|
||||
int batchSize = Math.max(1, properties.getCozeBatchSize());
|
||||
List<AppearancePatentResultRowDto> result = new ArrayList<>();
|
||||
for (int i = 0; i < items.size(); i += batchSize) {
|
||||
result.addAll(cozeClient.inspect(items.subList(i, Math.min(i + batchSize, items.size())), prompt));
|
||||
if (progressHook != null) {
|
||||
progressHook.run();
|
||||
}
|
||||
result.addAll(cozeClient.inspect(items.subList(i, Math.min(i + batchSize, items.size())), prompt));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -697,9 +701,6 @@ public class AppearancePatentTaskService {
|
||||
.orderByAsc(TaskChunkEntity::getChunkIndex));
|
||||
log.info("[appearance-patent] async coze start taskId={} chunks={}", task.getId(), chunks.size());
|
||||
for (TaskChunkEntity chunk : chunks) {
|
||||
if (progressHook != null) {
|
||||
progressHook.run();
|
||||
}
|
||||
Map<String, AppearancePatentResultRowDto> persistedRows = readChunkRows(chunk);
|
||||
if (persistedRows.isEmpty()) {
|
||||
continue;
|
||||
@@ -1021,12 +1022,68 @@ public class AppearancePatentTaskService {
|
||||
if (result == null || !MODULE_TYPE.equals(result.getModuleType())) {
|
||||
throw new BusinessException("结果记录不存在");
|
||||
}
|
||||
Runnable progressHook = () -> taskFileJobService.touchRunning(job.getId());
|
||||
List<TaskChunkEntity> chunks = taskChunkMapper.selectList(new LambdaQueryWrapper<TaskChunkEntity>()
|
||||
.eq(TaskChunkEntity::getTaskId, task.getId())
|
||||
.eq(TaskChunkEntity::getModuleType, MODULE_TYPE)
|
||||
.orderByAsc(TaskChunkEntity::getChunkIndex));
|
||||
int cozeWorkUnits = countCozeWorkUnits(chunks, Math.max(1, properties.getCozeBatchSize()));
|
||||
int totalProgressUnits = Math.max(3, cozeWorkUnits + 3);
|
||||
int[] completedProgressUnits = {0};
|
||||
saveFileBuildProgress(task, job, totalProgressUnits, completedProgressUnits[0], "正在请求 Coze");
|
||||
Runnable progressHook = () -> {
|
||||
taskFileJobService.touchRunning(job.getId());
|
||||
completedProgressUnits[0] = Math.min(totalProgressUnits - 2, completedProgressUnits[0] + 1);
|
||||
saveFileBuildProgress(task, job, totalProgressUnits, completedProgressUnits[0], "正在请求 Coze");
|
||||
};
|
||||
applyCozeToPersistedChunks(task, progressHook);
|
||||
progressHook.run();
|
||||
completedProgressUnits[0] = Math.max(completedProgressUnits[0], totalProgressUnits - 2);
|
||||
saveFileBuildProgress(task, job, totalProgressUnits, completedProgressUnits[0], "正在组装 xlsx");
|
||||
assembleResultWorkbook(task, result);
|
||||
progressHook.run();
|
||||
completedProgressUnits[0] = totalProgressUnits - 1;
|
||||
saveFileBuildProgress(task, job, totalProgressUnits, completedProgressUnits[0], "正在上传结果文件");
|
||||
fileResultMapper.updateById(result);
|
||||
saveFileBuildProgress(task, job, totalProgressUnits, totalProgressUnits, "结果文件已生成");
|
||||
}
|
||||
|
||||
private int countCozeWorkUnits(List<TaskChunkEntity> chunks, int batchSize) {
|
||||
if (chunks == null || chunks.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
for (TaskChunkEntity chunk : chunks) {
|
||||
Map<String, AppearancePatentResultRowDto> persistedRows = readChunkRows(chunk);
|
||||
if (persistedRows.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
int unresolved = pickGroupRepresentativesForCoze(persistedRows.values()).size();
|
||||
if (unresolved > 0) {
|
||||
total += Math.max(1, (unresolved + batchSize - 1) / batchSize);
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private void saveFileBuildProgress(FileTaskEntity task,
|
||||
TaskFileJobEntity job,
|
||||
int total,
|
||||
int completed,
|
||||
String message) {
|
||||
if (task == null || task.getId() == null || job == null || job.getId() == null) {
|
||||
return;
|
||||
}
|
||||
int safeTotal = Math.max(1, total);
|
||||
int safeCompleted = Math.max(0, Math.min(completed, safeTotal));
|
||||
taskProgressSnapshotService.save(
|
||||
task.getId(),
|
||||
MODULE_TYPE,
|
||||
STATUS_RUNNING,
|
||||
safeTotal,
|
||||
safeCompleted,
|
||||
0,
|
||||
job.getScopeKey(),
|
||||
message,
|
||||
Map.of("phase", "RESULT_FILE", "jobId", job.getId())
|
||||
);
|
||||
}
|
||||
|
||||
public void cleanupResultFileJob(TaskFileJobEntity job) {
|
||||
@@ -1045,6 +1102,14 @@ public class AppearancePatentTaskService {
|
||||
private void assembleResultWorkbook(FileTaskEntity task, FileResultEntity result) {
|
||||
AppearancePatentParsedPayloadDto parsed = readParsedPayload(task);
|
||||
Map<String, AppearancePatentResultRowDto> resultMap = loadPersistedResultRows(task.getId());
|
||||
long resolvedRows = parsed.getAllItems().stream()
|
||||
.filter(row -> findResultRow(row, resultMap) != null || findResultRowByAsin(row.getAsin(), resultMap) != null)
|
||||
.count();
|
||||
long reasonRows = resultMap.values().stream()
|
||||
.filter(this::hasReasonFields)
|
||||
.count();
|
||||
log.info("[appearance-patent] assemble workbook taskId={} parsedRows={} resultRows={} resolvedRows={} reasonRows={}",
|
||||
task.getId(), parsed.getAllItems().size(), resultMap.size(), resolvedRows, reasonRows);
|
||||
File outputDir = new File(storageProperties.getLocalTempDir(), "appearance-patent-result");
|
||||
if (!outputDir.exists() && !outputDir.mkdirs()) {
|
||||
throw new BusinessException("创建结果目录失败");
|
||||
@@ -1087,8 +1152,8 @@ public class AppearancePatentTaskService {
|
||||
headerStyle.setFont(font);
|
||||
|
||||
List<String> resultHeaders = new ArrayList<>(RESULT_HEADERS);
|
||||
resultHeaders.add(4, "title");
|
||||
resultHeaders.add(5, "url");
|
||||
resultHeaders.add(4, "标题");
|
||||
resultHeaders.add(5, "图片链接");
|
||||
Row header = sheet.createRow(0);
|
||||
for (int i = 0; i < resultHeaders.size(); i++) {
|
||||
Cell cell = header.createCell(i);
|
||||
@@ -1099,6 +1164,9 @@ public class AppearancePatentTaskService {
|
||||
int rowIndex = 1;
|
||||
for (AppearancePatentParsedRowVo parsedRow : parsed.getAllItems()) {
|
||||
AppearancePatentResultRowDto resultRow = findResultRow(parsedRow, resultMap);
|
||||
if (resultRow == null) {
|
||||
resultRow = findResultRowByAsin(parsedRow.getAsin(), resultMap);
|
||||
}
|
||||
String missingReason = "";
|
||||
if (resultRow == null) {
|
||||
missingReason = hasPromptFields(parsedRow) ? "未匹配到检测结果" : "未送检:缺少标题或图片";
|
||||
@@ -1162,12 +1230,28 @@ public class AppearancePatentTaskService {
|
||||
if (normalizedAsin.isBlank() || resultMap == null || resultMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
AppearancePatentResultRowDto fallback = null;
|
||||
for (AppearancePatentResultRowDto row : resultMap.values()) {
|
||||
if (row != null && normalizedAsin.equals(normalize(row.getAsin()).toUpperCase(Locale.ROOT))) {
|
||||
if (row == null || !normalizedAsin.equals(normalize(row.getAsin()).toUpperCase(Locale.ROOT))) {
|
||||
continue;
|
||||
}
|
||||
if (hasResolvedCozeFields(row) || hasReasonFields(row)) {
|
||||
return row;
|
||||
}
|
||||
if (fallback == null) {
|
||||
fallback = row;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private boolean hasReasonFields(AppearancePatentResultRowDto row) {
|
||||
if (row == null) {
|
||||
return false;
|
||||
}
|
||||
return !normalize(row.getAppearanceReason()).isBlank()
|
||||
|| !normalize(row.getPatentReason()).isBlank()
|
||||
|| !normalize(row.getTitleReason()).isBlank();
|
||||
}
|
||||
|
||||
private ParsedWorkbook parseWorkbook(File input, AppearancePatentSourceFileDto source) {
|
||||
@@ -1468,11 +1552,50 @@ public class AppearancePatentTaskService {
|
||||
vo.setFileReady(row.getResultFileUrl() != null && !row.getResultFileUrl().isBlank());
|
||||
if (job == null) {
|
||||
vo.setFileStatus(Boolean.TRUE.equals(vo.getFileReady()) ? "SUCCESS" : null);
|
||||
attachFileProgress(vo, row, job);
|
||||
return;
|
||||
}
|
||||
vo.setFileJobId(job.getId());
|
||||
vo.setFileStatus(job.getStatus());
|
||||
vo.setFileError(job.getErrorMessage());
|
||||
attachFileProgress(vo, row, job);
|
||||
}
|
||||
|
||||
private void attachFileProgress(AppearancePatentHistoryItemVo vo, FileResultEntity row, TaskFileJobEntity job) {
|
||||
if (row == null || row.getTaskId() == null) {
|
||||
return;
|
||||
}
|
||||
if (Boolean.TRUE.equals(vo.getFileReady())) {
|
||||
vo.setFileProgressCurrent(1);
|
||||
vo.setFileProgressTotal(1);
|
||||
vo.setFileProgressPercent(100);
|
||||
vo.setFileProgressMessage("结果文件已生成");
|
||||
return;
|
||||
}
|
||||
TaskProgressSnapshotEntity snapshot = taskProgressSnapshotService.find(row.getTaskId(), MODULE_TYPE);
|
||||
if (snapshot == null) {
|
||||
return;
|
||||
}
|
||||
int total = snapshot.getTotalCount() == null ? 0 : snapshot.getTotalCount();
|
||||
int current = snapshot.getSuccessCount() == null ? 0 : snapshot.getSuccessCount();
|
||||
if (total <= 0) {
|
||||
return;
|
||||
}
|
||||
current = Math.max(0, Math.min(current, total));
|
||||
int percent = Math.max(1, Math.min(99, (int) Math.floor(current * 100.0 / total)));
|
||||
if (job != null && STATUS_RUNNING.equals(job.getStatus())) {
|
||||
LocalDateTime baseTime = snapshot.getUpdatedAt() != null ? snapshot.getUpdatedAt() : job.getUpdatedAt();
|
||||
long elapsedSeconds = baseTime == null ? 0 : Math.max(0, Duration.between(baseTime, LocalDateTime.now()).getSeconds());
|
||||
if (current <= 0) {
|
||||
percent = Math.max(percent, Math.min(35, 8 + (int) (elapsedSeconds / 6)));
|
||||
} else if (current < total) {
|
||||
percent = Math.max(percent, Math.min(92, percent + (int) (elapsedSeconds / 10)));
|
||||
}
|
||||
}
|
||||
vo.setFileProgressCurrent(current);
|
||||
vo.setFileProgressTotal(total);
|
||||
vo.setFileProgressPercent(percent);
|
||||
vo.setFileProgressMessage(snapshot.getMessage());
|
||||
}
|
||||
|
||||
private String fmt(LocalDateTime t) {
|
||||
|
||||
Reference in New Issue
Block a user