暂存提交

This commit is contained in:
2026-07-31 14:57:09 +08:00
parent 7a19e2f096
commit f8804fbc52
53 changed files with 3771 additions and 0 deletions
@@ -0,0 +1,61 @@
package com.baiyue.warehouse.controller;
import io.swagger.v3.oas.annotations.Operation;
import org.junit.jupiter.api.Test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import static org.assertj.core.api.Assertions.assertThat;
class PlatformMasterDataControllerContractTest {
@Test
void 往来单位分页路径权限和接口说明完整() throws Exception {
assertContract(PlatformPartnerController.class, "platform:counterparty:list", "/api/platform/v1/partners");
assertMethod(PlatformPartnerController.class, "detail", GetMapping.class, "platform:counterparty:list");
assertMethod(PlatformPartnerController.class, "create", PostMapping.class, "platform:counterparty:create");
assertMethod(PlatformPartnerController.class, "update", PutMapping.class, "platform:counterparty:edit");
assertMethod(PlatformPartnerController.class, "changeStatus", PatchMapping.class, "platform:counterparty:status");
}
@Test
void 快递公司分页路径权限和接口说明完整() throws Exception {
assertContract(PlatformExpressCompanyController.class, "platform:express:list", "/api/platform/v1/express-companies");
assertMethod(PlatformExpressCompanyController.class, "detail", GetMapping.class, "platform:express:list");
assertMethod(PlatformExpressCompanyController.class, "create", PostMapping.class, "platform:express:create");
assertMethod(PlatformExpressCompanyController.class, "update", PutMapping.class, "platform:express:edit");
assertMethod(PlatformExpressCompanyController.class, "changeStatus", PatchMapping.class, "platform:express:status");
}
private static void assertContract(Class<?> controller, String authority, String path) throws Exception {
RequestMapping mapping = controller.getAnnotation(RequestMapping.class);
assertThat(mapping.value()).containsExactly(path);
Method page = controller.getDeclaredMethod("page",
com.baiyue.common.security.AuthenticatedUser.class,
long.class,
controller == PlatformPartnerController.class
? com.baiyue.warehouse.dto.MasterDataQueries.PartnerPageQuery.class
: com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery.class);
assertThat(page.getAnnotation(GetMapping.class)).isNotNull();
assertThat(page.getAnnotation(PreAuthorize.class).value()).contains(authority);
assertThat(page.getAnnotation(Operation.class).summary()).contains("分页查询");
}
private static void assertMethod(Class<?> controller, String methodName,
Class<? extends java.lang.annotation.Annotation> mapping,
String authority) {
Method method = java.util.Arrays.stream(controller.getDeclaredMethods())
.filter(candidate -> candidate.getName().equals(methodName))
.findFirst()
.orElseThrow();
assertThat(method.getAnnotation(mapping)).isNotNull();
assertThat(method.getAnnotation(PreAuthorize.class).value()).contains(authority);
assertThat(method.getAnnotation(Operation.class).summary()).isNotBlank();
}
}
@@ -0,0 +1,43 @@
package com.baiyue.warehouse.repository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.mapper.ExpressCompanyMapper;
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.mockito.ArgumentCaptor;
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.verify;
import static org.mockito.Mockito.when;
class ExpressCompanyRepositoryTest {
@BeforeAll
static void 初始化实体字段缓存() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "test"), ExpressCompanyEntity.class);
}
@Test
void 平台分页强制租户并支持筛选() {
ExpressCompanyMapper mapper = mock(ExpressCompanyMapper.class);
when(mapper.selectPage(any(Page.class), any())).thenAnswer(invocation -> invocation.getArgument(0));
new ExpressCompanyRepository(mapper).page(10001L,
new ExpressCompanyPageQuery(1, 20, "顺丰", "ACTIVE"));
@SuppressWarnings("unchecked")
ArgumentCaptor<LambdaQueryWrapper<ExpressCompanyEntity>> wrapper =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(mapper).selectPage(any(Page.class), wrapper.capture());
wrapper.getValue().getSqlSegment();
assertThat(wrapper.getValue().getParamNameValuePairs().values())
.contains(10001L, "ACTIVE", "%顺丰%");
}
}
@@ -0,0 +1,62 @@
package com.baiyue.warehouse.repository;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class WarehouseSchemaContractTest {
@Test
void 仓库三张表包含实体全部持久化字段() throws IOException {
String sql = read("warehouse.sql");
assertColumns(sql, "wh_warehouse", "warehouse_code", "warehouse_name", "warehouse_type", "country_code",
"timezone", "contact_name", "contact_phone", "contact_email", "status", "remark");
assertColumns(sql, "wh_warehouse_address", "warehouse_id", "address_type", "country_code", "state_province",
"city", "district", "postal_code", "address_line1", "address_line2", "latitude", "longitude");
assertColumns(sql, "wh_service_capability", "warehouse_id", "capability_code", "capability_name", "enabled",
"cutoff_time", "description");
assertBaseColumns(sql);
}
@Test
void 往来单位表包含实体全部持久化字段() throws IOException {
String sql = read("warehouse-partner.sql");
assertColumns(sql, "wh_partner", "partner_code", "partner_name", "partner_type", "contact_name",
"contact_phone", "contact_email", "country_code", "province", "city", "district", "address_line",
"postal_code", "status");
assertBaseColumns(sql);
}
@Test
void 快递公司表包含实体全部持久化字段() throws IOException {
String sql = read("warehouse-express-company.sql");
assertColumns(sql, "wh_express_company", "company_code", "company_name", "service_phone", "website",
"tracking_url_template", "status", "remark");
assertBaseColumns(sql);
}
private static String read(String file) throws IOException {
return Files.readString(Path.of("src/main/resources/db", file));
}
private static void assertColumns(String sql, String table, String... columns) {
assertThat(sql).contains("CREATE TABLE IF NOT EXISTS " + table);
for (String column : columns) {
assertThat(sql).contains(column + " ");
}
}
private static void assertBaseColumns(String sql) {
assertColumns(sql, tableName(sql), "tenant_id", "created_by", "created_at", "updated_by", "updated_at",
"deleted", "version");
}
private static String tableName(String sql) {
int start = sql.indexOf("CREATE TABLE IF NOT EXISTS ") + "CREATE TABLE IF NOT EXISTS ".length();
int end = sql.indexOf(' ', start);
return sql.substring(start, end);
}
}
@@ -0,0 +1,196 @@
package com.baiyue.warehouse.service;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanySaveRequest;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerSaveRequest;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.repository.ExpressCompanyRepository;
import com.baiyue.warehouse.repository.PartnerRepository;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataIntegrityViolationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.doAnswer;
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 PlatformMasterDataManagementServiceTest {
@Test
void 往来单位详情强制租户隔离() {
PartnerRepository repository = mock(PartnerRepository.class);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
assertThatThrownBy(() -> service.detail(10002L, 1L)).isInstanceOf(BizException.class);
verify(repository).findOwned(10002L, 1L);
}
@Test
void 往来单位编码在租户内必须唯一() {
PartnerRepository repository = mock(PartnerRepository.class);
when(repository.existsCode(10001L, "CUS-001", null)).thenReturn(true);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
assertThatThrownBy(() -> service.create(10001L, partnerRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
verify(repository, never()).insert(any());
}
@Test
void 往来单位并发重复编码统一返回业务冲突() {
PartnerRepository repository = mock(PartnerRepository.class);
doThrow(new DataIntegrityViolationException("duplicate")).when(repository).insert(any());
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
assertThatThrownBy(() -> service.create(10001L, partnerRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
}
@Test
void 往来单位新增写入租户和请求字段() {
PartnerRepository repository = mock(PartnerRepository.class);
doAnswer(invocation -> {
PartnerEntity entity = invocation.getArgument(0);
entity.setId(18L);
return null;
}).when(repository).insert(any());
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
assertThat(service.create(10001L, partnerRequest())).isEqualTo(18L);
org.mockito.ArgumentCaptor<PartnerEntity> entity = org.mockito.ArgumentCaptor.forClass(PartnerEntity.class);
verify(repository).insert(entity.capture());
assertThat(entity.getValue().getTenantId()).isEqualTo(10001L);
assertThat(entity.getValue().getPartnerCode()).isEqualTo("CUS-001");
assertThat(entity.getValue().getPartnerName()).isEqualTo("示例客户");
}
@Test
void 往来单位编辑保留归属并更新字段() {
PartnerRepository repository = mock(PartnerRepository.class);
PartnerEntity entity = new PartnerEntity();
entity.setId(19L);
entity.setTenantId(10001L);
when(repository.findOwned(10001L, 19L)).thenReturn(entity);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
service.update(10001L, 19L, partnerRequest());
assertThat(entity.getTenantId()).isEqualTo(10001L);
assertThat(entity.getPartnerName()).isEqualTo("示例客户");
verify(repository).save(entity);
}
@Test
void 往来单位停用保留主数据() {
PartnerRepository repository = mock(PartnerRepository.class);
PartnerEntity entity = new PartnerEntity();
entity.setId(8L);
entity.setTenantId(10001L);
entity.setStatus("ACTIVE");
when(repository.findOwned(10001L, 8L)).thenReturn(entity);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
service.changeStatus(10001L, 8L, "INACTIVE");
assertThat(entity.getStatus()).isEqualTo("INACTIVE");
verify(repository).save(entity);
}
@Test
void 快递公司详情强制租户隔离() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
assertThatThrownBy(() -> service.detail(10002L, 1L)).isInstanceOf(BizException.class);
verify(repository).findOwned(10002L, 1L);
}
@Test
void 快递公司编码在租户内必须唯一() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
when(repository.existsCode(10001L, "SF", null)).thenReturn(true);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
assertThatThrownBy(() -> service.create(10001L, expressRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
verify(repository, never()).insert(any());
}
@Test
void 快递公司并发重复编码统一返回业务冲突() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
doThrow(new DataIntegrityViolationException("duplicate")).when(repository).insert(any());
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
assertThatThrownBy(() -> service.create(10001L, expressRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
}
@Test
void 快递公司新增写入租户和请求字段() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
doAnswer(invocation -> {
ExpressCompanyEntity entity = invocation.getArgument(0);
entity.setId(28L);
return null;
}).when(repository).insert(any());
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
assertThat(service.create(10001L, expressRequest())).isEqualTo(28L);
org.mockito.ArgumentCaptor<ExpressCompanyEntity> entity =
org.mockito.ArgumentCaptor.forClass(ExpressCompanyEntity.class);
verify(repository).insert(entity.capture());
assertThat(entity.getValue().getTenantId()).isEqualTo(10001L);
assertThat(entity.getValue().getCompanyCode()).isEqualTo("SF");
assertThat(entity.getValue().getCompanyName()).isEqualTo("顺丰速运");
}
@Test
void 快递公司编辑保留归属并更新字段() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyEntity entity = new ExpressCompanyEntity();
entity.setId(29L);
entity.setTenantId(10001L);
when(repository.findOwned(10001L, 29L)).thenReturn(entity);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
service.update(10001L, 29L, expressRequest());
assertThat(entity.getTenantId()).isEqualTo(10001L);
assertThat(entity.getCompanyName()).isEqualTo("顺丰速运");
verify(repository).save(entity);
}
@Test
void 快递公司停用保留主数据() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyEntity entity = new ExpressCompanyEntity();
entity.setId(9L);
entity.setTenantId(10001L);
entity.setStatus("ACTIVE");
when(repository.findOwned(10001L, 9L)).thenReturn(entity);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
service.changeStatus(10001L, 9L, "INACTIVE");
assertThat(entity.getStatus()).isEqualTo("INACTIVE");
verify(repository).save(entity);
}
private static PartnerSaveRequest partnerRequest() {
return new PartnerSaveRequest("CUS-001", "示例客户", "CUSTOMER", "张三", "13800138000",
"test@example.com", "CN", "广东省", "深圳市", "南山区", "示例地址", "518000", "ACTIVE");
}
private static ExpressCompanySaveRequest expressRequest() {
return new ExpressCompanySaveRequest("SF", "顺丰速运", "95338", "https://www.sf-express.com",
"https://example.com/{trackingNo}", "ACTIVE", null);
}
}