完善多店铺开启多窗口、外观进度条等
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -279,16 +279,21 @@ public class BrandTaskService {
|
||||
throw new BusinessException("totalLines 超过原始文件行数: " + fileUrl);
|
||||
}
|
||||
BrandTaskStorageService.ChunkStoreResult storeResult = brandTaskStorageService.storeChunk(taskId, resultFile);
|
||||
BrandTaskStorageService.ChunkStoreResult refreshResult = brandTaskStorageService.refreshFileAggregate(taskId, fileUrl);
|
||||
BrandFileAggregateCacheDto aggregate = refreshResult.aggregate() == null ? storeResult.aggregate() : refreshResult.aggregate();
|
||||
finishedCount = Math.max(finishedCount, storeResult.finishedFiles());
|
||||
log.info("[brand-submit] taskId={} fileUrl={} chunk={}/{} stored={} newlyCompletedFile={} finishedFiles={}",
|
||||
finishedCount = Math.max(finishedCount, refreshResult.finishedFiles());
|
||||
log.info("[brand-submit] taskId={} fileUrl={} chunk={}/{} stored={} newlyCompletedFile={} finishedFiles={} receivedChunks={} processedLines={} totalLines={}",
|
||||
taskId,
|
||||
fileUrl,
|
||||
resultFile.getChunkIndex(),
|
||||
resultFile.getChunkTotal(),
|
||||
storeResult.stored(),
|
||||
storeResult.newlyCompletedFile(),
|
||||
finishedCount);
|
||||
BrandFileAggregateCacheDto aggregate = storeResult.aggregate();
|
||||
storeResult.newlyCompletedFile() || refreshResult.newlyCompletedFile(),
|
||||
finishedCount,
|
||||
aggregate == null ? 0 : defaultInteger(aggregate.getReceivedChunkCount()),
|
||||
aggregate == null ? 0 : defaultInteger(aggregate.getProcessedLineCount()),
|
||||
aggregate == null ? 0 : defaultInteger(aggregate.getTotalLines()));
|
||||
String fileName = blankToDefault(sourceFile.getOriginalFilename(), sourceFile.getFileUrl());
|
||||
int fileIndex = sourceIndexByUrl.getOrDefault(fileUrl, 0);
|
||||
int currentLine = aggregate == null ? 0 : defaultInteger(aggregate.getProcessedLineCount());
|
||||
|
||||
@@ -126,6 +126,7 @@ public class BrandTaskStorageService {
|
||||
if (existingChunk != null) {
|
||||
if (payloadHash.equals(existingChunk.getPayloadHash())) {
|
||||
BrandFileAggregateCacheDto aggregate = rebuildAggregate(taskId, scopeKey, scopeHash);
|
||||
saveAggregate(taskId, scopeKey, scopeHash, aggregate, now);
|
||||
return new ChunkStoreResult(false, false, countCompletedFiles(taskId), aggregate);
|
||||
}
|
||||
throw new BusinessException("Brand task chunk conflict: " + scopeKey + "#" + file.getChunkIndex());
|
||||
@@ -155,6 +156,7 @@ public class BrandTaskStorageService {
|
||||
if (concurrentChunk != null && payloadHash.equals(concurrentChunk.getPayloadHash())) {
|
||||
transientPayloadStorageService.deletePayloadIfPresent(storedPayload);
|
||||
BrandFileAggregateCacheDto aggregate = rebuildAggregate(taskId, scopeKey, scopeHash);
|
||||
saveAggregate(taskId, scopeKey, scopeHash, aggregate, now);
|
||||
return new ChunkStoreResult(false, false, countCompletedFiles(taskId), aggregate);
|
||||
}
|
||||
transientPayloadStorageService.deletePayloadIfPresent(storedPayload);
|
||||
@@ -218,6 +220,22 @@ public class BrandTaskStorageService {
|
||||
return count == null ? 0 : count.intValue();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ChunkStoreResult refreshFileAggregate(Long taskId, String fileUrl) {
|
||||
if (taskId == null || taskId <= 0 || isBlank(fileUrl)) {
|
||||
return new ChunkStoreResult(false, false, countCompletedFiles(taskId), null);
|
||||
}
|
||||
String scopeKey = normalize(fileUrl);
|
||||
String scopeHash = hash(scopeKey);
|
||||
BrandFileAggregateCacheDto aggregate = rebuildAggregate(taskId, scopeKey, scopeHash);
|
||||
if (aggregate == null) {
|
||||
return new ChunkStoreResult(false, false, countCompletedFiles(taskId), null);
|
||||
}
|
||||
boolean completed = Boolean.TRUE.equals(aggregate.getCompleted());
|
||||
saveAggregate(taskId, scopeKey, scopeHash, aggregate, LocalDateTime.now());
|
||||
return new ChunkStoreResult(false, completed, countCompletedFiles(taskId), aggregate);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteTaskData(Long taskId) {
|
||||
if (taskId == null || taskId <= 0) {
|
||||
|
||||
@@ -14,9 +14,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -27,6 +25,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api/dedupe")
|
||||
@@ -94,14 +94,14 @@ public class DedupeRunController {
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "返回结果文件流"),
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "结果文件不存在")
|
||||
})
|
||||
public ResponseEntity<Resource> download(
|
||||
public ResponseEntity<Void> download(
|
||||
@Parameter(description = "去重结果记录 ID", required = true) @PathVariable Long resultId,
|
||||
@Parameter(name = "user_id", description = "当前登录用户 ID", required = true, in = ParameterIn.QUERY)
|
||||
@RequestParam("user_id") Long userId) {
|
||||
Resource resource = dedupeRunService.getResultFile(resultId, userId);
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment")
|
||||
.body(resource);
|
||||
String downloadUrl = dedupeRunService.getResultDownloadUrl(resultId, userId);
|
||||
return ResponseEntity.status(302)
|
||||
.location(URI.create(downloadUrl))
|
||||
.header(HttpHeaders.CACHE_CONTROL, "no-store")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.WorkbookUtil;
|
||||
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
@@ -229,7 +227,7 @@ public class DedupeRunService {
|
||||
vo.setResultId(entity.getId());
|
||||
vo.setSourceFilename(entity.getSourceFilename());
|
||||
vo.setOutputFilename(entity.getResultFilename());
|
||||
vo.setDownloadUrl(null);
|
||||
vo.setDownloadUrl(entity.getResultFileUrl() == null ? null : ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl()));
|
||||
vo.setSuccess(entity.getSuccess() != null && entity.getSuccess() == 1);
|
||||
vo.setError(entity.getErrorMessage());
|
||||
return vo;
|
||||
@@ -245,16 +243,12 @@ public class DedupeRunService {
|
||||
fileResultMapper.deleteById(resultId);
|
||||
}
|
||||
|
||||
public Resource getResultFile(Long resultId, Long userId) {
|
||||
public String getResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || entity.getResultFileUrl() == null || !"DEDUPE".equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("结果文件不存在");
|
||||
}
|
||||
File file = new File(entity.getResultFileUrl());
|
||||
if (!file.exists()) {
|
||||
throw new BusinessException("结果文件不存在");
|
||||
}
|
||||
return new FileSystemResource(file);
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
}
|
||||
|
||||
private void cleanExcelByLegacyRules(File inputFile, File outputFile, List<String> selectedColumns,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -225,6 +225,27 @@ public class TaskFileJobService {
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteTaskJobs(Long taskId, String moduleType) {
|
||||
if (taskId == null || taskId <= 0 || moduleType == null || moduleType.isBlank()) {
|
||||
return;
|
||||
}
|
||||
taskFileJobMapper.delete(new LambdaQueryWrapper<TaskFileJobEntity>()
|
||||
.eq(TaskFileJobEntity::getTaskId, taskId)
|
||||
.eq(TaskFileJobEntity::getModuleType, moduleType));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteResultJobs(Long taskId, String moduleType, Long resultId) {
|
||||
if (taskId == null || taskId <= 0 || moduleType == null || moduleType.isBlank() || resultId == null || resultId <= 0) {
|
||||
return;
|
||||
}
|
||||
taskFileJobMapper.delete(new LambdaQueryWrapper<TaskFileJobEntity>()
|
||||
.eq(TaskFileJobEntity::getTaskId, taskId)
|
||||
.eq(TaskFileJobEntity::getModuleType, moduleType)
|
||||
.eq(TaskFileJobEntity::getResultId, resultId));
|
||||
}
|
||||
|
||||
private TaskFileJobEntity findJob(Long taskId, String moduleType, Long resultId, String jobType) {
|
||||
if (taskId == null || moduleType == null || moduleType.isBlank() || resultId == null || jobType == null || jobType.isBlank()) {
|
||||
return null;
|
||||
|
||||
@@ -84,6 +84,16 @@ public class TaskProgressSnapshotService {
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(Long taskId, String moduleType) {
|
||||
if (taskId == null || taskId <= 0 || isBlank(moduleType)) {
|
||||
return;
|
||||
}
|
||||
taskProgressSnapshotMapper.delete(new LambdaQueryWrapper<TaskProgressSnapshotEntity>()
|
||||
.eq(TaskProgressSnapshotEntity::getTaskId, taskId)
|
||||
.eq(TaskProgressSnapshotEntity::getModuleType, moduleType));
|
||||
}
|
||||
|
||||
private String writeJson(Object value) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(value);
|
||||
|
||||
@@ -94,6 +94,27 @@ public class TaskResultItemService {
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteResultItem(Long taskId, String moduleType, Long resultId) {
|
||||
if (taskId == null || taskId <= 0 || isBlank(moduleType) || resultId == null || resultId <= 0) {
|
||||
return;
|
||||
}
|
||||
List<TaskResultItemEntity> rows = taskResultItemMapper.selectList(new LambdaQueryWrapper<TaskResultItemEntity>()
|
||||
.select(TaskResultItemEntity::getId, TaskResultItemEntity::getPayloadJson)
|
||||
.eq(TaskResultItemEntity::getTaskId, taskId)
|
||||
.eq(TaskResultItemEntity::getModuleType, moduleType)
|
||||
.eq(TaskResultItemEntity::getResultId, resultId));
|
||||
if (rows != null) {
|
||||
for (TaskResultItemEntity row : rows) {
|
||||
transientPayloadStorageService.deletePayloadIfPresent(row.getPayloadJson());
|
||||
}
|
||||
}
|
||||
taskResultItemMapper.delete(new LambdaQueryWrapper<TaskResultItemEntity>()
|
||||
.eq(TaskResultItemEntity::getTaskId, taskId)
|
||||
.eq(TaskResultItemEntity::getModuleType, moduleType)
|
||||
.eq(TaskResultItemEntity::getResultId, resultId));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteTaskItems(Long taskId, String moduleType) {
|
||||
if (taskId == null || taskId <= 0 || isBlank(moduleType)) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS biz_patrol_delete_condition (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id BIGINT NOT NULL,
|
||||
condition_text VARCHAR(500) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_patrol_delete_condition_user_text (user_id, condition_text),
|
||||
KEY idx_patrol_delete_condition_user_id (user_id)
|
||||
);
|
||||
Reference in New Issue
Block a user