feat(web): 前端 SPA 化 + 工具页任务面板统一与历史批量删除
- SPA 化:22 个 MPA html 入口与 *-main.ts 合并为 index.html + vue-router(URL 无 .html 后缀), 页面跳转全部 router-link,/new_web_source/xxx.html 旧路径归一为 /xxx - 任务面板统一:共享 TaskCenterPanel/TaskItemCard/TaskStatCards/HistoryTaskLayer, 16 个工具页右侧统一为统计卡 + 当前任务 + 历史任务弹层(任务ID/开始/结束/状态必展示) - 历史记录支持单条删除 + 批量勾选删除(确认框/全选/失败提示) - Java 7 模块(dedupe/convert/split/productrisk/shopmatch/pricetrack/deletebrand) history 接口补齐任务时间字段(VO+Service,复用 biz_file_task 列) - 图片工作台/API 层(brand/permission/user)既有未提交改动一并提交
This commit is contained in:
+15
@@ -24,4 +24,19 @@ public class ConvertResultItemVo {
|
||||
|
||||
@Schema(description = "下载地址")
|
||||
private String downloadUrl;
|
||||
|
||||
@Schema(description = "所属任务ID(biz_file_task.id)")
|
||||
private Long taskId;
|
||||
|
||||
@Schema(description = "任务状态:PENDING / RUNNING / SUCCESS / FAILED")
|
||||
private String taskStatus;
|
||||
|
||||
@Schema(description = "任务创建时间(biz_file_task.created_at,格式化字符串)")
|
||||
private String createdAt;
|
||||
|
||||
@Schema(description = "任务开始时间(biz_file_task.created_at)")
|
||||
private String startedAt;
|
||||
|
||||
@Schema(description = "任务结束时间(biz_file_task.finished_at,未结束为 null)")
|
||||
private String finishedAt;
|
||||
}
|
||||
|
||||
+40
-3
@@ -201,12 +201,14 @@ public class ConvertRunService {
|
||||
}
|
||||
|
||||
public List<ConvertResultItemVo> listHistory(Long userId) {
|
||||
return fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
|
||||
List<FileResultEntity> entities = fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
|
||||
.eq(FileResultEntity::getModuleType, MODULE_TYPE)
|
||||
.eq(FileResultEntity::getUserId, userId)
|
||||
.orderByDesc(FileResultEntity::getCreatedAt)
|
||||
.last("limit 200"))
|
||||
.stream()
|
||||
.last("limit 200"));
|
||||
// 任务级信息(状态/开始/结束时间)来自 biz_file_task,按 task_id 批量联查
|
||||
Map<Long, FileTaskEntity> taskMap = loadHistoryTaskMap(entities);
|
||||
return entities.stream()
|
||||
.map(entity -> {
|
||||
ConvertResultItemVo vo = new ConvertResultItemVo();
|
||||
vo.setResultId(entity.getId());
|
||||
@@ -215,11 +217,46 @@ public class ConvertRunService {
|
||||
vo.setDownloadUrl(entity.getResultFileUrl() == null ? null : ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl()));
|
||||
vo.setSuccess(entity.getSuccess() != null && entity.getSuccess() == 1);
|
||||
vo.setError(entity.getErrorMessage());
|
||||
FileTaskEntity task = entity.getTaskId() == null ? null : taskMap.get(entity.getTaskId());
|
||||
vo.setTaskId(entity.getTaskId());
|
||||
vo.setTaskStatus(task == null ? null : task.getStatus());
|
||||
vo.setCreatedAt(fmt(task == null ? entity.getCreatedAt() : task.getCreatedAt()));
|
||||
// 任务开始时间复用 biz_file_task.created_at;缺 task 时回退到 result.createdAt 兜底
|
||||
vo.setStartedAt(fmt(task == null ? entity.getCreatedAt() : task.getCreatedAt()));
|
||||
vo.setFinishedAt(fmt(task == null ? null : task.getFinishedAt()));
|
||||
return vo;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
/** 批量加载转换结果关联的任务信息(列裁剪,只取状态与时间) */
|
||||
private Map<Long, FileTaskEntity> loadHistoryTaskMap(List<FileResultEntity> entities) {
|
||||
Map<Long, FileTaskEntity> taskMap = new LinkedHashMap<>();
|
||||
List<Long> taskIds = entities.stream()
|
||||
.map(FileResultEntity::getTaskId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (taskIds.isEmpty()) {
|
||||
return taskMap;
|
||||
}
|
||||
for (FileTaskEntity task : fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId,
|
||||
FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt,
|
||||
FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds))) {
|
||||
if (task != null) {
|
||||
taskMap.put(task.getId(), task);
|
||||
}
|
||||
}
|
||||
return taskMap;
|
||||
}
|
||||
|
||||
private static String fmt(LocalDateTime t) {
|
||||
return t == null ? null : t.toString();
|
||||
}
|
||||
|
||||
public void deleteHistory(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
|
||||
+15
@@ -27,4 +27,19 @@ public class DedupeResultItemVo {
|
||||
|
||||
@Schema(description = "结果文件大小(字节)")
|
||||
private Long resultFileSize;
|
||||
|
||||
@Schema(description = "所属任务ID(biz_file_task.id)")
|
||||
private Long taskId;
|
||||
|
||||
@Schema(description = "任务状态:PENDING / RUNNING / SUCCESS / FAILED")
|
||||
private String taskStatus;
|
||||
|
||||
@Schema(description = "结果记录创建时间(格式化字符串)")
|
||||
private String createdAt;
|
||||
|
||||
@Schema(description = "任务开始时间(biz_file_task.created_at)")
|
||||
private String startedAt;
|
||||
|
||||
@Schema(description = "任务结束时间(biz_file_task.finished_at,未结束为 null)")
|
||||
private String finishedAt;
|
||||
}
|
||||
|
||||
+40
-3
@@ -389,12 +389,14 @@ public class DedupeRunService {
|
||||
}
|
||||
|
||||
public List<DedupeResultItemVo> listHistory(Long userId) {
|
||||
return fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
|
||||
List<FileResultEntity> entities = fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
|
||||
.eq(FileResultEntity::getModuleType, "DEDUPE")
|
||||
.eq(FileResultEntity::getUserId, userId)
|
||||
.orderByDesc(FileResultEntity::getCreatedAt)
|
||||
.last("limit 50"))
|
||||
.stream()
|
||||
.last("limit 50"));
|
||||
// 任务级信息(状态/开始/结束时间)来自 biz_file_task,按 task_id 批量联查
|
||||
Map<Long, FileTaskEntity> taskMap = loadHistoryTaskMap(entities);
|
||||
return entities.stream()
|
||||
.map(entity -> {
|
||||
DedupeResultItemVo vo = new DedupeResultItemVo();
|
||||
vo.setResultId(entity.getId());
|
||||
@@ -403,11 +405,46 @@ public class DedupeRunService {
|
||||
vo.setDownloadUrl(entity.getResultFileUrl() == null ? null : ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl()));
|
||||
vo.setSuccess(entity.getSuccess() != null && entity.getSuccess() == 1);
|
||||
vo.setError(entity.getErrorMessage());
|
||||
FileTaskEntity task = entity.getTaskId() == null ? null : taskMap.get(entity.getTaskId());
|
||||
vo.setTaskId(entity.getTaskId());
|
||||
vo.setTaskStatus(task == null ? null : task.getStatus());
|
||||
vo.setCreatedAt(fmt(task == null ? entity.getCreatedAt() : task.getCreatedAt()));
|
||||
// 任务开始时间复用 biz_file_task.created_at;缺 task 时回退到 result.createdAt 兜底
|
||||
vo.setStartedAt(fmt(task == null ? entity.getCreatedAt() : task.getCreatedAt()));
|
||||
vo.setFinishedAt(fmt(task == null ? null : task.getFinishedAt()));
|
||||
return vo;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
/** 批量加载去重结果关联的任务信息(复用 appearancepatent 的列裁剪写法) */
|
||||
private Map<Long, FileTaskEntity> loadHistoryTaskMap(List<FileResultEntity> entities) {
|
||||
Map<Long, FileTaskEntity> taskMap = new LinkedHashMap<>();
|
||||
List<Long> taskIds = entities.stream()
|
||||
.map(FileResultEntity::getTaskId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (taskIds.isEmpty()) {
|
||||
return taskMap;
|
||||
}
|
||||
for (FileTaskEntity task : fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId,
|
||||
FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt,
|
||||
FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds))) {
|
||||
if (task != null) {
|
||||
taskMap.put(task.getId(), task);
|
||||
}
|
||||
}
|
||||
return taskMap;
|
||||
}
|
||||
|
||||
private static String fmt(LocalDateTime t) {
|
||||
return t == null ? null : t.toString();
|
||||
}
|
||||
|
||||
public void deleteHistory(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !"DEDUPE".equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
|
||||
+10
@@ -78,4 +78,14 @@ public class DeleteBrandResultItemVo {
|
||||
|
||||
@Schema(description = "由于列表来自 history 接口,增加所属任务的状态(RUNNING/SUCCESS/FAILED),协助前端准确定位执行中任务")
|
||||
private String taskStatus;
|
||||
|
||||
/**
|
||||
* 任务创建时间(biz_file_task.created_at,格式化字符串),列表接口返回。
|
||||
*/
|
||||
private String createdAt;
|
||||
|
||||
/**
|
||||
* 任务结束时间(biz_file_task.finished_at,未结束为 null),列表接口返回。
|
||||
*/
|
||||
private String finishedAt;
|
||||
}
|
||||
|
||||
+9
@@ -293,6 +293,7 @@ public class DeleteBrandRunService {
|
||||
List<FileTaskEntity> recentTouchedTasks = fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId,
|
||||
FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt,
|
||||
FileTaskEntity::getUpdatedAt,
|
||||
FileTaskEntity::getFinishedAt)
|
||||
.eq(FileTaskEntity::getModuleType, MODULE_TYPE)
|
||||
@@ -375,6 +376,9 @@ public class DeleteBrandRunService {
|
||||
// 真实补充
|
||||
if (entity.getTaskId() != null) {
|
||||
item.setTaskStatus(statusByTaskId.get(entity.getTaskId()));
|
||||
FileTaskEntity taskEntity = taskById.get(entity.getTaskId());
|
||||
item.setCreatedAt(fmt(taskEntity == null ? null : taskEntity.getCreatedAt()));
|
||||
item.setFinishedAt(fmt(taskEntity == null ? null : taskEntity.getFinishedAt()));
|
||||
List<DeleteBrandResultItemVo> taskItems = null;
|
||||
if (taskItems != null && entity.getSourceFilename() != null && !entity.getSourceFilename().isBlank()) {
|
||||
for (DeleteBrandResultItemVo candidate : taskItems) {
|
||||
@@ -1234,6 +1238,7 @@ public class DeleteBrandRunService {
|
||||
tasks.addAll(fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId,
|
||||
FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt,
|
||||
FileTaskEntity::getUpdatedAt,
|
||||
FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds.subList(start, end))));
|
||||
@@ -1282,6 +1287,10 @@ public class DeleteBrandRunService {
|
||||
return Math.max(0L, (endExclusive - startInclusive) / 1_000_000L);
|
||||
}
|
||||
|
||||
private static String fmt(LocalDateTime t) {
|
||||
return t == null ? null : t.toString();
|
||||
}
|
||||
|
||||
@Transactional(propagation = org.springframework.transaction.annotation.Propagation.NEVER)
|
||||
public void submitResult(Long taskId, DeleteBrandSubmitResultRequest request) {
|
||||
if (taskId == null || taskId <= 0) {
|
||||
|
||||
+10
@@ -62,4 +62,14 @@ public class PriceTrackResultItemVo {
|
||||
|
||||
@Schema(description = "所属轮次,1-based", example = "2")
|
||||
private Integer roundIndex;
|
||||
|
||||
/**
|
||||
* 任务创建时间(biz_file_task.created_at,格式化字符串),列表接口返回。
|
||||
*/
|
||||
private String createdAt;
|
||||
|
||||
/**
|
||||
* 任务结束时间(biz_file_task.finished_at,未结束为 null),列表接口返回。
|
||||
*/
|
||||
private String finishedAt;
|
||||
}
|
||||
|
||||
+26
-9
@@ -142,7 +142,7 @@ public class PriceTrackTaskService {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private List<FileTaskEntity> selectTaskStatusesByIdsInBatches(List<Long> taskIds) {
|
||||
private List<FileTaskEntity> selectTaskHistoryFieldsByIdsInBatches(List<Long> taskIds) {
|
||||
List<FileTaskEntity> tasks = new ArrayList<>();
|
||||
if (taskIds == null || taskIds.isEmpty()) {
|
||||
return tasks;
|
||||
@@ -151,7 +151,8 @@ public class PriceTrackTaskService {
|
||||
for (int start = 0; start < taskIds.size(); start += batchSize) {
|
||||
int end = Math.min(start + batchSize, taskIds.size());
|
||||
tasks.addAll(fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId, FileTaskEntity::getStatus)
|
||||
.select(FileTaskEntity::getId, FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt, FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds.subList(start, end))));
|
||||
}
|
||||
return tasks;
|
||||
@@ -208,16 +209,16 @@ public class PriceTrackTaskService {
|
||||
userId, elapsedMs(startedAt, resultRowsLoadedAt), elapsedMs(startedAt, resultRowsLoadedAt));
|
||||
return vo;
|
||||
}
|
||||
Map<Long, String> statusByTaskId = new LinkedHashMap<>();
|
||||
Map<Long, FileTaskEntity> taskById = new LinkedHashMap<>();
|
||||
List<Long> taskIds = entities.stream()
|
||||
.map(FileResultEntity::getTaskId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (!taskIds.isEmpty()) {
|
||||
List<FileTaskEntity> tasks = selectTaskStatusesByIdsInBatches(taskIds);
|
||||
List<FileTaskEntity> tasks = selectTaskHistoryFieldsByIdsInBatches(taskIds);
|
||||
for (FileTaskEntity t : tasks) {
|
||||
if (t != null) statusByTaskId.put(t.getId(), t.getStatus());
|
||||
if (t != null) taskById.put(t.getId(), t);
|
||||
}
|
||||
}
|
||||
long tasksLoadedAt = System.nanoTime();
|
||||
@@ -229,14 +230,14 @@ public class PriceTrackTaskService {
|
||||
long jobsLoadedAt = System.nanoTime();
|
||||
List<PriceTrackResultItemVo> items = new ArrayList<>();
|
||||
for (FileResultEntity entity : entities) {
|
||||
items.add(toHistoryItemVo(entity, statusByTaskId.get(entity.getTaskId()), jobMap.get(entity.getId()), false));
|
||||
items.add(toHistoryItemVo(entity, taskById.get(entity.getTaskId()), jobMap.get(entity.getId()), false));
|
||||
}
|
||||
vo.setItems(items);
|
||||
long finishedAt = System.nanoTime();
|
||||
log.info("[price-track] history timing userId={} rows={} tasks={} jobs={} totalMs={} resultQueryMs={} taskQueryMs={} jobQueryMs={} buildMs={}",
|
||||
userId,
|
||||
entities.size(),
|
||||
statusByTaskId.size(),
|
||||
taskById.size(),
|
||||
jobMap.size(),
|
||||
elapsedMs(startedAt, finishedAt),
|
||||
elapsedMs(startedAt, resultRowsLoadedAt),
|
||||
@@ -1720,16 +1721,32 @@ public class PriceTrackTaskService {
|
||||
}
|
||||
|
||||
private PriceTrackResultItemVo toHistoryItemVo(FileResultEntity entity, String taskStatus) {
|
||||
return toHistoryItemVo(entity, taskStatus, null, true);
|
||||
// 快照路径:仅有任务状态字符串,无任务实体可提供时间字段
|
||||
return buildHistoryItemVo(entity, taskStatus, null, null, true);
|
||||
}
|
||||
|
||||
private PriceTrackResultItemVo toHistoryItemVo(FileResultEntity entity, FileTaskEntity task) {
|
||||
return buildHistoryItemVo(entity, task == null ? null : task.getStatus(), task, null, true);
|
||||
}
|
||||
|
||||
private PriceTrackResultItemVo toHistoryItemVo(FileResultEntity entity, String taskStatus, TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
return buildHistoryItemVo(entity, taskStatus, null, job, mergeRequestFields);
|
||||
}
|
||||
|
||||
private PriceTrackResultItemVo toHistoryItemVo(FileResultEntity entity, FileTaskEntity task, TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
return buildHistoryItemVo(entity, task == null ? null : task.getStatus(), task, job, mergeRequestFields);
|
||||
}
|
||||
|
||||
private PriceTrackResultItemVo buildHistoryItemVo(FileResultEntity entity, String taskStatus, FileTaskEntity task,
|
||||
TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
PriceTrackResultItemVo vo = new PriceTrackResultItemVo();
|
||||
vo.setResultId(entity.getId());
|
||||
vo.setTaskId(entity.getTaskId());
|
||||
vo.setShopName(entity.getSourceFilename());
|
||||
vo.setShopId(entity.getSourceFileUrl());
|
||||
vo.setTaskStatus(taskStatus);
|
||||
vo.setCreatedAt(fmt(task == null ? null : task.getCreatedAt()));
|
||||
vo.setFinishedAt(fmt(task == null ? null : task.getFinishedAt()));
|
||||
boolean ok = entity.getSuccess() != null && entity.getSuccess() == 1;
|
||||
vo.setSuccess(ok);
|
||||
vo.setError(entity.getErrorMessage());
|
||||
@@ -1767,7 +1784,7 @@ public class PriceTrackTaskService {
|
||||
.eq(FileResultEntity::getModuleType, MODULE_TYPE)
|
||||
.orderByAsc(FileResultEntity::getId));
|
||||
for (FileResultEntity fr : rows) {
|
||||
detail.getItems().add(toHistoryItemVo(fr, task.getStatus()));
|
||||
detail.getItems().add(toHistoryItemVo(fr, task));
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
+8
@@ -79,4 +79,12 @@ public class ProductRiskResultItemVo {
|
||||
@JsonAlias("skip_asin_details_by_country")
|
||||
@JsonProperty("skipAsinDetailsByCountry")
|
||||
private Map<String, List<SkipPriceAsinDetailDto>> skipAsinDetailsByCountry = new LinkedHashMap<>();
|
||||
|
||||
@JsonProperty("createdAt")
|
||||
@Schema(description = "任务创建时间(biz_file_task.created_at,格式化字符串)")
|
||||
private String createdAt;
|
||||
|
||||
@JsonProperty("finishedAt")
|
||||
@Schema(description = "任务结束时间(biz_file_task.finished_at,未结束为 null)")
|
||||
private String finishedAt;
|
||||
}
|
||||
|
||||
+26
-9
@@ -133,7 +133,7 @@ public class ProductRiskTaskService {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private List<FileTaskEntity> selectTaskStatusesByIdsInBatches(List<Long> taskIds) {
|
||||
private List<FileTaskEntity> selectTaskHistoryFieldsByIdsInBatches(List<Long> taskIds) {
|
||||
List<FileTaskEntity> tasks = new ArrayList<>();
|
||||
if (taskIds == null || taskIds.isEmpty()) {
|
||||
return tasks;
|
||||
@@ -142,7 +142,8 @@ public class ProductRiskTaskService {
|
||||
for (int start = 0; start < taskIds.size(); start += batchSize) {
|
||||
int end = Math.min(start + batchSize, taskIds.size());
|
||||
tasks.addAll(fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId, FileTaskEntity::getStatus)
|
||||
.select(FileTaskEntity::getId, FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt, FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds.subList(start, end))));
|
||||
}
|
||||
return tasks;
|
||||
@@ -202,17 +203,17 @@ public class ProductRiskTaskService {
|
||||
userId, elapsedMs(startedAt, resultRowsLoadedAt), elapsedMs(startedAt, resultRowsLoadedAt));
|
||||
return vo;
|
||||
}
|
||||
Map<Long, String> statusByTaskId = new LinkedHashMap<>();
|
||||
Map<Long, FileTaskEntity> taskById = new LinkedHashMap<>();
|
||||
List<Long> taskIds = entities.stream()
|
||||
.map(FileResultEntity::getTaskId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (!taskIds.isEmpty()) {
|
||||
List<FileTaskEntity> tasks = selectTaskStatusesByIdsInBatches(taskIds);
|
||||
List<FileTaskEntity> tasks = selectTaskHistoryFieldsByIdsInBatches(taskIds);
|
||||
for (FileTaskEntity t : tasks) {
|
||||
if (t != null) {
|
||||
statusByTaskId.put(t.getId(), t.getStatus());
|
||||
taskById.put(t.getId(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,14 +226,14 @@ public class ProductRiskTaskService {
|
||||
long jobsLoadedAt = System.nanoTime();
|
||||
List<ProductRiskResultItemVo> items = new ArrayList<>();
|
||||
for (FileResultEntity entity : entities) {
|
||||
items.add(toHistoryItemVo(entity, statusByTaskId.get(entity.getTaskId()), jobMap.get(entity.getId()), false));
|
||||
items.add(toHistoryItemVo(entity, taskById.get(entity.getTaskId()), jobMap.get(entity.getId()), false));
|
||||
}
|
||||
vo.setItems(items);
|
||||
long finishedAt = System.nanoTime();
|
||||
log.info("[product-risk] history timing userId={} rows={} tasks={} jobs={} totalMs={} resultQueryMs={} taskQueryMs={} jobQueryMs={} buildMs={}",
|
||||
userId,
|
||||
entities.size(),
|
||||
statusByTaskId.size(),
|
||||
taskById.size(),
|
||||
jobMap.size(),
|
||||
elapsedMs(startedAt, finishedAt),
|
||||
elapsedMs(startedAt, resultRowsLoadedAt),
|
||||
@@ -999,7 +1000,7 @@ public class ProductRiskTaskService {
|
||||
.eq(FileResultEntity::getModuleType, MODULE_TYPE)
|
||||
.orderByAsc(FileResultEntity::getId));
|
||||
for (FileResultEntity fr : rows) {
|
||||
detail.getItems().add(toHistoryItemVo(fr, task.getStatus()));
|
||||
detail.getItems().add(toHistoryItemVo(fr, task));
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
@@ -1048,16 +1049,32 @@ public class ProductRiskTaskService {
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, String taskStatus) {
|
||||
return toHistoryItemVo(entity, taskStatus, null, true);
|
||||
// 快照路径:仅有任务状态字符串,无任务实体可提供时间字段
|
||||
return buildHistoryItemVo(entity, taskStatus, null, null, true);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, FileTaskEntity task) {
|
||||
return buildHistoryItemVo(entity, task == null ? null : task.getStatus(), task, null, true);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, String taskStatus, TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
return buildHistoryItemVo(entity, taskStatus, null, job, mergeRequestFields);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, FileTaskEntity task, TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
return buildHistoryItemVo(entity, task == null ? null : task.getStatus(), task, job, mergeRequestFields);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo buildHistoryItemVo(FileResultEntity entity, String taskStatus, FileTaskEntity task,
|
||||
TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
ProductRiskResultItemVo vo = new ProductRiskResultItemVo();
|
||||
vo.setResultId(entity.getId());
|
||||
vo.setTaskId(entity.getTaskId());
|
||||
vo.setShopName(entity.getSourceFilename());
|
||||
vo.setShopId(entity.getSourceFileUrl());
|
||||
vo.setTaskStatus(taskStatus);
|
||||
vo.setCreatedAt(fmt(task == null ? null : task.getCreatedAt()));
|
||||
vo.setFinishedAt(fmt(task == null ? null : task.getFinishedAt()));
|
||||
boolean ok = entity.getSuccess() != null && entity.getSuccess() == 1;
|
||||
vo.setSuccess(ok);
|
||||
vo.setError(entity.getErrorMessage());
|
||||
|
||||
+26
-9
@@ -168,7 +168,7 @@ public class ShopMatchTaskService {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private List<FileTaskEntity> selectTaskStatusesByIdsInBatches(List<Long> taskIds) {
|
||||
private List<FileTaskEntity> selectTaskHistoryFieldsByIdsInBatches(List<Long> taskIds) {
|
||||
List<FileTaskEntity> tasks = new ArrayList<>();
|
||||
if (taskIds == null || taskIds.isEmpty()) {
|
||||
return tasks;
|
||||
@@ -177,7 +177,8 @@ public class ShopMatchTaskService {
|
||||
for (int start = 0; start < taskIds.size(); start += batchSize) {
|
||||
int end = Math.min(start + batchSize, taskIds.size());
|
||||
tasks.addAll(fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId, FileTaskEntity::getStatus)
|
||||
.select(FileTaskEntity::getId, FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt, FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds.subList(start, end))));
|
||||
}
|
||||
return tasks;
|
||||
@@ -237,16 +238,16 @@ public class ShopMatchTaskService {
|
||||
userId, elapsedMs(startedAt, resultRowsLoadedAt), elapsedMs(startedAt, resultRowsLoadedAt));
|
||||
return vo;
|
||||
}
|
||||
Map<Long, String> statusByTaskId = new LinkedHashMap<>();
|
||||
Map<Long, FileTaskEntity> taskById = new LinkedHashMap<>();
|
||||
List<Long> taskIds = entities.stream()
|
||||
.map(FileResultEntity::getTaskId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (!taskIds.isEmpty()) {
|
||||
for (FileTaskEntity task : selectTaskStatusesByIdsInBatches(taskIds)) {
|
||||
for (FileTaskEntity task : selectTaskHistoryFieldsByIdsInBatches(taskIds)) {
|
||||
if (task != null) {
|
||||
statusByTaskId.put(task.getId(), task.getStatus());
|
||||
taskById.put(task.getId(), task);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,14 +260,14 @@ public class ShopMatchTaskService {
|
||||
long jobsLoadedAt = System.nanoTime();
|
||||
List<ProductRiskResultItemVo> items = new ArrayList<>();
|
||||
for (FileResultEntity entity : entities) {
|
||||
items.add(toHistoryItemVo(entity, statusByTaskId.get(entity.getTaskId()), jobMap.get(entity.getId()), false));
|
||||
items.add(toHistoryItemVo(entity, taskById.get(entity.getTaskId()), jobMap.get(entity.getId()), false));
|
||||
}
|
||||
vo.setItems(items);
|
||||
long finishedAt = System.nanoTime();
|
||||
log.info("[shop-match] history timing userId={} rows={} tasks={} jobs={} totalMs={} resultQueryMs={} taskQueryMs={} jobQueryMs={} buildMs={}",
|
||||
userId,
|
||||
entities.size(),
|
||||
statusByTaskId.size(),
|
||||
taskById.size(),
|
||||
jobMap.size(),
|
||||
elapsedMs(startedAt, finishedAt),
|
||||
elapsedMs(startedAt, resultRowsLoadedAt),
|
||||
@@ -963,7 +964,7 @@ public class ShopMatchTaskService {
|
||||
.eq(FileResultEntity::getModuleType, MODULE_TYPE)
|
||||
.orderByAsc(FileResultEntity::getId));
|
||||
for (FileResultEntity row : rows) {
|
||||
detail.getItems().add(toHistoryItemVo(row, task.getStatus()));
|
||||
detail.getItems().add(toHistoryItemVo(row, task));
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
@@ -1031,16 +1032,32 @@ public class ShopMatchTaskService {
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, String taskStatus) {
|
||||
return toHistoryItemVo(entity, taskStatus, null, true);
|
||||
// 快照路径:仅有任务状态字符串,无任务实体可提供时间字段
|
||||
return buildHistoryItemVo(entity, taskStatus, null, null, true);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, FileTaskEntity task) {
|
||||
return buildHistoryItemVo(entity, task == null ? null : task.getStatus(), task, null, true);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, String taskStatus, TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
return buildHistoryItemVo(entity, taskStatus, null, job, mergeRequestFields);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo toHistoryItemVo(FileResultEntity entity, FileTaskEntity task, TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
return buildHistoryItemVo(entity, task == null ? null : task.getStatus(), task, job, mergeRequestFields);
|
||||
}
|
||||
|
||||
private ProductRiskResultItemVo buildHistoryItemVo(FileResultEntity entity, String taskStatus, FileTaskEntity task,
|
||||
TaskFileJobEntity job, boolean mergeRequestFields) {
|
||||
ProductRiskResultItemVo vo = new ProductRiskResultItemVo();
|
||||
vo.setResultId(entity.getId());
|
||||
vo.setTaskId(entity.getTaskId());
|
||||
vo.setShopName(entity.getSourceFilename());
|
||||
vo.setShopId(entity.getSourceFileUrl());
|
||||
vo.setTaskStatus(taskStatus);
|
||||
vo.setCreatedAt(fmt(task == null ? null : task.getCreatedAt()));
|
||||
vo.setFinishedAt(fmt(task == null ? null : task.getFinishedAt()));
|
||||
boolean success = entity.getSuccess() != null && entity.getSuccess() == 1;
|
||||
vo.setSuccess(success);
|
||||
vo.setError(entity.getErrorMessage());
|
||||
|
||||
+15
@@ -35,4 +35,19 @@ public class SplitResultItemVo {
|
||||
|
||||
@Schema(description = "压缩包内文件列表")
|
||||
private List<SplitArchiveEntryVo> entries;
|
||||
|
||||
@Schema(description = "所属任务ID(biz_file_task.id)")
|
||||
private Long taskId;
|
||||
|
||||
@Schema(description = "任务状态:PENDING / RUNNING / SUCCESS / FAILED")
|
||||
private String taskStatus;
|
||||
|
||||
@Schema(description = "任务创建时间(biz_file_task.created_at,格式化字符串)")
|
||||
private String createdAt;
|
||||
|
||||
@Schema(description = "任务开始时间(biz_file_task.created_at)")
|
||||
private String startedAt;
|
||||
|
||||
@Schema(description = "任务结束时间(biz_file_task.finished_at,未结束为 null)")
|
||||
private String finishedAt;
|
||||
}
|
||||
|
||||
+41
-3
@@ -29,6 +29,7 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -161,12 +162,14 @@ public class SplitRunService {
|
||||
}
|
||||
|
||||
public List<SplitResultItemVo> listHistory(Long userId) {
|
||||
return fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
|
||||
List<FileResultEntity> entities = fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
|
||||
.eq(FileResultEntity::getModuleType, MODULE_TYPE)
|
||||
.eq(FileResultEntity::getUserId, userId)
|
||||
.orderByDesc(FileResultEntity::getCreatedAt)
|
||||
.last("limit 100"))
|
||||
.stream()
|
||||
.last("limit 100"));
|
||||
// 任务级信息(状态/开始/结束时间)来自 biz_file_task,按 task_id 批量联查
|
||||
Map<Long, FileTaskEntity> taskMap = loadHistoryTaskMap(entities);
|
||||
return entities.stream()
|
||||
.map(entity -> {
|
||||
SplitResultItemVo vo = new SplitResultItemVo();
|
||||
vo.setResultId(entity.getId());
|
||||
@@ -178,11 +181,46 @@ public class SplitRunService {
|
||||
vo.setError(entity.getErrorMessage());
|
||||
vo.setEntryCount(0);
|
||||
vo.setEntries(new ArrayList<>());
|
||||
FileTaskEntity task = entity.getTaskId() == null ? null : taskMap.get(entity.getTaskId());
|
||||
vo.setTaskId(entity.getTaskId());
|
||||
vo.setTaskStatus(task == null ? null : task.getStatus());
|
||||
vo.setCreatedAt(fmt(task == null ? entity.getCreatedAt() : task.getCreatedAt()));
|
||||
// 任务开始时间复用 biz_file_task.created_at;缺 task 时回退到 result.createdAt 兜底
|
||||
vo.setStartedAt(fmt(task == null ? entity.getCreatedAt() : task.getCreatedAt()));
|
||||
vo.setFinishedAt(fmt(task == null ? null : task.getFinishedAt()));
|
||||
return vo;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
/** 批量加载拆分结果关联的任务信息(列裁剪,只取状态与时间) */
|
||||
private Map<Long, FileTaskEntity> loadHistoryTaskMap(List<FileResultEntity> entities) {
|
||||
Map<Long, FileTaskEntity> taskMap = new LinkedHashMap<>();
|
||||
List<Long> taskIds = entities.stream()
|
||||
.map(FileResultEntity::getTaskId)
|
||||
.filter(id -> id != null && id > 0)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (taskIds.isEmpty()) {
|
||||
return taskMap;
|
||||
}
|
||||
for (FileTaskEntity task : fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.select(FileTaskEntity::getId,
|
||||
FileTaskEntity::getStatus,
|
||||
FileTaskEntity::getCreatedAt,
|
||||
FileTaskEntity::getFinishedAt)
|
||||
.in(FileTaskEntity::getId, taskIds))) {
|
||||
if (task != null) {
|
||||
taskMap.put(task.getId(), task);
|
||||
}
|
||||
}
|
||||
return taskMap;
|
||||
}
|
||||
|
||||
private static String fmt(LocalDateTime t) {
|
||||
return t == null ? null : t.toString();
|
||||
}
|
||||
|
||||
public void deleteHistory(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
|
||||
Reference in New Issue
Block a user