✨ 毛利分析报表导出接入 OSS 存储

This commit is contained in:
caojia
2026-10-16 17:09:34 +08:00
parent 9e391888f5
commit eb67ccc3cb
4 changed files with 218 additions and 22 deletions
@@ -0,0 +1,135 @@
package com.bms.report;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicReference;
import com.bms.common.config.BmsConfig;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.file.OssStorage;
import com.bms.price.domain.PrcProfitReportExport;
import com.bms.price.domain.vo.PrcProfitReportQuery;
import com.bms.price.service.IPrcProfitReportService;
import com.bms.web.controller.report.PrcProfitReportController;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
class ProfitReportOssTest {
@TempDir
Path tempDir;
private final IPrcProfitReportService service = mock(IPrcProfitReportService.class);
private final OssStorage storage = mock(OssStorage.class);
private PrcProfitReportController controller() {
PrcProfitReportController controller = new PrcProfitReportController() {
@Override
public String getUsername() { return "tester"; }
};
ReflectionTestUtils.setField(controller, "profitReportService", service);
ReflectionTestUtils.setField(controller, "ossStorage", storage);
when(service.createExport(any())).thenAnswer(invocation -> {
invocation.<PrcProfitReportExport>getArgument(0).setExportId(1L);
return 1;
});
when(service.selectOrderRows(any())).thenReturn(Collections.emptyList());
when(service.selectProductRows(any())).thenReturn(Collections.emptyList());
return controller;
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void bothDimensionsUploadValidExcelAndCleanTempFile(boolean product) throws Exception {
PrcProfitReportController controller = controller();
AtomicReference<Path> generated = new AtomicReference<>();
when(storage.upload(any(), startsWith("report/profit/"), anyString())).thenAnswer(invocation -> {
Path file = invocation.getArgument(0);
generated.set(file);
try (XSSFWorkbook workbook = new XSSFWorkbook(file.toFile())) {
assertTrue(workbook.getSheetAt(0).getRow(0).getPhysicalNumberOfCells() > 0);
}
verify(service, never()).completeExport(any());
return "oss://lionqueen/bms/test/report/profit/test.xlsx";
});
when(service.completeExport(any())).thenReturn(1);
AjaxResult result = product ? controller.exportProducts(new PrcProfitReportQuery())
: controller.exportOrders(new PrcProfitReportQuery());
PrcProfitReportExport export = (PrcProfitReportExport) result.get("data");
assertEquals("SUCCESS", export.getExportStatus());
assertTrue(export.getStorageKey().startsWith("oss://"));
assertFalse(Files.exists(generated.get()));
verify(service).completeExport(export);
verify(service, never()).failExport(anyLong(), anyString());
}
@Test
void uploadFailureMarksFailedAndCleansTemp() throws Exception {
PrcProfitReportController controller = controller();
AtomicReference<Path> generated = new AtomicReference<>();
when(storage.upload(any(), anyString(), anyString())).thenAnswer(invocation -> {
generated.set(invocation.getArgument(0));
throw new IOException("upload unavailable");
});
assertThrows(ServiceException.class, () -> controller.exportProducts(new PrcProfitReportQuery()));
assertFalse(Files.exists(generated.get()));
verify(service, never()).completeExport(any());
verify(service).failExport(eq(1L), anyString());
}
@Test
void unsuccessfulStatusUpdateDeletesUploadedObject() throws Exception {
PrcProfitReportController controller = controller();
when(storage.upload(any(), anyString(), anyString())).thenReturn("oss://lionqueen/bms/test/report/profit/test.xlsx");
assertThrows(ServiceException.class, () -> controller.exportOrders(new PrcProfitReportQuery()));
verify(storage).delete("oss://lionqueen/bms/test/report/profit/test.xlsx");
verify(service).failExport(eq(1L), anyString());
}
@Test
void ossDownloadUsesCurrentUserAndStorage() throws Exception {
PrcProfitReportController controller = controller();
when(service.selectExport(1L, "tester")).thenReturn(export("oss://lionqueen/bms/test/report/profit/test.xlsx"));
MockHttpServletResponse response = new MockHttpServletResponse();
controller.downloadExport(1L, response);
verify(storage).download(eq("oss://lionqueen/bms/test/report/profit/test.xlsx"), same(response.getOutputStream()));
}
@Test
void historicalLocalFileStillDownloads() throws Exception {
String previousProfile = BmsConfig.getProfile();
try {
new BmsConfig().setProfile(tempDir.toString());
Path file = tempDir.resolve("download/old.xlsx");
Files.createDirectories(file.getParent());
Files.write(file, new byte[] {1, 2, 3});
PrcProfitReportController controller = controller();
when(service.selectExport(1L, "tester")).thenReturn(export("old.xlsx"));
MockHttpServletResponse response = new MockHttpServletResponse();
controller.downloadExport(1L, response);
assertArrayEquals(new byte[] {1, 2, 3}, response.getContentAsByteArray());
verifyNoInteractions(storage);
} finally {
new BmsConfig().setProfile(previousProfile);
}
}
private PrcProfitReportExport export(String key) {
PrcProfitReportExport export = new PrcProfitReportExport();
export.setStorageKey(key);
export.setExportStatus("SUCCESS");
export.setFileName("profit.xlsx");
return export;
}
}