视频任务管理更新
This commit is contained in:
@@ -149,4 +149,19 @@ class AppearancePatentCozeClientTest {
|
||||
assertThat(merged.get(0).getAppearanceRisk()).isNullOrEmpty();
|
||||
verify(brandCheckClient, never()).checkTitleText(anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void preserveSubmittedPriceWhenMergingCozeResult() throws Exception {
|
||||
AppearancePatentResultRowDto row = new AppearancePatentResultRowDto();
|
||||
row.setId("1");
|
||||
row.setPrice("12.99");
|
||||
String dataText = """
|
||||
{"data":[{"row_id":"1","title":"无","appearance":"无侵权","result":"无侵权"}]}
|
||||
""";
|
||||
|
||||
List<AppearancePatentResultRowDto> merged = client.mergeRowsFromDataText(List.of(row), dataText);
|
||||
|
||||
assertThat(merged).hasSize(1);
|
||||
assertThat(merged.get(0).getPrice()).isEqualTo("12.99");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,16 @@ class AppearancePatentTaskServiceTest {
|
||||
parsedRow.getValues().clear();
|
||||
assertEquals("", AppearancePatentTaskService.resolveBrand(resultRow, parsedRow));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resultPriceUsesOnlyPythonSubmittedValue() {
|
||||
assertEquals("", AppearancePatentTaskService.resolvePrice(null));
|
||||
|
||||
AppearancePatentResultRowDto resultRow = new AppearancePatentResultRowDto();
|
||||
resultRow.setPrice(" 12.99 ");
|
||||
assertEquals("12.99", AppearancePatentTaskService.resolvePrice(resultRow));
|
||||
|
||||
resultRow.setPrice(null);
|
||||
assertEquals("", AppearancePatentTaskService.resolvePrice(resultRow));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,53 @@ import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ImageVideoArchiveServiceTest {
|
||||
|
||||
@Test
|
||||
void deletesExpiredWorkflowTasksAndArchivedObjects() {
|
||||
ImageVideoAsyncTaskMapper taskMapper = mock(ImageVideoAsyncTaskMapper.class);
|
||||
OssStorageService ossStorageService = mock(OssStorageService.class);
|
||||
ImageVideoArchiveService archiveService = new ImageVideoArchiveService(
|
||||
taskMapper, ossStorageService, new ImageVideoProperties(), new ObjectMapper());
|
||||
ImageVideoAsyncTaskEntity task = new ImageVideoAsyncTaskEntity();
|
||||
task.setId(10L);
|
||||
task.setTaskType(ImageVideoArchiveService.TASK_TYPE);
|
||||
task.setArchivedVideosJson("[{\"objectKey\":\"result/image_video/10/video.mp4\"}]");
|
||||
when(taskMapper.selectList(any())).thenReturn(List.of(task));
|
||||
when(taskMapper.deleteById(10L)).thenReturn(1);
|
||||
|
||||
archiveService.cleanupExpiredWorkflowTasks();
|
||||
|
||||
verify(ossStorageService).deleteObject("result/image_video/10/video.mp4");
|
||||
verify(taskMapper).deleteById(10L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsExpiredTaskWhenArchivedObjectDeletionFails() {
|
||||
ImageVideoAsyncTaskMapper taskMapper = mock(ImageVideoAsyncTaskMapper.class);
|
||||
OssStorageService ossStorageService = mock(OssStorageService.class);
|
||||
ImageVideoArchiveService archiveService = new ImageVideoArchiveService(
|
||||
taskMapper, ossStorageService, new ImageVideoProperties(), new ObjectMapper());
|
||||
ImageVideoAsyncTaskEntity task = new ImageVideoAsyncTaskEntity();
|
||||
task.setId(11L);
|
||||
task.setTaskType(ImageVideoArchiveService.TASK_TYPE);
|
||||
task.setArchivedVideosJson("[{\"objectKey\":\"result/image_video/11/video.mp4\"}]");
|
||||
when(taskMapper.selectList(any())).thenReturn(List.of(task));
|
||||
doThrow(new IllegalStateException("OSS unavailable"))
|
||||
.when(ossStorageService).deleteObject("result/image_video/11/video.mp4");
|
||||
|
||||
archiveService.cleanupExpiredWorkflowTasks();
|
||||
|
||||
verify(taskMapper, never()).deleteById(11L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractsNestedAndMultipleVideoUrlsWithoutDuplicates() {
|
||||
Map<String, Object> result = Map.of(
|
||||
|
||||
@@ -139,6 +139,69 @@ class ImageVideoAsyncTaskServiceTest {
|
||||
verify(taskMapper).updateById(task);
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedTaskIsReturnedOnceAndThenDeleted() {
|
||||
ImageVideoAsyncTaskMapper taskMapper = mock(ImageVideoAsyncTaskMapper.class);
|
||||
ImageVideoAsyncTaskService service = new ImageVideoAsyncTaskService(
|
||||
taskMapper,
|
||||
mock(ImageVideoCozeService.class),
|
||||
mock(ImageVideoWorkflowConfigService.class),
|
||||
mock(ImageVideoArchiveService.class),
|
||||
new ObjectMapper(),
|
||||
Runnable::run);
|
||||
ImageVideoAsyncTaskEntity task = waitingWorkflowTask();
|
||||
task.setStatus("FAILED");
|
||||
task.setCozeStatus("FAIL");
|
||||
task.setErrorMessage("model unavailable");
|
||||
when(taskMapper.selectOne(any())).thenReturn(task);
|
||||
|
||||
var result = service.getTask(98L, 1L);
|
||||
|
||||
assertEquals("FAILED", result.getStatus());
|
||||
assertEquals("model unavailable", result.getErrorMessage());
|
||||
verify(taskMapper).deleteById(98L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void recoverableFailedTaskIsNotDeleted() {
|
||||
ImageVideoAsyncTaskMapper taskMapper = mock(ImageVideoAsyncTaskMapper.class);
|
||||
ImageVideoAsyncTaskService service = new ImageVideoAsyncTaskService(
|
||||
taskMapper,
|
||||
mock(ImageVideoCozeService.class),
|
||||
mock(ImageVideoWorkflowConfigService.class),
|
||||
mock(ImageVideoArchiveService.class),
|
||||
new ObjectMapper(),
|
||||
Runnable::run);
|
||||
ImageVideoAsyncTaskEntity task = waitingWorkflowTask();
|
||||
task.setStatus("FAILED");
|
||||
task.setCozeStatus("RUNNING");
|
||||
task.setErrorMessage("Coze workflow finished with status RUNNING");
|
||||
when(taskMapper.selectOne(any())).thenReturn(task);
|
||||
|
||||
var result = service.getTask(98L, 1L);
|
||||
|
||||
assertEquals("WAITING", result.getStatus());
|
||||
verify(taskMapper).updateById(task);
|
||||
verify(taskMapper, never()).deleteById(98L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanupRemovesFailedTasksThatWereNotPolled() {
|
||||
ImageVideoAsyncTaskMapper taskMapper = mock(ImageVideoAsyncTaskMapper.class);
|
||||
ImageVideoAsyncTaskService service = new ImageVideoAsyncTaskService(
|
||||
taskMapper,
|
||||
mock(ImageVideoCozeService.class),
|
||||
mock(ImageVideoWorkflowConfigService.class),
|
||||
mock(ImageVideoArchiveService.class),
|
||||
new ObjectMapper(),
|
||||
Runnable::run);
|
||||
when(taskMapper.delete(any())).thenReturn(3);
|
||||
|
||||
service.cleanupFailedTasks();
|
||||
|
||||
verify(taskMapper).delete(any());
|
||||
}
|
||||
|
||||
private ImageVideoAsyncTaskEntity waitingDouyinTask() {
|
||||
ImageVideoAsyncTaskEntity task = new ImageVideoAsyncTaskEntity();
|
||||
task.setId(79L);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.nanri.aiimage.modules.permission.service;
|
||||
|
||||
import com.nanri.aiimage.modules.permission.mapper.AdminUserMapper;
|
||||
import com.nanri.aiimage.modules.permission.mapper.PermissionMenuMapper;
|
||||
import com.nanri.aiimage.modules.permission.mapper.UserColumnPermissionMapper;
|
||||
import com.nanri.aiimage.modules.permission.model.dto.UserColumnPermissionUpdateRequest;
|
||||
import com.nanri.aiimage.modules.permission.model.entity.AdminUserEntity;
|
||||
import com.nanri.aiimage.modules.permission.model.entity.PermissionMenuEntity;
|
||||
import com.nanri.aiimage.modules.permission.model.entity.UserColumnPermissionEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class PermissionMenuServiceTest {
|
||||
|
||||
@Test
|
||||
void preservesExistingImageVideoPermissionDuringGenericReplacement() {
|
||||
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
|
||||
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
|
||||
AdminUserMapper userMapper = mock(AdminUserMapper.class);
|
||||
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
|
||||
when(userMapper.selectById(7L)).thenReturn(new AdminUserEntity());
|
||||
when(menuMapper.selectCount(any())).thenReturn(1L);
|
||||
when(menuMapper.selectOne(any())).thenReturn(imageVideoPermission());
|
||||
when(permissionMapper.selectCount(any())).thenReturn(1L);
|
||||
|
||||
UserColumnPermissionUpdateRequest request = new UserColumnPermissionUpdateRequest();
|
||||
request.setColumnIds(List.of(2L));
|
||||
service.updateUserColumnPermissions(7L, request);
|
||||
|
||||
ArgumentCaptor<UserColumnPermissionEntity> captor = ArgumentCaptor.forClass(UserColumnPermissionEntity.class);
|
||||
verify(permissionMapper, times(2)).insert(captor.capture());
|
||||
assertThat(captor.getAllValues())
|
||||
.extracting(UserColumnPermissionEntity::getColumnId)
|
||||
.containsExactly(2L, 75L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotGrantImageVideoPermissionThroughGenericReplacement() {
|
||||
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
|
||||
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
|
||||
AdminUserMapper userMapper = mock(AdminUserMapper.class);
|
||||
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
|
||||
when(userMapper.selectById(8L)).thenReturn(new AdminUserEntity());
|
||||
when(menuMapper.selectCount(any())).thenReturn(1L);
|
||||
when(menuMapper.selectOne(any())).thenReturn(imageVideoPermission());
|
||||
when(permissionMapper.selectCount(any())).thenReturn(0L);
|
||||
|
||||
UserColumnPermissionUpdateRequest request = new UserColumnPermissionUpdateRequest();
|
||||
request.setColumnIds(List.of(75L));
|
||||
service.updateUserColumnPermissions(8L, request);
|
||||
|
||||
verify(permissionMapper, times(0)).insert(any(UserColumnPermissionEntity.class));
|
||||
}
|
||||
|
||||
private PermissionMenuEntity imageVideoPermission() {
|
||||
PermissionMenuEntity entity = new PermissionMenuEntity();
|
||||
entity.setId(75L);
|
||||
entity.setColumnKey("admin_image_video_tasks");
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.nanri.aiimage.modules.pricetrack.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.config.TaskPressureProperties;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import com.nanri.aiimage.modules.pricetrack.mapper.PriceTrackShopCandidateMapper;
|
||||
import com.nanri.aiimage.modules.shopkey.service.SkipPriceAsinService;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
|
||||
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
|
||||
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
|
||||
import com.nanri.aiimage.modules.task.service.TaskDistributedLockService;
|
||||
import com.nanri.aiimage.modules.task.service.TaskFileJobService;
|
||||
import com.nanri.aiimage.modules.task.service.TaskResultPayloadService;
|
||||
import com.nanri.aiimage.modules.ziniao.service.ZiniaoShopSwitchService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PriceTrackTaskServiceTest {
|
||||
|
||||
@Mock private FileTaskMapper fileTaskMapper;
|
||||
@Mock private FileResultMapper fileResultMapper;
|
||||
@Mock private PriceTrackShopCandidateMapper candidateMapper;
|
||||
@Mock private ZiniaoShopSwitchService ziniaoShopSwitchService;
|
||||
@Mock private OssStorageService ossStorageService;
|
||||
@Mock private ObjectMapper objectMapper;
|
||||
@Mock private SkipPriceAsinService skipPriceAsinService;
|
||||
@Mock private PriceTrackExcelAssemblyService excelAssemblyService;
|
||||
@Mock private PriceTrackTaskCacheService priceTrackTaskCacheService;
|
||||
@Mock private PriceTrackLoopRunService priceTrackLoopRunService;
|
||||
@Mock private TaskPressureProperties taskPressureProperties;
|
||||
@Mock private TaskResultPayloadService taskResultPayloadService;
|
||||
@Mock private TaskFileJobService taskFileJobService;
|
||||
@Mock private TaskDistributedLockService taskDistributedLockService;
|
||||
|
||||
@InjectMocks private PriceTrackTaskService service;
|
||||
|
||||
@Test
|
||||
void markDispatchFailedImmediatelyFinalizesUnqueuedTask() {
|
||||
long taskId = 19788L;
|
||||
long userId = 671L;
|
||||
String error = "当前店铺 张志云 正在执行中,请等待店铺完成之后重试";
|
||||
|
||||
FileTaskEntity task = new FileTaskEntity();
|
||||
task.setId(taskId);
|
||||
task.setUserId(userId);
|
||||
task.setModuleType("PRICE_TRACK");
|
||||
task.setStatus("RUNNING");
|
||||
task.setRequestJson("{}");
|
||||
|
||||
FileResultEntity result = new FileResultEntity();
|
||||
result.setId(9001L);
|
||||
result.setTaskId(taskId);
|
||||
result.setModuleType("PRICE_TRACK");
|
||||
result.setSourceFilename("张志云");
|
||||
result.setSuccess(0);
|
||||
|
||||
TaskDistributedLockService.LockHandle lock = mock(TaskDistributedLockService.LockHandle.class);
|
||||
when(taskDistributedLockService.acquire("PRICE_TRACK", taskId)).thenReturn(lock);
|
||||
when(priceTrackTaskCacheService.getTaskCacheBatch(List.of(taskId))).thenReturn(Map.of(taskId, task));
|
||||
when(fileResultMapper.selectList(any())).thenReturn(List.of(result));
|
||||
|
||||
service.markDispatchFailed(taskId, userId, error);
|
||||
|
||||
assertEquals("FAILED", task.getStatus());
|
||||
assertEquals("张志云: " + error, task.getErrorMessage());
|
||||
assertNotNull(task.getFinishedAt());
|
||||
assertEquals(error, result.getErrorMessage());
|
||||
verify(fileResultMapper).updateById(result);
|
||||
verify(fileTaskMapper).updateById(task);
|
||||
verify(priceTrackTaskCacheService).deleteTaskCache(taskId);
|
||||
verify(priceTrackLoopRunService).syncLoopRunAfterChildTerminal(taskId);
|
||||
verify(lock).close();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.nanri.aiimage.modules.productrisk.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskRowDto;
|
||||
import com.nanri.aiimage.modules.productrisk.model.enums.ProductRiskCountryCode;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -15,6 +17,9 @@ import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ProductRiskExcelAssemblyServiceTest {
|
||||
|
||||
@@ -47,6 +52,76 @@ class ProductRiskExcelAssemblyServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesViolationDetailsAndMergesRepeatedAsinCells() throws Exception {
|
||||
Path xlsx = Files.createTempFile("product-risk-details-", ".xlsx");
|
||||
try {
|
||||
ProductRiskRowDto row = row("B0D2LG8R11", "处理失败", Boolean.FALSE);
|
||||
row.setViolationDetails(List.of(
|
||||
detail("商品安全违规", "成功"),
|
||||
detail("商品安全违规", "失败"),
|
||||
detail("商品安全违规", "成功")));
|
||||
ProductRiskRowDto secondRow = row("B0987884CV", "处理完成", Boolean.FALSE);
|
||||
secondRow.setViolationDetails(List.of(
|
||||
detail("商品安全违规", "成功"),
|
||||
detail("商品详情页违规", "成功")));
|
||||
|
||||
service.writeWorkbook(
|
||||
xlsx.toFile(),
|
||||
"店铺A",
|
||||
Map.of(ProductRiskCountryCode.DE.getSheetName(), List.of(row, secondRow)));
|
||||
|
||||
try (InputStream in = Files.newInputStream(xlsx);
|
||||
Workbook workbook = new XSSFWorkbook(in)) {
|
||||
Sheet sheet = workbook.getSheet(ProductRiskCountryCode.DE.getSheetName());
|
||||
assertEquals("ASIN", sheet.getRow(0).getCell(6).getStringCellValue());
|
||||
assertEquals("原因", sheet.getRow(0).getCell(7).getStringCellValue());
|
||||
assertEquals("状态", sheet.getRow(0).getCell(8).getStringCellValue());
|
||||
assertEquals("B0D2LG8R11", sheet.getRow(1).getCell(6).getStringCellValue());
|
||||
assertEquals("商品安全违规", sheet.getRow(2).getCell(7).getStringCellValue());
|
||||
assertEquals("失败", sheet.getRow(2).getCell(8).getStringCellValue());
|
||||
assertTrue(sheet.getMergedRegions().contains(new CellRangeAddress(1, 3, 6, 6)));
|
||||
assertTrue(sheet.getMergedRegions().contains(new CellRangeAddress(4, 5, 6, 6)));
|
||||
assertEquals("处理失败", sheet.getRow(1).getCell(2).getStringCellValue());
|
||||
assertEquals("B0987884CV", sheet.getRow(4).getCell(1).getStringCellValue());
|
||||
assertEquals("B0987884CV", sheet.getRow(4).getCell(6).getStringCellValue());
|
||||
assertEquals("商品详情页违规", sheet.getRow(5).getCell(7).getStringCellValue());
|
||||
}
|
||||
} finally {
|
||||
Files.deleteIfExists(xlsx);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void violationDetailsUseLatestNonEmptyListAndIgnoreEmptyRetry() {
|
||||
List<ProductRiskRowDto.ViolationDetail> original = List.of(detail("旧原因", "成功"));
|
||||
List<ProductRiskRowDto.ViolationDetail> replacement = List.of(
|
||||
detail("商品安全违规", "成功"),
|
||||
detail("商品安全违规", "失败"));
|
||||
|
||||
List<ProductRiskRowDto.ViolationDetail> kept =
|
||||
ProductRiskTaskService.mergeViolationDetails(original, List.of());
|
||||
List<ProductRiskRowDto.ViolationDetail> replaced =
|
||||
ProductRiskTaskService.mergeViolationDetails(original, replacement);
|
||||
|
||||
assertEquals(1, kept.size());
|
||||
assertEquals("旧原因", kept.get(0).getReason());
|
||||
assertEquals(2, replaced.size());
|
||||
assertEquals("商品安全违规", replaced.get(1).getReason());
|
||||
assertEquals("失败", replaced.get(1).getStatus());
|
||||
assertNotSame(replacement.get(0), replaced.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void oldPayloadWithoutViolationDetailsRemainsCompatible() throws Exception {
|
||||
ProductRiskRowDto row = new ObjectMapper().readValue(
|
||||
"{\"shopName\":\"店铺A\",\"productAsinSku\":\"B0D2LG8R11\",\"status\":\"处理完成\"}",
|
||||
ProductRiskRowDto.class);
|
||||
|
||||
assertNotNull(row.getViolationDetails());
|
||||
assertTrue(row.getViolationDetails().isEmpty());
|
||||
}
|
||||
|
||||
private static ProductRiskRowDto row(String productAsinSku, String status, Boolean done) {
|
||||
ProductRiskRowDto row = new ProductRiskRowDto();
|
||||
row.setShopName("店铺A");
|
||||
@@ -55,4 +130,11 @@ class ProductRiskExcelAssemblyServiceTest {
|
||||
row.setDone(done);
|
||||
return row;
|
||||
}
|
||||
|
||||
private static ProductRiskRowDto.ViolationDetail detail(String reason, String status) {
|
||||
ProductRiskRowDto.ViolationDetail detail = new ProductRiskRowDto.ViolationDetail();
|
||||
detail.setReason(reason);
|
||||
detail.setStatus(status);
|
||||
return detail;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user