完善多店铺开启多窗口、外观进度条等

This commit is contained in:
super
2026-05-01 21:09:54 +08:00
parent 70658041a5
commit aece8123c3
25 changed files with 945 additions and 183 deletions
@@ -1,10 +1,12 @@
package com.nanri.aiimage.modules.patroldelete.controller;
import com.nanri.aiimage.common.api.ApiResponse;
import com.nanri.aiimage.modules.patroldelete.model.dto.PatrolDeleteConditionAddRequest;
import com.nanri.aiimage.modules.patroldelete.model.dto.PatrolDeleteCreateTaskRequest;
import com.nanri.aiimage.modules.patroldelete.model.dto.PatrolDeleteSubmitResultRequest;
import com.nanri.aiimage.modules.patroldelete.model.dto.PatrolDeleteTaskBatchRequest;
import com.nanri.aiimage.modules.patroldelete.model.vo.PatrolDeleteCreateTaskVo;
import com.nanri.aiimage.modules.patroldelete.model.vo.PatrolDeleteConditionVo;
import com.nanri.aiimage.modules.patroldelete.model.vo.PatrolDeleteHistoryVo;
import com.nanri.aiimage.modules.patroldelete.model.vo.PatrolDeleteTaskBatchVo;
import com.nanri.aiimage.modules.patroldelete.service.PatrolDeleteResolveService;
@@ -92,6 +94,32 @@ public class PatrolDeleteController {
return ApiResponse.success(null);
}
@GetMapping("/conditions")
@Operation(summary = "查询删除条件", description = "返回当前用户保存的巡店删除条件。")
public ApiResponse<List<PatrolDeleteConditionVo>> listConditions(
@Parameter(name = "user_id", description = "当前用户 ID", required = true, in = ParameterIn.QUERY, example = "1")
@RequestParam("user_id") Long userId) {
return ApiResponse.success(patrolDeleteResolveService.listConditions(userId));
}
@PostMapping("/conditions")
@Operation(summary = "新增删除条件", description = "保存一条当前用户可复用的巡店删除条件。")
public ApiResponse<PatrolDeleteConditionVo> addCondition(
@Valid @RequestBody PatrolDeleteConditionAddRequest request) {
return ApiResponse.success(patrolDeleteResolveService.addCondition(request));
}
@DeleteMapping("/conditions/{id}")
@Operation(summary = "删除删除条件", description = "删除当前用户名下的一条巡店删除条件。")
public ApiResponse<Void> deleteCondition(
@Parameter(description = "删除条件主键", example = "10")
@PathVariable Long id,
@Parameter(name = "user_id", description = "当前用户 ID", required = true, in = ParameterIn.QUERY, example = "1")
@RequestParam("user_id") Long userId) {
patrolDeleteResolveService.deleteCondition(userId, id);
return ApiResponse.success(null);
}
@PostMapping("/match-shops")
@Operation(summary = "批量匹配店铺", description = "根据店铺名批量匹配紫鸟店铺索引,返回是否命中、店铺 ID、平台、公司和匹配状态。")
public ApiResponse<ProductRiskMatchShopsVo> matchShops(
@@ -0,0 +1,9 @@
package com.nanri.aiimage.modules.patroldelete.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nanri.aiimage.modules.patroldelete.model.entity.PatrolDeleteConditionEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PatrolDeleteConditionMapper extends BaseMapper<PatrolDeleteConditionEntity> {
}
@@ -0,0 +1,22 @@
package com.nanri.aiimage.modules.patroldelete.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
@Schema(description = "巡店删除条件新增请求")
public class PatrolDeleteConditionAddRequest {
@NotNull
@JsonProperty("user_id")
@Schema(description = "当前用户 ID", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
@NotBlank
@JsonProperty("condition_text")
@Schema(description = "删除条件文本", example = "删除商品信息草稿", requiredMode = Schema.RequiredMode.REQUIRED)
private String conditionText;
}
@@ -0,0 +1,19 @@
package com.nanri.aiimage.modules.patroldelete.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("biz_patrol_delete_condition")
public class PatrolDeleteConditionEntity {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String conditionText;
private LocalDateTime createdAt;
}
@@ -0,0 +1,17 @@
package com.nanri.aiimage.modules.patroldelete.model.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class PatrolDeleteConditionVo {
private Long id;
@JsonProperty("conditionText")
private String conditionText;
private LocalDateTime createdAt;
}
@@ -2,8 +2,12 @@ package com.nanri.aiimage.modules.patroldelete.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.modules.patroldelete.mapper.PatrolDeleteConditionMapper;
import com.nanri.aiimage.modules.patroldelete.mapper.PatrolDeleteShopCandidateMapper;
import com.nanri.aiimage.modules.patroldelete.model.dto.PatrolDeleteConditionAddRequest;
import com.nanri.aiimage.modules.patroldelete.model.entity.PatrolDeleteConditionEntity;
import com.nanri.aiimage.modules.patroldelete.model.entity.PatrolDeleteShopCandidateEntity;
import com.nanri.aiimage.modules.patroldelete.model.vo.PatrolDeleteConditionVo;
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskCandidateAddRequest;
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskMatchShopsRequest;
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskCandidateVo;
@@ -27,6 +31,7 @@ import java.util.Objects;
public class PatrolDeleteResolveService {
private final PatrolDeleteShopCandidateMapper candidateMapper;
private final PatrolDeleteConditionMapper conditionMapper;
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
public List<ProductRiskCandidateVo> listCandidates(Long userId) {
@@ -127,6 +132,67 @@ public class PatrolDeleteResolveService {
return count == null ? 0L : count;
}
public List<PatrolDeleteConditionVo> listConditions(Long userId) {
validateUserId(userId);
List<PatrolDeleteConditionEntity> rows = conditionMapper.selectList(
new LambdaQueryWrapper<PatrolDeleteConditionEntity>()
.eq(PatrolDeleteConditionEntity::getUserId, userId)
.orderByDesc(PatrolDeleteConditionEntity::getId));
List<PatrolDeleteConditionVo> list = new ArrayList<>();
for (PatrolDeleteConditionEntity row : rows) {
list.add(toConditionVo(row));
}
return list;
}
@Transactional
public PatrolDeleteConditionVo addCondition(PatrolDeleteConditionAddRequest request) {
validateUserId(request.getUserId());
String text = normalizeConditionText(request.getConditionText());
if (text.isBlank()) {
throw new BusinessException("删除条件不能为空");
}
PatrolDeleteConditionEntity existing = conditionMapper.selectOne(
new LambdaQueryWrapper<PatrolDeleteConditionEntity>()
.eq(PatrolDeleteConditionEntity::getUserId, request.getUserId())
.eq(PatrolDeleteConditionEntity::getConditionText, text)
.last("limit 1"));
if (existing != null) {
return toConditionVo(existing);
}
PatrolDeleteConditionEntity entity = new PatrolDeleteConditionEntity();
entity.setUserId(request.getUserId());
entity.setConditionText(text);
entity.setCreatedAt(LocalDateTime.now());
conditionMapper.insert(entity);
return toConditionVo(entity);
}
@Transactional
public void deleteCondition(Long userId, Long id) {
validateUserId(userId);
if (id == null || id <= 0) {
throw new BusinessException("id 不合法");
}
PatrolDeleteConditionEntity row = conditionMapper.selectById(id);
if (row == null || !userId.equals(row.getUserId())) {
throw new BusinessException("记录不存在");
}
conditionMapper.deleteById(id);
}
private PatrolDeleteConditionVo toConditionVo(PatrolDeleteConditionEntity row) {
PatrolDeleteConditionVo vo = new PatrolDeleteConditionVo();
vo.setId(row.getId());
vo.setConditionText(row.getConditionText());
vo.setCreatedAt(row.getCreatedAt());
return vo;
}
private String normalizeConditionText(String value) {
return value == null ? "" : value.trim();
}
private ProductRiskShopQueueItemVo matchOneShop(String shopName) {
ProductRiskShopQueueItemVo item = new ProductRiskShopQueueItemVo();
item.setShopName(shopName);
@@ -149,11 +149,16 @@ public class PatrolDeleteTaskService {
Map<Long, FileTaskEntity> taskMap = loadTaskMap(entities);
Map<Long, Map<Long, PatrolDeleteResultItemVo>> snapshotMap = buildSnapshotMap(taskMap);
Map<Long, TaskFileJobEntity> jobMap = taskFileJobService.findAssembleJobsByResultIds(MODULE_TYPE, entities.stream()
.map(FileResultEntity::getId)
.filter(id -> id != null && id > 0)
.distinct()
.toList());
List<PatrolDeleteResultItemVo> items = new ArrayList<>();
for (FileResultEntity entity : entities) {
FileTaskEntity task = taskMap.get(entity.getTaskId());
PatrolDeleteResultItemVo snapshot = snapshotMap.getOrDefault(entity.getTaskId(), Map.of()).get(entity.getId());
items.add(toHistoryItem(entity, task, snapshot));
items.add(toHistoryItem(entity, task, snapshot, jobMap.get(entity.getId())));
}
vo.setItems(items);
return vo;
@@ -183,6 +188,11 @@ public class PatrolDeleteTaskService {
for (FileResultEntity row : rows) {
rowsByTaskId.computeIfAbsent(row.getTaskId(), ignored -> new ArrayList<>()).add(row);
}
Map<Long, TaskFileJobEntity> jobMap = taskFileJobService.findAssembleJobsByResultIds(MODULE_TYPE, rows.stream()
.map(FileResultEntity::getId)
.filter(id -> id != null && id > 0)
.distinct()
.toList());
for (Long taskId : normalizedTaskIds) {
FileTaskEntity task = taskMap.get(taskId);
@@ -195,7 +205,7 @@ public class PatrolDeleteTaskService {
batch.getMissingTaskIds().add(taskId);
continue;
}
batch.getItems().addAll(buildProgressItems(task, taskRows));
batch.getItems().addAll(buildProgressItems(task, taskRows, jobMap));
}
return batch;
}
@@ -252,8 +262,6 @@ public class PatrolDeleteTaskService {
task.setCreatedAt(now);
task.setUpdatedAt(now);
fileTaskMapper.insert(task);
taskCacheService.saveTaskCache(task);
taskCacheService.touchTaskHeartbeat(task.getId());
List<PatrolDeleteResultItemVo> snapshots = new ArrayList<>();
for (PatrolDeleteTaskItemDto item : uniqueItems) {
@@ -272,9 +280,6 @@ public class PatrolDeleteTaskService {
fileResultMapper.insert(result);
snapshots.add(toSnapshotVo(result, item, task.getStatus(), null));
}
persistTaskJson(task, uniqueItems, snapshots);
taskCacheService.saveTaskCache(task);
PatrolDeleteCreateTaskVo vo = new PatrolDeleteCreateTaskVo();
vo.setTaskId(task.getId());
vo.setItems(snapshots);
@@ -291,7 +296,8 @@ public class PatrolDeleteTaskService {
FileTaskEntity task = loadTaskForExecution(taskId);
if (task == null || !MODULE_TYPE.equals(task.getModuleType())) {
throw new BusinessException("任务不存在");
log.info("[patrol-delete] ignore result callback for deleted task taskId={}", taskId);
return;
}
if ("SUCCESS".equals(task.getStatus()) || "FAILED".equals(task.getStatus())) {
throw new BusinessException("任务已结束,拒绝重复提交");
@@ -419,6 +425,7 @@ public class PatrolDeleteTaskService {
fileResultMapper.delete(new LambdaQueryWrapper<FileResultEntity>()
.eq(FileResultEntity::getTaskId, taskId)
.eq(FileResultEntity::getModuleType, MODULE_TYPE));
cleanupTaskAuxiliaryData(taskId);
fileTaskMapper.deleteById(taskId);
taskCacheService.deleteTaskCache(taskId);
}
@@ -432,6 +439,7 @@ public class PatrolDeleteTaskService {
}
Long taskId = entity.getTaskId();
fileResultMapper.deleteById(resultId);
cleanupResultAuxiliaryData(taskId, resultId);
reconcileTaskAfterResultRemoval(taskId);
}
@@ -445,6 +453,7 @@ public class PatrolDeleteTaskService {
}
List<FileResultEntity> rows = listTaskRows(taskId);
if (rows.isEmpty()) {
cleanupTaskAuxiliaryData(taskId);
fileTaskMapper.deleteById(taskId);
taskCacheService.deleteTaskCache(taskId);
return;
@@ -455,6 +464,17 @@ public class PatrolDeleteTaskService {
taskCacheService.saveTaskCache(task);
}
private void cleanupTaskAuxiliaryData(Long taskId) {
taskResultItemService.deleteTaskItems(taskId, MODULE_TYPE);
taskProgressSnapshotService.delete(taskId, MODULE_TYPE);
taskFileJobService.deleteTaskJobs(taskId, MODULE_TYPE);
}
private void cleanupResultAuxiliaryData(Long taskId, Long resultId) {
taskResultItemService.deleteResultItem(taskId, MODULE_TYPE, resultId);
taskFileJobService.deleteResultJobs(taskId, MODULE_TYPE, resultId);
}
private long countTasks(Long userId, List<String> statuses) {
Long count = fileTaskMapper.selectCount(new LambdaQueryWrapper<FileTaskEntity>()
.eq(FileTaskEntity::getModuleType, MODULE_TYPE)
@@ -517,6 +537,10 @@ public class PatrolDeleteTaskService {
}
private PatrolDeleteResultItemVo toHistoryItem(FileResultEntity entity, FileTaskEntity task, PatrolDeleteResultItemVo snapshot) {
return toHistoryItem(entity, task, snapshot, null);
}
private PatrolDeleteResultItemVo toHistoryItem(FileResultEntity entity, FileTaskEntity task, PatrolDeleteResultItemVo snapshot, TaskFileJobEntity job) {
PatrolDeleteResultItemVo item = snapshot != null ? snapshot : new PatrolDeleteResultItemVo();
item.setResultId(entity.getId());
item.setTaskId(entity.getTaskId());
@@ -529,7 +553,7 @@ public class PatrolDeleteTaskService {
item.setFinishedAt(task != null ? task.getFinishedAt() : item.getFinishedAt());
item.setOutputFilename(firstNonBlank(item.getOutputFilename(), entity.getResultFilename()));
item.setDownloadUrl(null);
attachFileJobState(item, entity);
attachFileJobState(item, entity, job);
if (item.getCountrySections() == null) {
item.setCountrySections(new ArrayList<>());
}
@@ -540,7 +564,10 @@ public class PatrolDeleteTaskService {
}
private void attachFileJobState(PatrolDeleteResultItemVo item, FileResultEntity entity) {
TaskFileJobEntity job = taskFileJobService.findAssembleJob(entity.getTaskId(), MODULE_TYPE, entity.getId());
attachFileJobState(item, entity, taskFileJobService.findAssembleJob(entity.getTaskId(), MODULE_TYPE, entity.getId()));
}
private void attachFileJobState(PatrolDeleteResultItemVo item, FileResultEntity entity, TaskFileJobEntity job) {
item.setFileReady(!blank(entity.getResultFileUrl()));
if (job == null) {
item.setFileStatus(Boolean.TRUE.equals(item.getFileReady()) ? "SUCCESS" : null);
@@ -612,7 +639,7 @@ public class PatrolDeleteTaskService {
return list;
}
private List<PatrolDeleteResultItemVo> buildProgressItems(FileTaskEntity task, List<FileResultEntity> rows) {
private List<PatrolDeleteResultItemVo> buildProgressItems(FileTaskEntity task, List<FileResultEntity> rows, Map<Long, TaskFileJobEntity> jobMap) {
List<PatrolDeleteResultItemVo> list = new ArrayList<>();
for (FileResultEntity row : rows) {
PatrolDeleteResultItemVo item = new PatrolDeleteResultItemVo();
@@ -627,7 +654,7 @@ public class PatrolDeleteTaskService {
item.setFinishedAt(task == null ? null : task.getFinishedAt());
item.setOutputFilename(row.getResultFilename());
item.setDownloadUrl(null);
attachFileJobState(item, row);
attachFileJobState(item, row, jobMap == null ? null : jobMap.get(row.getId()));
list.add(item);
}
return list;