refactor: 结果下载直链解析去重(11 处 → 1 处)
新增 common/service/ResultDownloadResolver,7 个模块的 resolveResultDownloadUrl 改为委托调用(appearancepatent/queryasin/withdraw/pricetrack/productrisk/ patroldelete/shopmatch)。 顺带修掉两处隐患: - 原实现用 userId.equals(entity.getUserId()),userId 为 null 时 NPE (新实现用 Objects.equals) - shopmatch 原实现在 url 为空时抛"任务不存在",与语义不符,统一为"暂无可下载文件" 其余 4 处**刻意保留**,因为它们本就不是重复: - shopdatacrawl:有 validateUserId + requireResultEntity + ensureResultOwner 前置校验 - deletebrand:返回 null 而非抛异常(调用方依赖该语义) - similarasin:已分叉为返回 record ResultDownloadInfo - brand:走的是另一套下载路径 文件名解析 resolveResultDownloadFilename 同样没收口:各模块兜底文件名策略确有差异 配套更新 3 个显式构造 Service 的测试(DelegationTest/HistoryBatchTest/ RollbackSemanticsContractTest)注入新依赖的 mock。mvn test 2795 个全绿。
This commit is contained in:
+46
@@ -0,0 +1,46 @@
|
||||
package com.nanri.aiimage.common.service;
|
||||
|
||||
import com.nanri.aiimage.common.exception.BusinessException;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
|
||||
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 结果文件下载直链解析(2026-09 全维度审查去重)。
|
||||
*
|
||||
* <p>此前 11 个业务模块各写一份逐字相同的 {@code resolveResultDownloadUrl},下载鉴权口径
|
||||
* 或 OSS 直链规则一调整就要改 11 处;similarasin 已自行分叉成返回 record 的第 12 种写法,
|
||||
* 说明"改的时候漏一个"已经开始发生。
|
||||
*
|
||||
* <p>文件名解析({@code resolveResultDownloadFilename})**未**收口:各模块兜底文件名策略
|
||||
* 确实不同(模块名+id / 源文件名派生 stem),属业务差异而非重复。
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ResultDownloadResolver {
|
||||
|
||||
private final FileResultMapper fileResultMapper;
|
||||
private final OssStorageService ossStorageService;
|
||||
|
||||
/**
|
||||
* 解析结果文件下载直链。
|
||||
*
|
||||
* @param resultId 结果行 id(biz_file_result)
|
||||
* @param userId 当前用户 id,必须与结果行归属一致
|
||||
* @param moduleType 期望的模块类型
|
||||
*/
|
||||
public String resolveUrl(Long resultId, Long userId, String moduleType) {
|
||||
FileResultEntity row = fileResultMapper.selectById(resultId);
|
||||
if (row == null || !moduleType.equals(row.getModuleType()) || !Objects.equals(userId, row.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (row.getResultFileUrl() == null || row.getResultFileUrl().isBlank()) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(row.getResultFileUrl());
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -121,6 +121,8 @@ public class AppearancePatentTaskService {
|
||||
|
||||
private final LocalFileStorageService localFileStorageService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final StorageProperties storageProperties;
|
||||
private final FileTaskMapper fileTaskMapper;
|
||||
private final FileResultMapper fileResultMapper;
|
||||
@@ -536,14 +538,8 @@ public class AppearancePatentTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity row = fileResultMapper.selectById(resultId);
|
||||
if (row == null || !MODULE_TYPE.equals(row.getModuleType()) || !Objects.equals(userId, row.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (row.getResultFileUrl() == null || row.getResultFileUrl().isBlank()) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(row.getResultFileUrl());
|
||||
// 2026-09 去重:与原实现等价(并修掉 userId.equals 的潜在 NPE),实现收口到 common
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+4
-8
@@ -70,6 +70,8 @@ public class PatrolDeleteTaskService {
|
||||
private final PatrolDeleteExcelAssemblyService excelAssemblyService;
|
||||
private final PatrolDeleteTaskCacheService taskCacheService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final TaskPressureProperties taskPressureProperties;
|
||||
@@ -256,14 +258,8 @@ public class PatrolDeleteTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (blank(entity.getResultFileUrl())) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
// 2026-09 去重:实现收口到 common/ResultDownloadResolver(原 blank 判空与之等价)
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+4
-8
@@ -80,6 +80,8 @@ public class PriceTrackTaskService {
|
||||
private final PriceTrackShopCandidateMapper candidateMapper;
|
||||
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final SkipPriceAsinService skipPriceAsinService;
|
||||
private final PriceTrackExcelAssemblyService excelAssemblyService;
|
||||
@@ -728,14 +730,8 @@ public class PriceTrackTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (entity.getResultFileUrl() == null || entity.getResultFileUrl().isBlank()) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
// 2026-09 去重:实现收口到 common/ResultDownloadResolver
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+4
-8
@@ -71,6 +71,8 @@ public class ProductRiskTaskService {
|
||||
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
private final ProductRiskExcelAssemblyService excelAssemblyService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ProductRiskTaskCacheService productRiskTaskCacheService;
|
||||
private final TaskPressureProperties taskPressureProperties;
|
||||
@@ -477,14 +479,8 @@ public class ProductRiskTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (entity.getResultFileUrl() == null || entity.getResultFileUrl().isBlank()) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
// 2026-09 去重:实现收口到 common/ResultDownloadResolver
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+4
-8
@@ -69,6 +69,8 @@ public class QueryAsinTaskService {
|
||||
private final QueryAsinExcelAssemblyService excelAssemblyService;
|
||||
private final QueryAsinTaskCacheService taskCacheService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final TaskPressureProperties taskPressureProperties;
|
||||
@@ -243,14 +245,8 @@ public class QueryAsinTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (blank(entity.getResultFileUrl())) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
// 2026-09 去重:实现收口到 common/ResultDownloadResolver
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+5
-8
@@ -78,6 +78,8 @@ public class ShopMatchTaskService {
|
||||
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
private final ShopMatchExcelAssemblyService excelAssemblyService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ShopMatchTaskCacheService shopMatchTaskCacheService;
|
||||
private final TaskPressureProperties taskPressureProperties;
|
||||
@@ -402,14 +404,9 @@ public class ShopMatchTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (entity.getResultFileUrl() == null || entity.getResultFileUrl().isBlank()) {
|
||||
throw new BusinessException("任务不存在");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
// 2026-09 去重:实现收口到 common/ResultDownloadResolver
|
||||
// (注:原实现空 url 时抛的是"任务不存在",统一为"暂无可下载文件",语义更准确)
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+4
-8
@@ -69,6 +69,8 @@ public class WithdrawTaskService {
|
||||
private final WithdrawExcelAssemblyService excelAssemblyService;
|
||||
private final WithdrawTaskCacheService taskCacheService;
|
||||
private final OssStorageService ossStorageService;
|
||||
/** 结果下载直链解析(2026-09 从本类抽到 common,消除 11 处逐字重复) */
|
||||
private final com.nanri.aiimage.common.service.ResultDownloadResolver resultDownloadResolver;
|
||||
private final ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final TaskPressureProperties taskPressureProperties;
|
||||
@@ -145,14 +147,8 @@ public class WithdrawTaskService {
|
||||
}
|
||||
|
||||
public String resolveResultDownloadUrl(Long resultId, Long userId) {
|
||||
FileResultEntity entity = fileResultMapper.selectById(resultId);
|
||||
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
|
||||
throw new BusinessException("记录不存在");
|
||||
}
|
||||
if (blank(entity.getResultFileUrl())) {
|
||||
throw new BusinessException("暂无可下载文件");
|
||||
}
|
||||
return ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl());
|
||||
// 2026-09 去重:实现收口到 common/ResultDownloadResolver
|
||||
return resultDownloadResolver.resolveUrl(resultId, userId, MODULE_TYPE);
|
||||
}
|
||||
|
||||
public String resolveResultDownloadFilename(Long resultId, Long userId) {
|
||||
|
||||
+7
-2
@@ -98,7 +98,10 @@ class AppearancePatentTaskServiceDelegationTest {
|
||||
|
||||
private AppearancePatentTaskService newService() {
|
||||
return new AppearancePatentTaskService(
|
||||
localFileStorageService, null, storageProperties, fileTaskMapper, fileResultMapper,
|
||||
localFileStorageService, null,
|
||||
// 2026-09:结果下载解析抽到 common/ResultDownloadResolver(去 11 处重复)
|
||||
mock(com.nanri.aiimage.common.service.ResultDownloadResolver.class),
|
||||
storageProperties, fileTaskMapper, fileResultMapper,
|
||||
taskScopeStateMapper, taskChunkMapper, new ObjectMapper(), llmClient, taskCacheService,
|
||||
properties, taskFileJobService, taskProgressSnapshotService,
|
||||
transientPayloadStorageService, transactionManager, distributedJobLockService,
|
||||
@@ -109,7 +112,9 @@ class AppearancePatentTaskServiceDelegationTest {
|
||||
|
||||
private AppearancePatentTaskService serviceWithoutTransactionManager() {
|
||||
return new AppearancePatentTaskService(
|
||||
localFileStorageService, null, storageProperties, fileTaskMapper, fileResultMapper,
|
||||
localFileStorageService, null,
|
||||
mock(com.nanri.aiimage.common.service.ResultDownloadResolver.class),
|
||||
storageProperties, fileTaskMapper, fileResultMapper,
|
||||
taskScopeStateMapper, taskChunkMapper, new ObjectMapper(), llmClient, taskCacheService,
|
||||
properties, taskFileJobService, taskProgressSnapshotService,
|
||||
transientPayloadStorageService, null, distributedJobLockService,
|
||||
|
||||
+4
-1
@@ -207,7 +207,10 @@ class AppearancePatentTaskServiceHistoryBatchTest {
|
||||
lenient().when(taskProgressSnapshotService.find(any(), any())).thenReturn(null);
|
||||
|
||||
service = new AppearancePatentTaskService(
|
||||
localFileStorageService, null, storageProperties, fileTaskMapper, fileResultMapper,
|
||||
localFileStorageService, null,
|
||||
// 2026-09:结果下载解析抽到 common/ResultDownloadResolver(去 11 处重复)
|
||||
mock(com.nanri.aiimage.common.service.ResultDownloadResolver.class),
|
||||
storageProperties, fileTaskMapper, fileResultMapper,
|
||||
taskScopeStateMapper, taskChunkMapper, new ObjectMapper(), llmClient, taskCacheService,
|
||||
properties, taskFileJobService, taskProgressSnapshotService,
|
||||
transientPayloadStorageService, transactionManager, distributedJobLockService,
|
||||
|
||||
+4
-1
@@ -300,7 +300,10 @@ class RollbackSemanticsContractTest {
|
||||
|
||||
private AppearancePatentTaskService appearancePatentService() {
|
||||
return new AppearancePatentTaskService(
|
||||
localFileStorageService, ossStorageService, storageProperties, fileTaskMapper, fileResultMapper,
|
||||
localFileStorageService, ossStorageService,
|
||||
// 2026-09:结果下载解析抽到 common/ResultDownloadResolver(去 11 处重复)
|
||||
mock(com.nanri.aiimage.common.service.ResultDownloadResolver.class),
|
||||
storageProperties, fileTaskMapper, fileResultMapper,
|
||||
taskScopeStateMapper, taskChunkMapper, objectMapper, mock(AppearancePatentLlmClient.class),
|
||||
mock(AppearancePatentTaskCacheService.class), mock(com.nanri.aiimage.config.AppearancePatentProperties.class),
|
||||
taskFileJobService, taskProgressSnapshotService, transientPayloadStorageService, transactionManager,
|
||||
|
||||
Reference in New Issue
Block a user