This commit is contained in:
2026-08-06 11:49:51 +08:00
parent a11e47fe3b
commit 7dd92f1c84
91 changed files with 6233 additions and 174 deletions
@@ -4,6 +4,7 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.security.AuthenticatedUser;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Modifier;
import java.lang.reflect.Method;
import java.util.Set;
@@ -27,10 +28,15 @@ class MerchantControllerIdentityContractTest {
}
@Test
void merchantWarehouseDefaultsToPlatformResourceScope() throws Exception {
var value = MerchantWarehouseController.class.getDeclaredField("publicTenantId")
.getAnnotation(org.springframework.beans.factory.annotation.Value.class);
assertThat(value.value()).isEqualTo("${baiyue.warehouse.public-tenant-id:0}");
void merchantControllersUseFixedPlatformResourceScope() throws Exception {
for (Class<?> controller : Set.of(MerchantWarehouseController.class, MerchantPartnerController.class)) {
var value = controller.getDeclaredField("PUBLIC_TENANT_ID");
value.setAccessible(true);
assertThat(Modifier.isStatic(value.getModifiers())).isTrue();
assertThat(Modifier.isFinal(value.getModifiers())).isTrue();
assertThat(value.getLong(null)).isEqualTo(0L);
assertThat(value.getAnnotation(org.springframework.beans.factory.annotation.Value.class)).isNull();
}
}
private static AuthenticatedUser user(String appCode, Long tenantId) {
@@ -49,6 +49,8 @@ class PlatformMasterDataControllerContractTest {
assertWarehouseMethod("create", PostMapping.class, "platform:warehouse:create");
assertWarehouseMethod("update", PutMapping.class, "platform:warehouse:edit");
assertWarehouseMethod("status", PatchMapping.class, "platform:warehouse:status");
assertWarehouseMethod("importTemplate", GetMapping.class, "platform:warehouse:create");
assertWarehouseMethod("importWarehouses", PostMapping.class, "platform:warehouse:create");
Method page = java.util.Arrays.stream(PlatformWarehouseController.class.getDeclaredMethods())
.filter(candidate -> candidate.getName().equals("page"))
@@ -56,6 +58,8 @@ class PlatformMasterDataControllerContractTest {
.orElseThrow();
assertThat(page.getParameterTypes()[1]).isEqualTo(Long.class);
assertThat(page.getParameters()[1].getAnnotation(RequestParam.class).required()).isFalse();
assertThat(page.getGenericReturnType().getTypeName())
.contains("WarehouseSummaryResponse");
}
@Test
@@ -110,6 +114,23 @@ class PlatformMasterDataControllerContractTest {
}
}
@Test
void upsertWarehouseImportRequiresEditPermission() throws Exception {
var guard = PlatformWarehouseController.class.getDeclaredMethod("requireImportPermission",
AuthenticatedUser.class, com.baiyue.warehouse.dto.WarehouseImportDtos.ImportMode.class);
guard.setAccessible(true);
var createOnlyUser = new AuthenticatedUser(1L, 0L, 0L, 1L,
"admin", "PLATFORM", "WEB", java.util.Set.of("platform:warehouse:create"));
var editUser = new AuthenticatedUser(1L, 0L, 0L, 1L,
"admin", "PLATFORM", "WEB", java.util.Set.of("platform:warehouse:create", "platform:warehouse:edit"));
guard.invoke(null, createOnlyUser, com.baiyue.warehouse.dto.WarehouseImportDtos.ImportMode.CREATE_ONLY);
guard.invoke(null, editUser, com.baiyue.warehouse.dto.WarehouseImportDtos.ImportMode.UPSERT);
assertThatThrownBy(() -> guard.invoke(null, createOnlyUser,
com.baiyue.warehouse.dto.WarehouseImportDtos.ImportMode.UPSERT))
.hasCauseInstanceOf(BizException.class);
}
private static void assertContract(Class<?> controller, String authority, String path) throws Exception {
RequestMapping mapping = controller.getAnnotation(RequestMapping.class);
assertThat(mapping.value()).containsExactly(path);
@@ -0,0 +1,83 @@
package com.baiyue.warehouse.repository;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.warehouse.entity.ServiceCapabilityEntity;
import com.baiyue.warehouse.entity.WarehouseAddressEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.mapper.ServiceCapabilityMapper;
import com.baiyue.warehouse.mapper.WarehouseAddressMapper;
import com.baiyue.warehouse.mapper.WarehouseMapper;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import jakarta.annotation.Resource;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(WarehouseRepository.class)
class WarehouseRepositoryTest {
@MockBean private WarehouseMapper warehouses;
@MockBean private WarehouseAddressMapper addresses;
@MockBean private ServiceCapabilityMapper capabilities;
@Resource private WarehouseRepository repository;
@BeforeAll
static void setUp() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "warehouse-test"), WarehouseEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "warehouse-address-test"), WarehouseAddressEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "warehouse-capability-test"), ServiceCapabilityEntity.class);
}
@Test
void pageSummariesSearchesAddressKeywordAndHydratesPrimaryAddress() {
WarehouseAddressEntity matchedAddress = new WarehouseAddressEntity();
matchedAddress.setWarehouseId(7L);
WarehouseAddressEntity primaryAddress = new WarehouseAddressEntity();
primaryAddress.setId(70L);
primaryAddress.setWarehouseId(7L);
primaryAddress.setCountryCode("US");
primaryAddress.setStateProvince("CA");
primaryAddress.setCity("Los Angeles");
primaryAddress.setAddressLine1("123 Logistics Ave");
when(addresses.selectList(any())).thenReturn(List.of(matchedAddress), List.of(primaryAddress));
WarehouseEntity warehouse = new WarehouseEntity();
warehouse.setId(7L);
warehouse.setTenantId(0L);
warehouse.setWarehouseCode("FBA-LAX9");
warehouse.setWarehouseName("LAX9");
warehouse.setWarehouseType("FBA");
warehouse.setCountryCode("US");
warehouse.setTimezone("UTC-8");
warehouse.setStatus("ACTIVE");
when(warehouses.selectPage(any(), any())).thenAnswer(invocation -> {
Page<WarehouseEntity> page = invocation.getArgument(0);
page.setRecords(List.of(warehouse));
page.setTotal(1);
return page;
});
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(1, 20, "Los Angeles",
null, null, "FBA", "ACTIVE");
var result = repository.pageSummaries(0L, query);
assertThat(result.total()).isEqualTo(1);
assertThat(result.records()).singleElement().satisfies(item -> {
assertThat(item.warehouseCode()).isEqualTo("FBA-LAX9");
assertThat(item.primaryAddress().city()).isEqualTo("Los Angeles");
assertThat(item.primaryAddress().displayAddress()).contains("123 Logistics Ave");
});
verify(addresses, org.mockito.Mockito.times(2)).selectList(any());
verify(warehouses).selectPage(any(), any());
}
}
@@ -2,10 +2,13 @@ package com.baiyue.warehouse.service;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.warehouse.entity.ServiceCapabilityEntity;
import com.baiyue.warehouse.entity.WarehouseAddressEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.WarehouseRepository;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.common.core.api.PageResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
@@ -17,6 +20,15 @@ import static org.mockito.Mockito.*;
class WarehouseApplicationServiceTest {
@MockBean private WarehouseRepository repository;
@Resource private WarehouseApplicationService service;
@BeforeEach
void setUp() {
doAnswer(invocation -> {
WarehouseEntity warehouse = invocation.getArgument(0);
warehouse.setId(1L);
return null;
}).when(repository).insertWarehouse(any(WarehouseEntity.class));
}
@Test
void shouldHandleCase01() {
WarehouseEntity warehouse=new WarehouseEntity(); warehouse.setId(1L); warehouse.setTenantId(10001L); warehouse.setStatus("ACTIVE");
@@ -40,10 +52,68 @@ class WarehouseApplicationServiceTest {
}
@Test
void pagesAllPlatformWarehousesWithoutCustomerFilter() {
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(1, 200, null, null, null, null);
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(1, 200, null, null, null, null, null);
when(repository.page(null, query)).thenReturn(new PageResult<>(java.util.List.of(), 1, 200, 0, 0));
assertThat(service.page(null, query).records()).isEmpty();
verify(repository).page(null, query);
}
@Test
void createsDomesticWarehouseWithPlatformDefaults() {
WarehouseRequests.SaveRequest request = new WarehouseRequests.SaveRequest(null, "CN-SZX-01", "深圳作业仓",
null, null, null, "张三", "13800138000", null, null, null,
new WarehouseRequests.AddressRequest(null, null, "广东省", "深圳市", "南山区",
"518000", "前海路 1 号", null, null, null),
java.util.List.of(new WarehouseRequests.CapabilityRequest("RECEIVING", "收货", true, null, null)));
service.create(request);
var warehouse = org.mockito.ArgumentCaptor.forClass(WarehouseEntity.class);
var address = org.mockito.ArgumentCaptor.forClass(WarehouseAddressEntity.class);
var capability = org.mockito.ArgumentCaptor.forClass(ServiceCapabilityEntity.class);
verify(repository).insertWarehouse(warehouse.capture());
verify(repository).insertAddress(address.capture());
verify(repository).insertCapability(capability.capture());
assertThat(warehouse.getValue().getTenantId()).isZero();
assertThat(warehouse.getValue().getWarehouseType()).isEqualTo("DOMESTIC");
assertThat(warehouse.getValue().getCountryCode()).isEqualTo("CN");
assertThat(warehouse.getValue().getTimezone()).isEqualTo("UTC+8");
assertThat(warehouse.getValue().getStatus()).isEqualTo("ACTIVE");
assertThat(address.getValue().getAddressType()).isEqualTo("WAREHOUSE");
assertThat(address.getValue().getCountryCode()).isEqualTo("CN");
}
@Test
void fbaWarehouseUsesConfirmedStateTimezoneAndDoesNotCreateCapabilities() {
WarehouseRequests.SaveRequest request = new WarehouseRequests.SaveRequest(null, "FBA-LAX9", "LAX9",
"FBA", "US", null, null, null, null, null, null,
new WarehouseRequests.AddressRequest(null, "US", "CA", "Redlands", null,
"92374", "Amazon Fulfillment Center", null, null, null),
java.util.List.of(new WarehouseRequests.CapabilityRequest("RECEIVING", "收货", true, null, null)));
service.create(request);
var warehouse = org.mockito.ArgumentCaptor.forClass(WarehouseEntity.class);
verify(repository).insertWarehouse(warehouse.capture());
verify(repository, never()).insertCapability(any());
assertThat(warehouse.getValue().getTenantId()).isZero();
assertThat(warehouse.getValue().getWarehouseType()).isEqualTo("FBA");
assertThat(warehouse.getValue().getTimezone()).isEqualTo("UTC-8");
assertThat(warehouse.getValue().getStatus()).isEqualTo("ACTIVE");
}
@Test
void fbaWarehouseInMultiTimezoneStateRequiresExplicitTimezone() {
WarehouseRequests.SaveRequest request = new WarehouseRequests.SaveRequest(null, "FBA-DFW", "DFW",
"FBA", "US", null, null, null, null, null, null,
new WarehouseRequests.AddressRequest(null, "US", "TX", "Dallas", null,
"75201", "Amazon", null, null, null),
null);
assertThatThrownBy(() -> service.create(request)).isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.VALIDATION_FAILED);
verify(repository, never()).insertWarehouse(any());
}
}
@@ -0,0 +1,180 @@
package com.baiyue.warehouse.service;
import com.baiyue.warehouse.dto.WarehouseImportDtos.ImportMode;
import com.baiyue.warehouse.dto.WarehouseImportDtos.WarehouseImportResult;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.warehouse.entity.WarehouseAddressEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.WarehouseRepository;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import jakarta.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@DisplayName("仓库批量导入")
@SpringJUnitConfig(WarehouseImportService.class)
class WarehouseImportServiceTest {
@MockBean private WarehouseRepository repository;
@MockBean private WarehouseApplicationService warehouseService;
@Resource private WarehouseImportService service;
@Test
@DisplayName("默认 CREATE_ONLY 只新增新仓库并返回预检分类")
void shouldCreateOnlyNewWarehousesByDefault() throws Exception {
byte[] file = withRows(service.template(), new String[][] {
{"CN-SZX-01", "深圳作业仓", "", "", "广东省", "深圳市", "南山区", "前海路 1 号", "518000", "", "", "张三", "13800138000", "", ""},
{"FBA-LAX9", "", "FBA", "US", "CA", "Redlands", "", "Amazon FC", "92374", "", "", "", "", "", ""}
});
WarehouseImportResult result = service.importFile(0L, "warehouses.xlsx", file, null);
assertThat(result.success()).isTrue();
assertThat(result.committed()).isTrue();
assertThat(result.mode()).isEqualTo(ImportMode.CREATE_ONLY);
assertThat(result.createdRows()).isEqualTo(2);
assertThat(result.previewRows()).extracting("action").containsExactly("CREATE", "CREATE");
var captor = org.mockito.ArgumentCaptor.forClass(WarehouseRequests.SaveRequest.class);
verify(warehouseService, org.mockito.Mockito.times(2)).create(captor.capture());
assertThat(captor.getAllValues().get(0).tenantId()).isZero();
assertThat(captor.getAllValues().get(1).warehouseType()).isEqualTo("FBA");
assertThat(captor.getAllValues().get(1).warehouseName()).isEqualTo("FBA-LAX9");
}
@Test
@DisplayName("任一行错误时整批零写入")
void shouldRejectWholeBatchWhenAnyRowInvalid() throws Exception {
byte[] file = withRows(service.template(), new String[][] {
{"FBA-DFW", "DFW", "FBA", "US", "TX", "Dallas", "", "Amazon FC", "75201", "", "", "", "", "", ""},
{"CN-SZX-01", "深圳作业仓", "", "", "广东省", "深圳市", "", "前海路 1 号", "", "", "", "", "", "", ""}
});
WarehouseImportResult result = service.importFile(0L, "warehouses.xlsx", file, null);
assertThat(result.success()).isFalse();
assertThat(result.committed()).isFalse();
assertThat(result.createdRows()).isZero();
assertThat(result.errors()).anyMatch(error -> error.row() == 4 && error.field().equals("时区"));
verify(warehouseService, never()).create(any());
verify(warehouseService, never()).update(org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), any());
}
@Test
@DisplayName("UPSERT 更新现有仓库时保留联系人、备注并不重启停用数据")
void shouldPreserveContactRemarkAndInactiveStatusOnUpsert() throws Exception {
WarehouseEntity existing = new WarehouseEntity();
existing.setId(9L);
existing.setTenantId(0L);
existing.setWarehouseCode("CN-SZX-01");
existing.setWarehouseName("旧名称");
existing.setWarehouseType("DOMESTIC");
existing.setCountryCode("CN");
existing.setTimezone("UTC+8");
existing.setContactName("旧联系人");
existing.setContactPhone("旧电话");
existing.setContactEmail("old@example.com");
existing.setStatus("INACTIVE");
existing.setRemark("旧备注");
WarehouseAddressEntity address = new WarehouseAddressEntity();
address.setCountryCode("CN");
address.setStateProvince("广东省");
address.setCity("深圳市");
address.setAddressLine1("旧地址");
when(repository.findByCode(0L, "CN-SZX-01")).thenReturn(existing);
when(repository.findAddresses(0L, 9L)).thenReturn(java.util.List.of(address));
byte[] file = withRows(service.template(), new String[][] {
{"CN-SZX-01", "深圳作业仓", "", "", "广东省", "深圳市", "", "新地址", "", "", "ACTIVE", "新联系人", "新电话", "new@example.com", "新备注"}
});
WarehouseImportResult result = service.importFile(0L, "warehouses.xlsx", file, ImportMode.UPSERT);
assertThat(result.success()).isTrue();
assertThat(result.committed()).isTrue();
assertThat(result.updatedRows()).isEqualTo(1);
var captor = org.mockito.ArgumentCaptor.forClass(WarehouseRequests.SaveRequest.class);
verify(warehouseService).update(eq(0L), eq(9L), captor.capture());
assertThat(captor.getValue().contactName()).isEqualTo("旧联系人");
assertThat(captor.getValue().remark()).isEqualTo("旧备注");
assertThat(captor.getValue().status()).isEqualTo("INACTIVE");
}
@Test
@DisplayName("dryRun 只返回预检分类和计数,不写入仓库")
void shouldOnlyPreviewWhenDryRun() throws Exception {
byte[] file = withRows(service.template(), new String[][] {
{"CN-SZX-01", "深圳作业仓", "", "", "广东省", "深圳市", "南山区", "前海路 1 号", "518000", "", "", "张三", "13800138000", "", ""},
{"FBA-LAX9", "LAX9", "FBA", "US", "CA", "Redlands", "", "Amazon FC", "92374", "", "", "", "", "", ""}
});
WarehouseImportResult result = service.importFile(0L, "warehouses.xlsx", file, null, true);
assertThat(result.success()).isTrue();
assertThat(result.committed()).isFalse();
assertThat(result.createdRows()).isEqualTo(2);
assertThat(result.previewRows()).extracting("action").containsExactly("CREATE", "CREATE");
verify(warehouseService, never()).create(any());
verify(warehouseService, never()).update(org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), any());
}
@Test
@DisplayName("CREATE_ONLY 遇到现有仓库时不更新并按无变化统计")
void shouldSkipExistingWarehouseInCreateOnlyMode() throws Exception {
WarehouseEntity existing = new WarehouseEntity();
existing.setId(9L);
existing.setTenantId(0L);
existing.setWarehouseCode("CN-SZX-01");
existing.setWarehouseName("旧名称");
existing.setWarehouseType("DOMESTIC");
existing.setCountryCode("CN");
existing.setTimezone("UTC+8");
existing.setStatus("ACTIVE");
WarehouseAddressEntity address = new WarehouseAddressEntity();
address.setCountryCode("CN");
address.setStateProvince("广东省");
address.setCity("深圳市");
address.setAddressLine1("旧地址");
when(repository.findByCode(0L, "CN-SZX-01")).thenReturn(existing);
when(repository.findAddresses(0L, 9L)).thenReturn(java.util.List.of(address));
byte[] file = withRows(service.template(), new String[][] {
{"CN-SZX-01", "深圳作业仓", "", "", "广东省", "深圳市", "", "新地址", "", "", "", "", "", "", ""}
});
WarehouseImportResult result = service.importFile(0L, "warehouses.xlsx", file, ImportMode.CREATE_ONLY);
assertThat(result.success()).isTrue();
assertThat(result.committed()).isTrue();
assertThat(result.createdRows()).isZero();
assertThat(result.updatedRows()).isZero();
assertThat(result.unchangedRows()).isEqualTo(1);
assertThat(result.previewRows()).extracting("action").containsExactly("UNCHANGED");
assertThat(result.previewRows()).extracting("reason").containsExactly("仅新增模式,现有仓库不修改");
verify(warehouseService, never()).create(any());
verify(warehouseService, never()).update(org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), any());
}
private static byte[] withRows(byte[] template, String[][] values) throws Exception {
try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(template));
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
for (int rowIndex = 0; rowIndex < values.length; rowIndex++) {
Row row = workbook.getSheetAt(0).createRow(rowIndex + 3);
for (int column = 0; column < values[rowIndex].length; column++) {
row.createCell(column).setCellValue(values[rowIndex][column]);
}
}
workbook.write(output);
return output.toByteArray();
}
}
}
@@ -0,0 +1,48 @@
-- 运行前请先在同一连接中执行 warehouse/src/main/resources/db/warehouse-public-master-data.sql。
USE baiyue_warehouse;
SELECT COUNT(*) AS fixed_platform_warehouse_count
FROM wh_warehouse
WHERE tenant_id = 0
AND warehouse_code IN ('CN-YIWU-01', 'CN-DONGGUAN-01', 'US-LAX-01', 'US-NYC-01')
AND warehouse_type IN ('DOMESTIC', 'OVERSEAS')
AND deleted = 0;
SELECT COUNT(*) AS fixed_platform_address_count
FROM wh_warehouse_address address
JOIN wh_warehouse warehouse
ON warehouse.id = address.warehouse_id
AND warehouse.tenant_id = address.tenant_id
WHERE warehouse.tenant_id = 0
AND warehouse.warehouse_code IN ('CN-YIWU-01', 'CN-DONGGUAN-01', 'US-LAX-01', 'US-NYC-01')
AND address.address_type = 'OPERATING'
AND address.deleted = 0
AND warehouse.deleted = 0;
SELECT COUNT(*) AS fba_public_warehouse_count
FROM wh_warehouse
WHERE tenant_id = 0
AND warehouse_type = 'FBA'
AND country_code = 'US'
AND deleted = 0;
SELECT COUNT(*) AS fba_public_address_count
FROM wh_warehouse_address address
JOIN wh_warehouse warehouse
ON warehouse.id = address.warehouse_id
AND warehouse.tenant_id = address.tenant_id
WHERE warehouse.tenant_id = 0
AND warehouse.warehouse_type = 'FBA'
AND address.address_type = 'OPERATING'
AND address.country_code = 'US'
AND address.deleted = 0
AND warehouse.deleted = 0;
SELECT COUNT(*) AS fba_capability_violations
FROM wh_service_capability capability
JOIN wh_warehouse warehouse
ON warehouse.id = capability.warehouse_id
AND warehouse.tenant_id = capability.tenant_id
WHERE warehouse.tenant_id = 0
AND warehouse.warehouse_type = 'FBA'
AND capability.deleted = 0;
@@ -0,0 +1,116 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = dirname(fileURLToPath(import.meta.url))
const warehouseRoot = resolve(currentDir, '..', '..', '..')
const sqlPath = resolve(warehouseRoot, 'src/main/resources/db/warehouse-public-master-data.sql')
const acceptedPath = resolve(warehouseRoot, 'src/main/resources/db/master-data/fba-public-addresses.accepted.csv')
const rejectedPath = resolve(warehouseRoot, 'src/main/resources/db/master-data/fba-public-addresses.rejected.csv')
const fixedWarehousePath = resolve(warehouseRoot, 'src/main/resources/db/master-data/platform-fixed-warehouses.csv')
function parseCsv(text) {
const rows = []
let field = ''
let row = []
let quoted = false
for (let index = 0; index < text.length; index += 1) {
const char = text[index]
if (quoted) {
if (char === '"' && text[index + 1] === '"') {
field += '"'
index += 1
} else if (char === '"') {
quoted = false
} else {
field += char
}
} else if (char === '"') {
quoted = true
} else if (char === ',') {
row.push(field)
field = ''
} else if (char === '\n') {
row.push(field.replace(/\r$/, ''))
rows.push(row)
row = []
field = ''
} else {
field += char
}
}
if (field.length > 0 || row.length > 0) {
row.push(field)
rows.push(row)
}
const [headers, ...data] = rows.filter((item) => item.some((value) => value !== ''))
return data.map((values) => Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ''])))
}
const sql = readFileSync(sqlPath, 'utf8')
const accepted = parseCsv(readFileSync(acceptedPath, 'utf8'))
const rejected = parseCsv(readFileSync(rejectedPath, 'utf8'))
const fixedWarehouses = parseCsv(readFileSync(fixedWarehousePath, 'utf8'))
assert.equal(accepted.length, 252)
assert.equal(rejected.length, 54)
assert.equal(fixedWarehouses.length, 4)
const codes = new Set()
const warehouseIds = new Set()
const addressIds = new Set()
for (const row of accepted) {
assert.match(row.warehouse_code, /^[A-Z0-9]{3,5}$/)
assert.equal(row.warehouse_code, row.warehouse_code.trim().toUpperCase())
assert.ok(row.city.trim())
assert.ok(row.state_province.trim())
assert.ok(row.address_line1.trim())
assert.ok(row.postal_code.trim())
assert.ok(row.timezone.trim())
assert.equal(row.country_code, 'US')
assert.ok(!codes.has(row.warehouse_code), `duplicate code ${row.warehouse_code}`)
assert.ok(!warehouseIds.has(row.warehouse_id_seed), `duplicate warehouse id ${row.warehouse_id_seed}`)
assert.ok(!addressIds.has(row.address_id_seed), `duplicate address id ${row.address_id_seed}`)
codes.add(row.warehouse_code)
warehouseIds.add(row.warehouse_id_seed)
addressIds.add(row.address_id_seed)
}
assert.ok(rejected.some((row) => row.reject_reason.includes('non_fba_business_node')))
assert.equal(rejected.filter((row) => row.reject_reason.includes('timezone_pending_confirmation')).length, 1)
assert.deepEqual(fixedWarehouses.map((row) => row.warehouse_code), [
'CN-YIWU-01',
'CN-DONGGUAN-01',
'US-LAX-01',
'US-NYC-01',
])
assert.deepEqual(fixedWarehouses.map((row) => row.timezone), [
'UTC+8',
'UTC+8',
'UTC-8',
'UTC-5',
])
assert.ok(fixedWarehouses.every((row) => row.address_line1.includes('待维护') || row.address_line1.includes('pending maintenance')))
assert.match(sql, /CREATE TEMPORARY TABLE tmp_wh_fixed_platform_warehouse/)
assert.match(sql, /'CN-YIWU-01','义乌仓','DOMESTIC','CN','UTC\+8'/)
assert.match(sql, /'CN-DONGGUAN-01','东莞仓','DOMESTIC','CN','UTC\+8'/)
assert.match(sql, /'US-LAX-01','洛杉矶仓','OVERSEAS','US','UTC-8'/)
assert.match(sql, /'US-NYC-01','纽约仓','OVERSEAS','US','UTC-5'/)
assert.match(sql, /固定平台仓地址种子ID与既有数据冲突/)
assert.match(sql, /固定平台仓地址父子解析失败/)
assert.match(sql, /FROM tmp_wh_fixed_platform_warehouse source[\s\S]+ON DUPLICATE KEY UPDATE\s+tenant_id = VALUES\(tenant_id\);/)
assert.match(sql, /CREATE TEMPORARY TABLE tmp_wh_fba_public_master_data/)
assert.match(sql, /JOIN wh_warehouse warehouse\s+ON warehouse\.tenant_id = 0\s+AND warehouse\.warehouse_code = source\.warehouse_code/s)
assert.match(sql, /ON DUPLICATE KEY UPDATE\s+warehouse_type = VALUES\(warehouse_type\),\s+country_code = VALUES\(country_code\),\s+timezone = VALUES\(timezone\),\s+updated_at = CURRENT_TIMESTAMP\(3\);/s)
assert.doesNotMatch(sql, /status\s*=\s*VALUES\(status\)/)
assert.doesNotMatch(sql, /contact_name\s*=\s*VALUES\(contact_name\)/)
assert.doesNotMatch(sql, /contact_phone\s*=\s*VALUES\(contact_phone\)/)
assert.doesNotMatch(sql, /contact_email\s*=\s*VALUES\(contact_email\)/)
assert.doesNotMatch(sql, /remark\s*=\s*VALUES\(remark\)/)
assert.doesNotMatch(sql, /INSERT INTO wh_service_capability/i)
assert.match(sql, /FBA公共主数据地址种子ID与既有数据冲突/)
assert.match(sql, /FBA公共地址不得创建平台作业能力/)