后端优化,增加新需求
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
package com.nanri.aiimage.modules.dedupe.service;
|
||||
|
||||
import com.nanri.aiimage.common.exception.BusinessException;
|
||||
import com.nanri.aiimage.modules.dedupe.mapper.DedupeTotalDataMapper;
|
||||
import com.nanri.aiimage.modules.dedupe.model.dto.DedupeTotalDataCreateRequest;
|
||||
import com.nanri.aiimage.modules.dedupe.model.entity.DedupeTotalDataEntity;
|
||||
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportProgressVo;
|
||||
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataItemVo;
|
||||
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataPageVo;
|
||||
import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
|
||||
@@ -15,15 +17,23 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -42,6 +52,8 @@ class DedupeTotalDataServiceTest {
|
||||
private ShopManageGroupMapper shopManageGroupMapper;
|
||||
@Mock
|
||||
private PlatformTransactionManager transactionManager;
|
||||
@Mock
|
||||
private TransactionStatus transactionStatus;
|
||||
@InjectMocks
|
||||
private DedupeTotalDataService service;
|
||||
|
||||
@@ -65,13 +77,14 @@ class DedupeTotalDataServiceTest {
|
||||
});
|
||||
|
||||
DedupeTotalDataCreateRequest request = new DedupeTotalDataCreateRequest();
|
||||
request.setDataValue(" B012345678 ");
|
||||
request.setDataValue(" b012345678 ");
|
||||
DedupeTotalDataItemVo item = service.create(request, 23L);
|
||||
|
||||
ArgumentCaptor<DedupeTotalDataEntity> captor = ArgumentCaptor.forClass(DedupeTotalDataEntity.class);
|
||||
verify(dedupeTotalDataMapper).insert(captor.capture());
|
||||
assertEquals(23L, captor.getValue().getUploaderUserId());
|
||||
assertEquals("member-a", captor.getValue().getUploaderUsername());
|
||||
assertEquals("B012345678", captor.getValue().getDataValue());
|
||||
assertEquals("member-a", item.getUsername());
|
||||
}
|
||||
|
||||
@@ -125,31 +138,106 @@ class DedupeTotalDataServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void usernameSearchCannotExpandLeaderScope() {
|
||||
void concurrentDuplicateDuringImportIsSkipped() throws Exception {
|
||||
when(adminUserMapper.selectById(23L)).thenReturn(user(23L, "normal", "member-a"));
|
||||
when(dedupeTotalDataMapper.selectOne(any())).thenReturn(null);
|
||||
when(transactionManager.getTransaction(any())).thenReturn(transactionStatus);
|
||||
when(dedupeTotalDataMapper.insert(any(DedupeTotalDataEntity.class)))
|
||||
.thenThrow(new DuplicateKeyException("duplicate"));
|
||||
|
||||
var result = service.importFromExcel(asinWorkbook("b012345678"), 23L);
|
||||
|
||||
assertEquals(0, result.getInsertedCount());
|
||||
assertEquals(1, result.getSkippedCount());
|
||||
verify(transactionManager).rollback(transactionStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usernameSearchKeepsLeaderScope() {
|
||||
when(adminUserMapper.selectById(10L)).thenReturn(user(10L, "admin", "leader"));
|
||||
when(shopManageGroupMapper.selectManagedMemberUserIds(10L)).thenReturn(List.of(23L));
|
||||
when(adminUserMapper.selectIdsByUsernameLike("other")).thenReturn(List.of(99L));
|
||||
when(dedupeTotalDataMapper.selectCount(any())).thenReturn(0L);
|
||||
when(dedupeTotalDataMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
DedupeTotalDataPageVo page = service.page(1, 15, "", "other", 10L);
|
||||
|
||||
assertEquals(0L, page.getTotal());
|
||||
assertTrue(page.getItems().isEmpty());
|
||||
verify(dedupeTotalDataMapper, never()).selectCount(any());
|
||||
verify(dedupeTotalDataMapper, never()).selectList(any());
|
||||
verify(shopManageGroupMapper).selectManagedMemberUserIds(10L);
|
||||
verify(dedupeTotalDataMapper).selectCount(any());
|
||||
verify(dedupeTotalDataMapper).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void comparableValueLookupRemainsGlobal() {
|
||||
when(dedupeTotalDataMapper.selectExistingDataValues(List.of("B012345678")))
|
||||
.thenReturn(List.of("B012345678"));
|
||||
.thenReturn(List.of("b012345678"));
|
||||
|
||||
Set<String> values = service.findExistingComparableValues(List.of(" B012345678 "));
|
||||
Set<String> values = service.findExistingComparableValues(List.of(" b012345678 "));
|
||||
|
||||
assertEquals(Set.of("B012345678"), values);
|
||||
verify(adminUserMapper, never()).selectById(any(Long.class));
|
||||
verify(shopManageGroupMapper, never()).selectManagedMemberUserIds(any(Long.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportUsesScopedUsernameAndProducesWorkbook() throws Exception {
|
||||
when(adminUserMapper.selectById(10L)).thenReturn(user(10L, "admin", "leader"));
|
||||
when(shopManageGroupMapper.selectManagedMemberUserIds(10L)).thenReturn(List.of(23L));
|
||||
DedupeTotalDataEntity entity = data(91L, 23L);
|
||||
entity.setUploaderUsername("member-a");
|
||||
entity.setCreatedAt(LocalDateTime.of(2026, 7, 20, 12, 30));
|
||||
when(dedupeTotalDataMapper.selectList(any())).thenReturn(List.of(entity));
|
||||
|
||||
byte[] bytes = service.export(
|
||||
"member",
|
||||
LocalDate.of(2026, 7, 1),
|
||||
LocalDate.of(2026, 7, 20),
|
||||
10L);
|
||||
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(bytes))) {
|
||||
var sheet = workbook.getSheetAt(0);
|
||||
assertEquals("ASIN值", sheet.getRow(0).getCell(1).getStringCellValue());
|
||||
assertEquals("B012345678", sheet.getRow(1).getCell(1).getStringCellValue());
|
||||
assertEquals("member-a", sheet.getRow(1).getCell(2).getStringCellValue());
|
||||
assertEquals("2026-07-20 12:30:00", sheet.getRow(1).getCell(3).getStringCellValue());
|
||||
}
|
||||
|
||||
verify(shopManageGroupMapper).selectManagedMemberUserIds(10L);
|
||||
verify(dedupeTotalDataMapper).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportRejectsReversedDateRange() {
|
||||
assertThrows(BusinessException.class, () -> service.export(
|
||||
"",
|
||||
LocalDate.of(2026, 7, 20),
|
||||
LocalDate.of(2026, 7, 1),
|
||||
10L));
|
||||
verify(dedupeTotalDataMapper, never()).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void expiredCompletedProgressIsRemovedOnNextLookup() {
|
||||
Map<String, DedupeTotalDataImportProgressVo> progressMap =
|
||||
(Map<String, DedupeTotalDataImportProgressVo>) ReflectionTestUtils.getField(service, "importProgressMap");
|
||||
Map<String, Long> ownerMap = (Map<String, Long>) ReflectionTestUtils.getField(service, "importOwnerMap");
|
||||
Map<String, Long> completedAtMap =
|
||||
(Map<String, Long>) ReflectionTestUtils.getField(service, "importCompletedAtMap");
|
||||
DedupeTotalDataImportProgressVo progress = new DedupeTotalDataImportProgressVo();
|
||||
progress.setStatus("success");
|
||||
progressMap.put("expired", progress);
|
||||
ownerMap.put("expired", 23L);
|
||||
completedAtMap.put("expired", System.currentTimeMillis() - (2 * 60 * 60 * 1000L));
|
||||
|
||||
assertThrows(BusinessException.class, () -> service.getImportProgress("expired", 23L));
|
||||
|
||||
assertFalse(progressMap.containsKey("expired"));
|
||||
assertFalse(ownerMap.containsKey("expired"));
|
||||
assertFalse(completedAtMap.containsKey("expired"));
|
||||
}
|
||||
|
||||
private AdminUserEntity user(Long id, String role, String username) {
|
||||
AdminUserEntity user = new AdminUserEntity();
|
||||
user.setId(id);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.nanri.aiimage.modules.task.service;
|
||||
|
||||
import com.nanri.aiimage.config.InstanceMetadata;
|
||||
import com.nanri.aiimage.modules.appearancepatent.service.AppearancePatentTaskService;
|
||||
import com.nanri.aiimage.modules.brand.service.BrandTaskService;
|
||||
import com.nanri.aiimage.modules.collectdata.service.CollectDataService;
|
||||
import com.nanri.aiimage.modules.deletebrand.service.DeleteBrandRunService;
|
||||
import com.nanri.aiimage.modules.patroldelete.service.PatrolDeleteTaskService;
|
||||
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackTaskService;
|
||||
import com.nanri.aiimage.modules.productrisk.service.ProductRiskTaskService;
|
||||
import com.nanri.aiimage.modules.queryasin.service.QueryAsinTaskService;
|
||||
import com.nanri.aiimage.modules.shopmatch.service.ShopMatchTaskService;
|
||||
import com.nanri.aiimage.modules.similarasin.service.SimilarAsinTaskService;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
|
||||
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.TaskFileJobEntity;
|
||||
import com.nanri.aiimage.modules.withdraw.service.WithdrawTaskService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TaskResultFileJobWorkerTest {
|
||||
|
||||
@Mock private TaskFileJobService taskFileJobService;
|
||||
@Mock private TaskDistributedLockService taskDistributedLockService;
|
||||
@Mock private TaskResultPayloadService taskResultPayloadService;
|
||||
@Mock private FileResultMapper fileResultMapper;
|
||||
@Mock private TaskFileJobLocalDispatcher taskFileJobLocalDispatcher;
|
||||
@Mock private InstanceMetadata instanceMetadata;
|
||||
@Mock private ShopMatchTaskService shopMatchTaskService;
|
||||
@Mock private PriceTrackTaskService priceTrackTaskService;
|
||||
@Mock private ProductRiskTaskService productRiskTaskService;
|
||||
@Mock private QueryAsinTaskService queryAsinTaskService;
|
||||
@Mock private WithdrawTaskService withdrawTaskService;
|
||||
@Mock private PatrolDeleteTaskService patrolDeleteTaskService;
|
||||
@Mock private AppearancePatentTaskService appearancePatentTaskService;
|
||||
@Mock private SimilarAsinTaskService similarAsinTaskService;
|
||||
@Mock private DeleteBrandRunService deleteBrandRunService;
|
||||
@Mock private BrandTaskService brandTaskService;
|
||||
@Mock private CollectDataService collectDataService;
|
||||
|
||||
@InjectMocks private TaskResultFileJobWorker worker;
|
||||
|
||||
@Test
|
||||
void withdrawFileSuccessFinalizesTaskAfterReleasingLock() {
|
||||
long jobId = 13640L;
|
||||
long taskId = 20140L;
|
||||
long resultId = 22928L;
|
||||
TaskFileJobEntity job = new TaskFileJobEntity();
|
||||
job.setId(jobId);
|
||||
job.setTaskId(taskId);
|
||||
job.setResultId(resultId);
|
||||
job.setModuleType("WITHDRAW");
|
||||
job.setScopeKey("withdraw:20140");
|
||||
|
||||
FileResultEntity result = new FileResultEntity();
|
||||
result.setResultFileUrl("result/withdraw/20140.xlsx");
|
||||
TaskDistributedLockService.LockHandle lock = mock(TaskDistributedLockService.LockHandle.class);
|
||||
|
||||
when(taskFileJobService.markRunning(jobId)).thenReturn(true);
|
||||
when(taskDistributedLockService.acquire("WITHDRAW", taskId, TaskDistributedLockService.DEFAULT_WAIT_MILLIS))
|
||||
.thenReturn(lock);
|
||||
when(fileResultMapper.selectById(resultId)).thenReturn(result);
|
||||
|
||||
worker.process(job);
|
||||
|
||||
verify(taskFileJobService).markSuccess(job, "result/withdraw/20140.xlsx");
|
||||
verify(taskResultPayloadService).deleteLatest(taskId, "WITHDRAW", "withdraw:20140");
|
||||
InOrder order = inOrder(lock, withdrawTaskService);
|
||||
order.verify(lock).close();
|
||||
order.verify(withdrawTaskService).tryFinalizeTask(taskId, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user