格式修改

This commit is contained in:
2026-08-05 10:35:07 +08:00
parent 81c9c92f5d
commit 496fc58a90
241 changed files with 5600 additions and 2088 deletions
@@ -9,6 +9,8 @@ import com.baiyue.warehouse.service.PalletLocationAllocationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -25,13 +27,15 @@ import java.util.List;
@RequestMapping("/internal/warehouse/v1/pallet-locations")
@Tag(name = "托盘库位内部接口", description = "内部服务令牌保护的预占、确认和补偿释放")
public class InternalPalletLocationController {
private final PalletLocationAllocationService service;
private final byte[] expectedToken;
@Resource
private PalletLocationAllocationService service;
@Value("${baiyue.security.internal-service-token}")
private String internalToken;
private byte[] expectedToken;
public InternalPalletLocationController(PalletLocationAllocationService service,
@Value("${baiyue.security.internal-service-token}") String token) {
this.service = service;
this.expectedToken = token.getBytes(StandardCharsets.UTF_8);
@PostConstruct
void initializeToken() {
this.expectedToken = internalToken.getBytes(StandardCharsets.UTF_8);
}
@PostMapping("/reservations")
@@ -6,6 +6,7 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.service.PartnerQueryService;
import jakarta.annotation.Resource;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
@@ -17,11 +18,13 @@ public class InternalPartnerController {
@Resource
private PartnerQueryService service;
private final byte[] expected;
@Value("${baiyue.security.internal-service-token}")
private String internalToken;
private byte[] expected;
public InternalPartnerController(
@Value("${baiyue.security.internal-service-token}") String token) {
this.expected=token.getBytes(StandardCharsets.UTF_8);
@PostConstruct
void initializeToken() {
this.expected = internalToken.getBytes(StandardCharsets.UTF_8);
}
@GetMapping("/{tenantId}/{id}/active")
public ApiResponse<PartnerEntity> active(@RequestHeader(value="X-Internal-Service-Token",required=false) String token,
@@ -6,6 +6,7 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.WarehouseDetailResponse;
import com.baiyue.warehouse.service.WarehouseApplicationService;
import jakarta.annotation.Resource;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
@@ -17,11 +18,13 @@ public class InternalWarehouseController {
@Resource
private WarehouseApplicationService service;
private final byte[] expected;
@Value("${baiyue.security.internal-service-token}")
private String internalToken;
private byte[] expected;
public InternalWarehouseController(
@Value("${baiyue.security.internal-service-token}") String token) {
this.expected = token.getBytes(StandardCharsets.UTF_8);
@PostConstruct
void initializeToken() {
this.expected = internalToken.getBytes(StandardCharsets.UTF_8);
}
@GetMapping("/{tenantId}/{id}/active")
public ApiResponse<WarehouseDetailResponse> active(
@@ -11,6 +11,7 @@ import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanySaveRequest;
import com.baiyue.warehouse.dto.MasterDataQueries.StatusRequest;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.service.ExpressCompanyQueryService;
import jakarta.annotation.Resource;
import com.baiyue.warehouse.service.ExpressCompanyImportService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -47,13 +48,10 @@ import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/api/platform/v1/express-companies")
public class PlatformExpressCompanyController {
private final ExpressCompanyQueryService service;
private final ExpressCompanyImportService importService;
public PlatformExpressCompanyController(ExpressCompanyQueryService service, ExpressCompanyImportService importService) {
this.service = service;
this.importService = importService;
}
@Resource
private ExpressCompanyQueryService service;
@Resource
private ExpressCompanyImportService importService;
@GetMapping
@PreAuthorize("hasAuthority('platform:express:list')")
@@ -11,6 +11,7 @@ import com.baiyue.warehouse.dto.LocationDtos.SaveRequest;
import com.baiyue.warehouse.dto.LocationDtos.StatusRequest;
import com.baiyue.warehouse.entity.LocationEntity;
import com.baiyue.warehouse.service.LocationApplicationService;
import jakarta.annotation.Resource;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@@ -41,11 +42,8 @@ import java.util.List;
@RequestMapping("/api/platform/v1/locations")
@Tag(name = "平台库位管理", description = "按企业租户和仓库数据范围管理库位基础资料")
public class PlatformLocationController {
private final LocationApplicationService service;
public PlatformLocationController(LocationApplicationService service) {
this.service = service;
}
@Resource
private LocationApplicationService service;
@GetMapping
@PreAuthorize("hasAuthority('platform:location:list')")
@@ -7,6 +7,7 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.warehouse.dto.PalletRequests;
import com.baiyue.warehouse.service.PalletApplicationService;
import jakarta.annotation.Resource;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@@ -33,11 +34,8 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/platform/v1/pallets")
@Tag(name = "平台托盘管理", description = "按租户和仓库维护托盘主数据")
public class PlatformPalletController {
private final PalletApplicationService service;
public PlatformPalletController(PalletApplicationService service) {
this.service = service;
}
@Resource
private PalletApplicationService service;
@GetMapping
@PreAuthorize("hasAuthority('platform:pallet:list')")
@@ -11,6 +11,7 @@ import com.baiyue.warehouse.dto.MasterDataQueries.PartnerImportResult;
import com.baiyue.warehouse.dto.MasterDataQueries.StatusRequest;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.service.PlatformPartnerQueryService;
import jakarta.annotation.Resource;
import com.baiyue.warehouse.service.PartnerImportService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -47,13 +48,10 @@ import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/api/platform/v1/partners")
public class PlatformPartnerController {
private final PlatformPartnerQueryService service;
private final PartnerImportService importService;
public PlatformPartnerController(PlatformPartnerQueryService service, PartnerImportService importService) {
this.service = service;
this.importService = importService;
}
@Resource
private PlatformPartnerQueryService service;
@Resource
private PartnerImportService importService;
@GetMapping("/import-template")
@PreAuthorize("hasAuthority('platform:counterparty:import')")
@@ -2,6 +2,7 @@ package com.baiyue.warehouse.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.warehouse.service.WarehouseSchemaReadinessService;
import jakarta.annotation.Resource;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
@@ -18,11 +19,8 @@ import java.util.Map;
@RestController
@RequestMapping("/api/platform/v1/warehouses")
public class WarehouseHealthController {
private final WarehouseSchemaReadinessService readinessService;
public WarehouseHealthController(WarehouseSchemaReadinessService readinessService) {
this.readinessService = readinessService;
}
@Resource
private WarehouseSchemaReadinessService readinessService;
@Operation(summary = "Get service status / 查询服务状态", description = "Returns the logical status of warehouse-service. / 返回 warehouse-service 的逻辑运行状态。")
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Service is available / 服务可用")
@@ -6,17 +6,15 @@ import com.baiyue.common.core.api.PageResult;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.mapper.ExpressCompanyMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
/** 快递公司主数据仓储;平台分页租户条件可选,资源读写仍强制租户边界。 */
@Repository
public class ExpressCompanyRepository {
private final ExpressCompanyMapper mapper;
public ExpressCompanyRepository(ExpressCompanyMapper mapper) {
this.mapper = mapper;
}
@Resource
private ExpressCompanyMapper mapper;
public PageResult<ExpressCompanyEntity> page(Long tenantId, ExpressCompanyPageQuery query) {
LambdaQueryWrapper<ExpressCompanyEntity> wrapper = new LambdaQueryWrapper<ExpressCompanyEntity>()
@@ -13,6 +13,7 @@ import com.baiyue.warehouse.mapper.LocationMapper;
import com.baiyue.warehouse.mapper.LocationOccupancyMapper;
import com.baiyue.warehouse.mapper.LocationReservationMapper;
import com.baiyue.warehouse.mapper.WarehouseMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Repository;
import java.util.Collection;
@@ -21,18 +22,14 @@ import java.util.List;
/** 库位持久化边界;平台分页条件可选,资源读写继续强制租户、仓库联合条件。 */
@Repository
public class LocationRepository {
private final LocationMapper locations;
private final LocationReservationMapper reservations;
private final LocationOccupancyMapper occupancies;
private final WarehouseMapper warehouses;
public LocationRepository(LocationMapper locations, LocationReservationMapper reservations,
LocationOccupancyMapper occupancies, WarehouseMapper warehouses) {
this.locations = locations;
this.reservations = reservations;
this.occupancies = occupancies;
this.warehouses = warehouses;
}
@Resource
private LocationMapper locations;
@Resource
private LocationReservationMapper reservations;
@Resource
private LocationOccupancyMapper occupancies;
@Resource
private WarehouseMapper warehouses;
public WarehouseEntity findWarehouse(long tenantId, long warehouseId) {
return warehouses.selectOne(new LambdaQueryWrapper<WarehouseEntity>()
@@ -7,6 +7,7 @@ import com.baiyue.common.core.api.PageResult;
import com.baiyue.warehouse.dto.PalletRequests;
import com.baiyue.warehouse.entity.PalletEntity;
import com.baiyue.warehouse.mapper.PalletMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
@@ -16,11 +17,8 @@ import java.util.List;
/** 托盘持久化仓储;平台分页支持跨租户筛选,资源操作始终包含租户和仓库边界。 */
@Repository
public class PalletRepository {
private final PalletMapper mapper;
public PalletRepository(PalletMapper mapper) {
this.mapper = mapper;
}
@Resource
private PalletMapper mapper;
public PageResult<PalletEntity> page(Long tenantId, PalletRequests.PageQuery query) {
LambdaQueryWrapper<PalletEntity> wrapper = new LambdaQueryWrapper<PalletEntity>()
@@ -6,6 +6,7 @@ import com.baiyue.common.core.api.PageResult;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerPageQuery;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.mapper.PartnerMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import java.util.List;
@@ -15,8 +16,8 @@ import java.util.Map;
@Repository
public class PartnerRepository {
private final PartnerMapper mapper;
public PartnerRepository(PartnerMapper mapper) { this.mapper = mapper; }
@Resource
private PartnerMapper mapper;
public List<PartnerEntity> activeCandidates(long tenantId, String keyword, String type) {
List<Long> typedIds = StringUtils.hasText(type)
? typedIds(tenantId, type)
@@ -10,6 +10,7 @@ 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 jakarta.annotation.Resource;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
@@ -20,17 +21,12 @@ import java.util.List;
*/
@Repository
public class WarehouseRepository {
private final WarehouseMapper warehouses;
private final WarehouseAddressMapper addresses;
private final ServiceCapabilityMapper capabilities;
public WarehouseRepository(WarehouseMapper warehouses,
WarehouseAddressMapper addresses,
ServiceCapabilityMapper capabilities) {
this.warehouses = warehouses;
this.addresses = addresses;
this.capabilities = capabilities;
}
@Resource
private WarehouseMapper warehouses;
@Resource
private WarehouseAddressMapper addresses;
@Resource
private ServiceCapabilityMapper capabilities;
public void insertWarehouse(WarehouseEntity warehouse) {
warehouses.insert(warehouse);
@@ -5,6 +5,7 @@ import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyImportError;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyImportResult;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.repository.ExpressCompanyRepository;
import jakarta.annotation.Resource;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
@@ -34,11 +35,8 @@ public class ExpressCompanyImportService {
"快递公司编码*", "快递公司名称*", "客服电话", "官方网站", "轨迹查询地址模板", "状态*", "备注"
};
private final ExpressCompanyRepository repository;
public ExpressCompanyImportService(ExpressCompanyRepository repository) {
this.repository = repository;
}
@Resource
private ExpressCompanyRepository repository;
public byte[] template() {
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) {
@@ -8,6 +8,7 @@ import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanyPageQuery;
import com.baiyue.warehouse.dto.MasterDataQueries.ExpressCompanySaveRequest;
import com.baiyue.warehouse.entity.ExpressCompanyEntity;
import com.baiyue.warehouse.repository.ExpressCompanyRepository;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.transaction.annotation.Transactional;
@@ -15,11 +16,8 @@ import org.springframework.transaction.annotation.Transactional;
/** 平台快递公司管理服务。 */
@Service
public class ExpressCompanyQueryService {
private final ExpressCompanyRepository repository;
public ExpressCompanyQueryService(ExpressCompanyRepository repository) {
this.repository = repository;
}
@Resource
private ExpressCompanyRepository repository;
public PageResult<ExpressCompanyEntity> page(Long tenantId, ExpressCompanyPageQuery query) {
return repository.page(tenantId, query);
@@ -10,6 +10,7 @@ import com.baiyue.warehouse.dto.LocationDtos.SaveRequest;
import com.baiyue.warehouse.entity.LocationEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.LocationRepository;
import jakarta.annotation.Resource;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -19,11 +20,8 @@ import java.util.List;
/** 库位基础资料应用服务,负责平台分页、CRUD、租户仓库隔离和启停/删除约束。 */
@Service
public class LocationApplicationService {
private final LocationRepository repository;
public LocationApplicationService(LocationRepository repository) {
this.repository = repository;
}
@Resource
private LocationRepository repository;
public PageResult<LocationEntity> page(Long tenantId, PageQuery query) {
return repository.page(tenantId, query);
@@ -16,6 +16,7 @@ import com.baiyue.warehouse.entity.LocationOccupancyEntity;
import com.baiyue.warehouse.entity.LocationReservationEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.LocationRepository;
import jakarta.annotation.Resource;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -43,11 +44,8 @@ import java.util.Set;
@Service
@Validated
public class LocationResourceService {
private final LocationRepository repository;
public LocationResourceService(LocationRepository repository) {
this.repository = repository;
}
@Resource
private LocationRepository repository;
public List<LocationEntity> findAvailable(@Min(1) long tenantId, @Min(1) long warehouseId,
@Min(1) @Max(200) int limit) {
@@ -8,6 +8,7 @@ import com.baiyue.warehouse.dto.PalletRequests;
import com.baiyue.warehouse.entity.PalletEntity;
import com.baiyue.warehouse.repository.PalletRepository;
import com.baiyue.warehouse.repository.WarehouseRepository;
import jakarta.annotation.Resource;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -15,13 +16,10 @@ import org.springframework.transaction.annotation.Transactional;
/** 托盘主数据应用服务。 */
@Service
public class PalletApplicationService {
private final PalletRepository repository;
private final WarehouseRepository warehouseRepository;
public PalletApplicationService(PalletRepository repository, WarehouseRepository warehouseRepository) {
this.repository = repository;
this.warehouseRepository = warehouseRepository;
}
@Resource
private PalletRepository repository;
@Resource
private WarehouseRepository warehouseRepository;
public PageResult<PalletRequests.View> page(Long tenantId, PalletRequests.PageQuery query) {
PageResult<PalletEntity> source = repository.page(tenantId, query);
@@ -7,6 +7,7 @@ import com.baiyue.warehouse.dto.LocationDtos;
import com.baiyue.warehouse.dto.PalletAllocationDtos;
import com.baiyue.warehouse.entity.PalletEntity;
import com.baiyue.warehouse.repository.PalletRepository;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -21,17 +22,12 @@ import java.util.List;
public class PalletLocationAllocationService {
/** 测量完成后默认推荐的普通托盘类型编码。 */
public static final String DEFAULT_PALLET_TYPE = "STANDARD";
private final PalletRepository palletRepository;
private final PalletVolumeSplitter splitter;
private final LocationResourceService locationResourceService;
public PalletLocationAllocationService(PalletRepository palletRepository,
PalletVolumeSplitter splitter,
LocationResourceService locationResourceService) {
this.palletRepository = palletRepository;
this.splitter = splitter;
this.locationResourceService = locationResourceService;
}
@Resource
private PalletRepository palletRepository;
@Resource
private PalletVolumeSplitter splitter;
@Resource
private LocationResourceService locationResourceService;
@Transactional
@BusinessAudit(event = "PALLET_LOCATION_RESERVE", resourceType = "INBOUND", resourceId = "#request.businessId", description = "预占托盘和库位", captureBefore = false)
@@ -5,6 +5,7 @@ import com.baiyue.warehouse.dto.MasterDataQueries.PartnerImportError;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerImportResult;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.repository.PartnerRepository;
import jakarta.annotation.Resource;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
@@ -39,11 +40,8 @@ public class PartnerImportService {
"国家二字码*", "省/州", "城市*", "区/县", "详细地址*", "邮政编码", "状态*", "备注"
};
private final PartnerRepository repository;
public PartnerImportService(PartnerRepository repository) {
this.repository = repository;
}
@Resource
private PartnerRepository repository;
public byte[] template() {
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) {
@@ -4,13 +4,14 @@ import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.repository.PartnerRepository;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PartnerQueryService {
private final PartnerRepository repository;
public PartnerQueryService(PartnerRepository repository) { this.repository = repository; }
@Resource
private PartnerRepository repository;
public List<PartnerEntity> candidates(long tenantId, String keyword, String type) {
if (type != null && !type.isBlank() && !"SHIPPER".equals(type) && !"CONSIGNEE".equals(type)) {
throw new BizException(ResultCode.BAD_REQUEST, "候选类型只能是 SHIPPER 或 CONSIGNEE");
@@ -8,6 +8,7 @@ import com.baiyue.warehouse.dto.MasterDataQueries.PartnerPageQuery;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerSaveRequest;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.repository.PartnerRepository;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.transaction.annotation.Transactional;
@@ -19,11 +20,8 @@ import java.util.Set;
/** 平台往来单位管理服务。 */
@Service
public class PlatformPartnerQueryService {
private final PartnerRepository repository;
public PlatformPartnerQueryService(PartnerRepository repository) {
this.repository = repository;
}
@Resource
private PartnerRepository repository;
public PageResult<PartnerEntity> page(Long tenantId, PartnerPageQuery query) {
return repository.page(tenantId, query);
@@ -2,6 +2,7 @@ package com.baiyue.warehouse.service;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
@@ -24,11 +25,8 @@ public class WarehouseSchemaReadinessService {
"SELECT id, tenant_id, warehouse_id, pallet_id, location_id, shared_overflow, occupancy_status, active_location_occupancy, active_pallet_occupancy, version FROM wh_location_occupancy LIMIT 0"
);
private final JdbcTemplate jdbcTemplate;
public WarehouseSchemaReadinessService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Resource
private JdbcTemplate jdbcTemplate;
public Readiness readiness() {
try {
+1 -1
View File
@@ -35,6 +35,6 @@ logging:
rollingpolicy:
file-name-pattern: "${LOGGING_ROLLING_FILE_NAME_PATTERN:${LOG_PATH:./logs}/${spring.application.name}.%d{yyyy-MM-dd}.%i.log.gz}"
max-file-size: "${LOGGING_ROLLING_MAX_FILE_SIZE:100MB}"
max-history: "${LOGGING_ROLLING_MAX_HISTORY:30}"
max-history: "${LOGGING_ROLLING_MAX_HISTORY:7}"
total-size-cap: "${LOGGING_ROLLING_TOTAL_SIZE_CAP:5GB}"
clean-history-on-start: "${LOGGING_ROLLING_CLEAN_HISTORY_ON_START:false}"
@@ -10,7 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class PlatformExpressCompanyAllTenantQueryContractTest {
@Test
void 分页租户参数可选但详情写入启停和导入仍然必填() {
void keepsTenantRequiredOutsidePaging() {
Method page = method("page");
assertThat(page.getParameterTypes()[1]).isEqualTo(Long.class);
assertThat(page.getParameters()[1].getAnnotation(RequestParam.class).required()).isFalse();
@@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class PlatformLocationControllerContractTest {
@Test
void 库位接口路径权限和Swagger说明完整() {
void shouldHandleCase01() {
assertThat(PlatformLocationController.class.getAnnotation(RequestMapping.class).value())
.containsExactly("/api/platform/v1/locations");
assertMethod("page", GetMapping.class, "platform:location:list");
@@ -31,7 +31,7 @@ class PlatformLocationControllerContractTest {
}
@Test
void 分页查询的企业租户参数可选但写接口仍然必填() {
void keepsTenantRequiredForWrites() {
Method page = method("page");
assertThat(page.getParameterTypes()[1]).isEqualTo(Long.class);
assertThat(page.getParameters()[1].getAnnotation(RequestParam.class).required()).isFalse();
@@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class PlatformMasterDataControllerContractTest {
@Test
void 往来单位分页支持全部业务类型并拒绝未知类型() {
void shouldHandleCase01() {
try (var factory = Validation.buildDefaultValidatorFactory()) {
var validator = factory.getValidator();
for (String type : java.util.List.of(
@@ -36,7 +36,7 @@ class PlatformMasterDataControllerContractTest {
}
@Test
void 仓库平台接口按读取新增编辑和启停权限隔离() {
void shouldHandleCase02() {
RequestMapping mapping = PlatformWarehouseController.class.getAnnotation(RequestMapping.class);
assertThat(mapping.value()).containsExactly("/api/platform/v1/warehouse-admin");
@@ -48,7 +48,7 @@ class PlatformMasterDataControllerContractTest {
}
@Test
void 往来单位分页路径权限和接口说明完整() throws Exception {
void shouldHandleCase03() 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");
@@ -59,7 +59,7 @@ class PlatformMasterDataControllerContractTest {
}
@Test
void 快递公司分页路径权限和接口说明完整() throws Exception {
void shouldHandleCase04() 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");
@@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class PlatformPalletControllerContractTest {
@Test
void 托盘接口路径和按钮权限完整() {
void shouldHandleCase01() {
assertThat(PlatformPalletController.class.getAnnotation(RequestMapping.class).value())
.containsExactly("/api/platform/v1/pallets");
assertMethod("page", GetMapping.class, "platform:pallet:list");
@@ -15,7 +15,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class PlatformPartnerAllTenantQueryContractTest {
@Test
void 平台分页租户可选但详情写入和导入仍要求租户() {
void keepsTenantRequiredOutsidePaging() {
Method page = method(PlatformPartnerController.class, "page");
assertThat(page.getParameterTypes()[1]).isEqualTo(Long.class);
assertThat(page.getParameters()[1].getAnnotation(RequestParam.class).required()).isFalse();
@@ -28,7 +28,7 @@ class PlatformPartnerAllTenantQueryContractTest {
}
@Test
void 分页链路使用可选租户而其他仓储方法保持原有强租户边界() throws Exception {
void keepsTenantScopeForOtherMethods() throws Exception {
assertThat(PlatformPartnerQueryService.class.getMethod("page", Long.class, PartnerPageQuery.class))
.isNotNull();
assertThat(PartnerRepository.class.getMethod("page", Long.class, PartnerPageQuery.class))
@@ -40,7 +40,7 @@ class PlatformPartnerAllTenantQueryContractTest {
}
@Test
void 类型筛选在数据库内关联分页且租户条件可选() throws Exception {
void supportsOptionalTenantByType() throws Exception {
Method method = PartnerMapper.class.getMethod("selectPartnerPageByType",
Page.class, Long.class, String.class, String.class, String.class);
String sql = String.join("\n", method.getAnnotation(Select.class).value());
@@ -11,7 +11,7 @@ import static org.mockito.Mockito.when;
class WarehouseDataAccessExceptionHandlerTest {
@Test
void 数据源故障统一返回503并保留请求标识() {
void shouldHandleCase01() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getAttribute("requestId")).thenReturn("request-1");
when(request.getAttribute("traceId")).thenReturn("trace-1");
@@ -1,20 +1,25 @@
package com.baiyue.warehouse.controller;
import com.baiyue.warehouse.service.WarehouseSchemaReadinessService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(WarehouseHealthController.class)
class WarehouseHealthControllerTest {
@MockBean private WarehouseSchemaReadinessService service;
@Resource private WarehouseHealthController controller;
@Test
void 表结构漂移时状态接口返回503() {
WarehouseSchemaReadinessService service = mock(WarehouseSchemaReadinessService.class);
void shouldHandleCase01() {
when(service.readiness()).thenReturn(
new WarehouseSchemaReadinessService.Readiness(false, "DEGRADED", "需要执行迁移"));
var response = new WarehouseHealthController(service).status();
var response = controller.status();
assertThat(response.getStatusCode().value()).isEqualTo(503);
assertThat(response.getBody()).isNotNull();
@@ -11,7 +11,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class LocationMapperContractTest {
@Test
void 平台分页的企业和仓库条件均为可选且仍返回仓库快照() throws NoSuchMethodException {
void supportsOptionalPageFilters() throws NoSuchMethodException {
Method page = LocationMapper.class.getMethod("selectLocationPage", Page.class,
Long.class, Long.class, String.class, String.class, String.class, String.class);
String pageSql = sql(page.getAnnotation(Select.class).value());
@@ -27,7 +27,7 @@ class LocationMapperContractTest {
}
@Test
void 可用库位按库区和库位数字自然顺序临近分配() throws NoSuchMethodException {
void shouldHandleCase01() throws NoSuchMethodException {
String selectSql = sql(LocationMapper.class.getMethod(
"selectAvailable", long.class, long.class, int.class).getAnnotation(Select.class).value());
String lockSql = sql(LocationMapper.class.getMethod(
@@ -39,7 +39,7 @@ class LocationMapperContractTest {
}
@Test
void A0依靠唯一编码并发创建后加锁复用() throws NoSuchMethodException {
void shouldHandleCase02() throws NoSuchMethodException {
Method insert = LocationMapper.class.getMethod(
"insertSharedOverflowIfAbsent", long.class, long.class, long.class, long.class);
Method lock = LocationMapper.class.getMethod("lockSharedOverflow", long.class, long.class);
@@ -11,6 +11,9 @@ import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -18,18 +21,20 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(ExpressCompanyRepository.class)
class ExpressCompanyRepositoryTest {
@MockBean private ExpressCompanyMapper mapper;
@Resource private ExpressCompanyRepository repository;
@BeforeAll
static void 初始化实体字段缓存() {
static void setUpCase01() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "test"), ExpressCompanyEntity.class);
}
@Test
void 平台分页传入租户时按租户并支持筛选() {
ExpressCompanyMapper mapper = mock(ExpressCompanyMapper.class);
void shouldHandleCase02() {
when(mapper.selectPage(any(Page.class), any())).thenAnswer(invocation -> invocation.getArgument(0));
new ExpressCompanyRepository(mapper).page(10001L,
repository.page(10001L,
new ExpressCompanyPageQuery(1, 20, "顺丰", "ACTIVE"));
@SuppressWarnings("unchecked")
@@ -42,11 +47,10 @@ class ExpressCompanyRepositoryTest {
}
@Test
void 平台分页不传租户时查询全部企业并保留其他筛选() {
ExpressCompanyMapper mapper = mock(ExpressCompanyMapper.class);
void pagesAllTenantsWithFilters() {
when(mapper.selectPage(any(Page.class), any())).thenAnswer(invocation -> invocation.getArgument(0));
new ExpressCompanyRepository(mapper).page(null,
repository.page(null,
new ExpressCompanyPageQuery(1, 20, "顺丰", "ACTIVE"));
@SuppressWarnings("unchecked")
@@ -11,6 +11,9 @@ import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.Collection;
@@ -20,37 +23,39 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(PalletRepository.class)
class PalletRepositoryTest {
@MockBean private PalletMapper mapper;
@Resource private PalletRepository repository;
@BeforeAll
static void 初始化实体字段缓存() {
static void initEntityMetadata() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "test"), PalletEntity.class);
}
@Test
void 平台分页默认不限制企业和仓库() {
void pagesWithoutFilters() {
Collection<Object> values = queryValues(null, null);
assertThat(values).isEmpty();
}
@Test
void 平台分页可单独按企业筛选() {
void pagesByTenant() {
Collection<Object> values = queryValues(10001L, null);
assertThat(values).containsExactly(10001L);
}
@Test
void 平台分页可同时按企业和仓库筛选() {
void pagesByTenantAndWarehouse() {
Collection<Object> values = queryValues(10001L, 20L);
assertThat(values).containsExactlyInAnyOrder(10001L, 20L);
}
@Test
void 平台分页可按全局唯一仓库筛选且写查询仍保留联合边界() {
void keepsWriteScopeForWarehouseFilter() {
Collection<Object> pageValues = queryValues(null, 20L);
assertThat(pageValues).containsExactly(20L);
PalletMapper mapper = mapper();
new PalletRepository(mapper).findOwned(10001L, 20L, 30L);
repository.findOwned(10001L, 20L, 30L);
@SuppressWarnings("unchecked")
ArgumentCaptor<LambdaQueryWrapper<PalletEntity>> wrapper = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(mapper).selectOne(wrapper.capture());
@@ -59,9 +64,9 @@ class PalletRepositoryTest {
.containsExactlyInAnyOrder(10001L, 20L, 30L);
}
private static Collection<Object> queryValues(Long tenantId, Long warehouseId) {
PalletMapper mapper = mapper();
new PalletRepository(mapper).page(tenantId,
private Collection<Object> queryValues(Long tenantId, Long warehouseId) {
when(mapper.selectPage(any(Page.class), any())).thenAnswer(invocation -> invocation.getArgument(0));
repository.page(tenantId,
new PalletRequests.PageQuery(1, 20, warehouseId, null, null));
@SuppressWarnings("unchecked")
ArgumentCaptor<LambdaQueryWrapper<PalletEntity>> wrapper = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
@@ -70,9 +75,4 @@ class PalletRepositoryTest {
return wrapper.getValue().getParamNameValuePairs().values();
}
private static PalletMapper mapper() {
PalletMapper mapper = mock(PalletMapper.class);
when(mapper.selectPage(any(Page.class), any())).thenAnswer(invocation -> invocation.getArgument(0));
return mapper;
}
}
@@ -13,18 +13,24 @@ import org.apache.ibatis.session.Configuration;
import java.util.List;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baiyue.warehouse.dto.MasterDataQueries.PartnerPageQuery;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@SpringJUnitConfig(PartnerRepository.class)
class PartnerRepositoryTest {
@BeforeAll static void 初始化实体字段缓存() {
@MockBean private PartnerMapper mapper;
@Resource private PartnerRepository repository;
@BeforeAll static void setUpCase01() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "test"), PartnerEntity.class);
}
@Test void 候选查询强制租户和启用状态() {
PartnerMapper mapper=mock(PartnerMapper.class); when(mapper.selectList(any())).thenReturn(List.of());
@Test void shouldHandleCase02() {
when(mapper.selectList(any())).thenReturn(List.of());
when(mapper.selectPartnerIdsByType(10001,"SHIPPER")).thenReturn(List.of(1L));
new PartnerRepository(mapper).activeCandidates(10001,"示例","SHIPPER");
repository.activeCandidates(10001,"示例","SHIPPER");
@SuppressWarnings("unchecked") ArgumentCaptor<LambdaQueryWrapper<PartnerEntity>> c=ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(mapper).selectList(c.capture()); c.getValue().getSqlSegment();
assertThat(c.getValue().getParamNameValuePairs().values()).contains(10001L,"ACTIVE",1L);
@@ -34,32 +40,30 @@ class PartnerRepositoryTest {
@Test
@DisplayName("默认候选只包含发货单位和收货单位")
void shouldLimitDefaultCandidatesToOrderPartyTypes() {
PartnerMapper mapper=mock(PartnerMapper.class);
when(mapper.selectPartnerIdsByType(10001L,"SHIPPER")).thenReturn(List.of(1L));
when(mapper.selectPartnerIdsByType(10001L,"CONSIGNEE")).thenReturn(List.of(2L,1L));
when(mapper.selectList(any())).thenReturn(List.of());
new PartnerRepository(mapper).activeCandidates(10001L,null,null);
repository.activeCandidates(10001L,null,null);
@SuppressWarnings("unchecked") ArgumentCaptor<LambdaQueryWrapper<PartnerEntity>> wrapper=ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(mapper).selectList(wrapper.capture());
wrapper.getValue().getSqlSegment();
assertThat(wrapper.getValue().getParamNameValuePairs().values()).contains(10001L,"ACTIVE",1L,2L);
}
@Test void 详情查询强制租户和启用状态() {
PartnerMapper mapper=mock(PartnerMapper.class); when(mapper.selectOne(any())).thenReturn(null);
new PartnerRepository(mapper).findActive(10002,9);
@Test void shouldHandleCase03() {
when(mapper.selectOne(any())).thenReturn(null);
repository.findActive(10002,9);
@SuppressWarnings("unchecked") ArgumentCaptor<LambdaQueryWrapper<PartnerEntity>> c=ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(mapper).selectOne(c.capture()); c.getValue().getSqlSegment();
assertThat(c.getValue().getParamNameValuePairs().values()).contains(10002L,9L,"ACTIVE");
}
@Test void 平台分页强制租户并支持筛选() {
PartnerMapper mapper=mock(PartnerMapper.class);
@Test void shouldHandleCase04() {
when(mapper.selectPartnerPageByType(any(Page.class), eq(10001L), eq("CUSTOMER"),
eq("ACTIVE"), eq("示例"))).thenAnswer(invocation -> invocation.getArgument(0));
new PartnerRepository(mapper).page(10001L,
repository.page(10001L,
new PartnerPageQuery(1, 20, "示例", "CUSTOMER", "ACTIVE"));
verify(mapper).selectPartnerPageByType(any(Page.class), eq(10001L), eq("CUSTOMER"),
@@ -67,12 +71,11 @@ class PartnerRepositoryTest {
verify(mapper, never()).selectPartnerIdsByType(anyLong(), anyString());
}
@Test void 平台分页不传租户时查询全部企业并支持承运商类型筛选() {
PartnerMapper mapper=mock(PartnerMapper.class);
@Test void pagesAllTenantsByCarrier() {
when(mapper.selectPartnerPageByType(any(Page.class), isNull(), eq("CARRIER"),
eq("ACTIVE"), isNull())).thenAnswer(invocation -> invocation.getArgument(0));
new PartnerRepository(mapper).page(null,
repository.page(null,
new PartnerPageQuery(1, 20, null, "CARRIER", "ACTIVE"));
verify(mapper).selectPartnerPageByType(any(Page.class), isNull(), eq("CARRIER"),
@@ -10,7 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class WarehouseSchemaContractTest {
@Test
void 仓库三张表包含实体全部持久化字段() throws IOException {
void shouldHandleCase01() 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");
@@ -22,7 +22,7 @@ class WarehouseSchemaContractTest {
}
@Test
void 往来单位表包含实体全部持久化字段() throws IOException {
void shouldHandleCase02() throws IOException {
String sql = read("warehouse-partner.sql");
assertColumns(sql, "wh_partner", "data_scope_id", "partner_code", "partner_name", "partner_type", "contact_name",
"contact_phone", "contact_email", "country_code", "province", "city", "district", "address_line",
@@ -33,7 +33,7 @@ class WarehouseSchemaContractTest {
}
@Test
void 快递公司表包含实体全部持久化字段() throws IOException {
void shouldHandleCase03() 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");
@@ -41,7 +41,7 @@ class WarehouseSchemaContractTest {
}
@Test
void 二期库位共用表覆盖基础资料预占占用和并发约束() throws IOException {
void shouldHandleCase04() throws IOException {
String sql = read("phase2-pallet-location-schema.sql");
assertColumns(sql, "wh_location", "tenant_id", "warehouse_id", "zone_code", "zone_name",
"location_code", "location_type", "max_weight_kg", "shared_overflow", "status", "remark", "deleted", "version");
@@ -59,7 +59,7 @@ class WarehouseSchemaContractTest {
}
@Test
void A0迁移允许共享库位多占用但仍限制托盘唯一活动占用() throws IOException {
void shouldHandleCase05() throws IOException {
String sql = read("phase2-location-overflow-migration.sql");
assertThat(sql).contains("'wh_location', 'shared_overflow'")
.contains("'wh_location_reservation', 'shared_overflow'")
@@ -78,7 +78,7 @@ class WarehouseSchemaContractTest {
}
@Test
void 既有库增量迁移覆盖全部仓储实体表和列表索引() throws IOException {
void shouldHandleCase06() throws IOException {
String sql = read("phase1-warehouse-schema-migration.sql");
assertThat(sql).contains("FROM information_schema.columns")
.contains("CREATE PROCEDURE add_column_if_missing")
@@ -5,6 +5,9 @@ import com.baiyue.warehouse.repository.ExpressCompanyRepository;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -16,12 +19,13 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@DisplayName("快递公司批量导入")
@SpringJUnitConfig(ExpressCompanyImportService.class)
class ExpressCompanyImportServiceTest {
@MockBean private ExpressCompanyRepository repository;
@Resource private ExpressCompanyImportService service;
@Test
@DisplayName("标准模板通过整批校验后写入全部数据")
void shouldImportValidWorkbookAfterFullValidation() throws Exception {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyImportService service = new ExpressCompanyImportService(repository);
byte[] workbook = withRows(service.template(), new String[][] {
{"SF", "顺丰速运", "95338", "https://www.sf-express.com", "https://example.com/{trackingNo}", "ACTIVE", "国内件"}
});
@@ -39,8 +43,6 @@ class ExpressCompanyImportServiceTest {
@Test
@DisplayName("文件内重复名称时整批拒绝且不写入")
void shouldRejectWholeWorkbookWhenAnyRowIsInvalid() throws Exception {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyImportService service = new ExpressCompanyImportService(repository);
byte[] workbook = withRows(service.template(), new String[][] {
{"SF", "顺丰速运", "", "", "", "ACTIVE", ""},
{"SF2", "顺丰速运", "", "", "", "ACTIVE", ""}
@@ -7,6 +7,9 @@ import com.baiyue.warehouse.entity.LocationEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.LocationRepository;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.math.BigDecimal;
@@ -18,11 +21,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(LocationApplicationService.class)
class LocationApplicationServiceTest {
@MockBean private LocationRepository repository;
@Resource private LocationApplicationService service;
@Test
void 平台分页不传企业和仓库时查询全部库位() {
LocationRepository repository = mock(LocationRepository.class);
LocationApplicationService service = new LocationApplicationService(repository);
void pagesAllLocations() {
PageQuery query = pageQuery(null);
service.page(null, query);
@@ -32,9 +36,7 @@ class LocationApplicationServiceTest {
}
@Test
void 平台分页可只按企业筛选全部仓库() {
LocationRepository repository = mock(LocationRepository.class);
LocationApplicationService service = new LocationApplicationService(repository);
void filtersByTenant() {
PageQuery query = pageQuery(null);
service.page(10001L, query);
@@ -43,9 +45,7 @@ class LocationApplicationServiceTest {
}
@Test
void 平台分页可同时按企业和仓库筛选() {
LocationRepository repository = mock(LocationRepository.class);
LocationApplicationService service = new LocationApplicationService(repository);
void filtersByTenantAndWarehouse() {
PageQuery query = pageQuery(88L);
service.page(10001L, query);
@@ -54,9 +54,7 @@ class LocationApplicationServiceTest {
}
@Test
void 新增时仓库必须属于当前企业租户() {
LocationRepository repository = mock(LocationRepository.class);
LocationApplicationService service = new LocationApplicationService(repository);
void shouldHandleCase01() {
assertThatThrownBy(() -> service.create(10001L, request("AVAILABLE", null)))
.isInstanceOf(BizException.class).hasMessageContaining("企业租户");
@@ -64,23 +62,19 @@ class LocationApplicationServiceTest {
}
@Test
void 同一租户仓库的库位编码必须唯一() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase02() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse("ACTIVE"));
when(repository.existsCode(10001L, 88L, "A1-01", null)).thenReturn(true);
LocationApplicationService service = new LocationApplicationService(repository);
assertThatThrownBy(() -> service.create(10001L, request("AVAILABLE", null)))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
}
@Test
void 活动预占或占用库位不能停用() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase03() {
LocationEntity location = location("AVAILABLE", 3);
when(repository.findOwned(10001L, 88L, 9L)).thenReturn(location);
when(repository.hasActiveResource(10001L, 9L)).thenReturn(true);
LocationApplicationService service = new LocationApplicationService(repository);
assertThatThrownBy(() -> service.changeStatus(10001L, 88L, 9L, "DISABLED", 3, 7L))
.isInstanceOf(BizException.class).hasMessageContaining("不能停用");
@@ -88,10 +82,8 @@ class LocationApplicationServiceTest {
}
@Test
void 删除前必须先停用且没有活动资源() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase04() {
when(repository.findOwned(10001L, 88L, 9L)).thenReturn(location("AVAILABLE", 3));
LocationApplicationService service = new LocationApplicationService(repository);
assertThatThrownBy(() -> service.delete(10001L, 88L, 9L, 3, 7L))
.isInstanceOf(BizException.class).hasMessageContaining("先停用");
@@ -99,12 +91,10 @@ class LocationApplicationServiceTest {
}
@Test
void 条件更新失败统一转换为乐观锁冲突() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase05() {
when(repository.findOwned(10001L, 88L, 9L)).thenReturn(location("DISABLED", 3));
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse("ACTIVE"));
when(repository.changeStatus(10001L, 88L, 9L, "DISABLED", "AVAILABLE", 3, 7L)).thenReturn(0);
LocationApplicationService service = new LocationApplicationService(repository);
assertThatThrownBy(() -> service.changeStatus(10001L, 88L, 9L, "AVAILABLE", 3, 7L))
.isInstanceOf(BizException.class).hasMessageContaining("刷新");
@@ -11,6 +11,9 @@ import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.LocationRepository;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -26,15 +29,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(LocationResourceService.class)
class LocationResourceServiceTest {
@MockBean private LocationRepository repository;
@Resource private LocationResourceService service;
@Test
void 普通库位不足时优先分配普通位并将剩余托盘共享到A0() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase01() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse());
when(repository.lockAvailable(10001L, 88L, 2)).thenReturn(List.of(location(9L, "A1")));
when(repository.lockOrCreateSharedOverflow(10001L, 88L, 7L)).thenReturn(overflow());
when(repository.changeStatus(10001L, 88L, 9L, "AVAILABLE", "RESERVED", 0, 7L)).thenReturn(1);
LocationResourceService service = new LocationResourceService(repository);
var result = service.reserveAvailable(reserveCommand());
@@ -46,12 +50,10 @@ class LocationResourceServiceTest {
}
@Test
void 无普通库位时同一请求的所有不足目标复用同一个A0() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase02() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse());
when(repository.lockAvailable(10001L, 88L, 2)).thenReturn(List.of());
when(repository.lockOrCreateSharedOverflow(10001L, 88L, 7L)).thenReturn(overflow());
LocationResourceService service = new LocationResourceService(repository);
var result = service.reserveAvailable(reserveCommand());
@@ -62,14 +64,12 @@ class LocationResourceServiceTest {
}
@Test
void 单事务按托盘顺序锁定并条件预占库位() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase03() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse());
when(repository.lockAvailable(10001L, 88L, 2))
.thenReturn(List.of(location(9L, "A1"), location(10L, "A2")));
when(repository.changeStatus(10001L, 88L, 9L, "AVAILABLE", "RESERVED", 0, 7L)).thenReturn(1);
when(repository.changeStatus(10001L, 88L, 10L, "AVAILABLE", "RESERVED", 0, 7L)).thenReturn(1);
LocationResourceService service = new LocationResourceService(repository);
var result = service.reserveAvailable(reserveCommand());
@@ -81,8 +81,7 @@ class LocationResourceServiceTest {
}
@Test
void 已确认预占按占用事实幂等返回() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase04() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse());
LocationReservationEntity reservation = reservation("CONFIRMED");
when(repository.lockReservation(10001L, 101L)).thenReturn(reservation);
@@ -94,7 +93,6 @@ class LocationResourceServiceTest {
occupancy.setLocationId(9L);
occupancy.setLocationCode("A1");
when(repository.findOccupancyByReservation(10001L, 101L)).thenReturn(occupancy);
LocationResourceService service = new LocationResourceService(repository);
var result = service.confirmOccupied(confirmCommand());
@@ -103,15 +101,13 @@ class LocationResourceServiceTest {
}
@Test
void 确认占用使用预占归属和库位版本条件更新() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase05() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse());
LocationReservationEntity reservation = reservation("RESERVED");
when(repository.lockReservation(10001L, 101L)).thenReturn(reservation);
when(repository.findOwned(10001L, 88L, 9L)).thenReturn(location(9L, "A1"));
when(repository.changeStatus(10001L, 88L, 9L, "RESERVED", "OCCUPIED", 0, 7L)).thenReturn(1);
when(repository.confirmReservation(reservation)).thenReturn(1);
LocationResourceService service = new LocationResourceService(repository);
var result = service.confirmOccupied(confirmCommand());
@@ -121,8 +117,7 @@ class LocationResourceServiceTest {
}
@Test
void 确认A0占用时保持共享库位AVAILABLE() {
LocationRepository repository = mock(LocationRepository.class);
void shouldHandleCase06() {
when(repository.findWarehouse(10001L, 88L)).thenReturn(warehouse());
LocationReservationEntity reservation = reservation("RESERVED");
reservation.setLocationId(99L);
@@ -131,7 +126,6 @@ class LocationResourceServiceTest {
when(repository.lockReservation(10001L, 101L)).thenReturn(reservation);
when(repository.findOwned(10001L, 88L, 99L)).thenReturn(overflow());
when(repository.confirmReservation(reservation)).thenReturn(1);
LocationResourceService service = new LocationResourceService(repository);
var result = service.confirmOccupied(confirmCommand());
@@ -7,19 +7,23 @@ import com.baiyue.warehouse.entity.PalletEntity;
import com.baiyue.warehouse.repository.PalletRepository;
import com.baiyue.warehouse.repository.WarehouseRepository;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(PalletApplicationService.class)
class PalletApplicationServiceTest {
private final PalletRepository repository = mock(PalletRepository.class);
private final WarehouseRepository warehouseRepository = mock(WarehouseRepository.class);
private final PalletApplicationService service = new PalletApplicationService(repository, warehouseRepository);
@MockBean private PalletRepository repository;
@MockBean private WarehouseRepository warehouseRepository;
@Resource private PalletApplicationService service;
@Test
void 平台详情不暴露内部状态() {
void shouldHandleCase01() {
PalletEntity entity = pallet("OCCUPIED");
when(repository.findOwned(10001, 20, 30)).thenReturn(entity);
org.assertj.core.api.Assertions.assertThat(service.detail(10001, 20, 30))
@@ -27,12 +31,12 @@ class PalletApplicationServiceTest {
}
@Test
void 跨仓库托盘不可见() {
void shouldHandleCase02() {
assertThatThrownBy(() -> service.detail(10001, 21, 30)).isInstanceOf(BizException.class);
}
@Test
void 平台分页允许不指定企业并保留分页结果() {
void pagesWithoutTenantFilter() {
PalletRequests.PageQuery query = new PalletRequests.PageQuery(1, 20, null, null, null);
when(repository.page(null, query)).thenReturn(new PageResult<>(java.util.List.of(pallet("AVAILABLE")),
1, 20, 1, 1));
@@ -6,6 +6,11 @@ import com.baiyue.warehouse.dto.PalletAllocationDtos;
import com.baiyue.warehouse.entity.PalletEntity;
import com.baiyue.warehouse.repository.PalletRepository;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -19,14 +24,19 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(classes = {PalletLocationAllocationService.class, PalletLocationAllocationServiceTest.TestConfig.class})
class PalletLocationAllocationServiceTest {
private final PalletRepository pallets = mock(PalletRepository.class);
private final LocationResourceService locations = mock(LocationResourceService.class);
private final PalletLocationAllocationService service =
new PalletLocationAllocationService(pallets, new PalletVolumeSplitter(), locations);
@MockBean private PalletRepository pallets;
@MockBean private LocationResourceService locations;
@Resource private PalletLocationAllocationService service;
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean PalletVolumeSplitter splitter() { return new PalletVolumeSplitter(); }
}
@Test
void 并发抢占托盘时整批失败且不预占库位() {
void shouldHandleCase01() {
when(locations.findActiveResources(10001, "FIRSTLEG", 88)).thenReturn(List.of());
when(pallets.findAvailable(10001, 20, "木托盘", new BigDecimal("2"), 1))
.thenReturn(List.of(pallet(30, "TP-001")));
@@ -37,7 +47,7 @@ class PalletLocationAllocationServiceTest {
}
@Test
void 未指定类型时默认推荐普通托盘() {
void shouldHandleCase02() {
when(locations.findActiveResources(10001, "FIRSTLEG", 99)).thenReturn(List.of());
when(pallets.findAvailable(10001, 20, "STANDARD", new BigDecimal("2"), 1))
.thenReturn(List.of(pallet(30, "TP-001")));
@@ -55,7 +65,7 @@ class PalletLocationAllocationServiceTest {
}
@Test
void 释放预占后托盘恢复可用并可再次预占() {
void shouldHandleCase03() {
LocationDtos.ActiveResource resource = new LocationDtos.ActiveResource(
101, null, 20, 30, "TP-001", 40, "A-01", false, "RESERVED");
when(locations.findActiveResources(10001, "FIRSTLEG", 88)).thenReturn(List.of(resource));
@@ -70,7 +80,7 @@ class PalletLocationAllocationServiceTest {
}
@Test
void 已确认占用禁止补偿释放避免回滚入库事实() {
void shouldHandleCase04() {
LocationDtos.ActiveResource resource = new LocationDtos.ActiveResource(
101, 201L, 20, 30, "TP-001", 40, "A-01", false, "OCCUPIED");
when(locations.findActiveResources(10001, "FIRSTLEG", 88)).thenReturn(List.of(resource));
@@ -82,7 +92,7 @@ class PalletLocationAllocationServiceTest {
}
@Test
void 相同业务幂等重试直接返回已有预占() {
void shouldHandleCase05() {
LocationDtos.ActiveResource resource = new LocationDtos.ActiveResource(
101, null, 20, 30, "TP-001", 40, "A-01", false, "RESERVED");
when(locations.findActiveResources(10001, "FIRSTLEG", 88)).thenReturn(List.of(resource));
@@ -12,14 +12,14 @@ class PalletVolumeSplitterTest {
private final PalletVolumeSplitter splitter = new PalletVolumeSplitter();
@Test
void 整除时不多拆托盘() {
void shouldHandleCase01() {
var result = splitter.split(new BigDecimal("20"), new BigDecimal("2"));
assertThat(result.palletCount()).isEqualTo(10);
assertThat(result.allocations()).allMatch(item -> item.allocatedVolumeCbm().compareTo(new BigDecimal("2")) == 0);
}
@Test
void 非整除时向上拆分并保留尾盘体积() {
void shouldHandleCase02() {
var result = splitter.split(new BigDecimal("20.1"), new BigDecimal("2"));
assertThat(result.palletCount()).isEqualTo(11);
assertThat(result.allocations().get(10).allocatedVolumeCbm()).isEqualByComparingTo("0.1");
@@ -28,7 +28,7 @@ class PalletVolumeSplitterTest {
}
@Test
void 拒绝空值零值负值和超大拆分() {
void shouldHandleCase03() {
assertThatThrownBy(() -> splitter.split(null, BigDecimal.ONE)).isInstanceOf(BizException.class);
assertThatThrownBy(() -> splitter.split(BigDecimal.ZERO, BigDecimal.ONE)).isInstanceOf(BizException.class);
assertThatThrownBy(() -> splitter.split(BigDecimal.ONE, BigDecimal.ZERO)).isInstanceOf(BizException.class);
@@ -7,6 +7,9 @@ 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 jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -18,12 +21,13 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@DisplayName("往来单位批量导入")
@SpringJUnitConfig(PartnerImportService.class)
class PartnerImportServiceTest {
@MockBean private PartnerRepository repository;
@Resource private PartnerImportService service;
@Test
@DisplayName("标准模板支持多类型并写入服务端数据范围")
void shouldImportValidMultiTypeRow() throws Exception {
PartnerRepository repository = mock(PartnerRepository.class);
PartnerImportService service = new PartnerImportService(repository);
byte[] file = withRows(service.template(), new String[][] {
{"CUS-100", "测试单位", "CUSTOMER,SHIPPER", "张三", "13800138000", "test@example.com",
"CN", "广东省", "深圳市", "南山区", "测试路 1 号", "518000", "ACTIVE", "测试备注"}
@@ -43,8 +47,6 @@ class PartnerImportServiceTest {
@Test
@DisplayName("文件内任一错误时整批零写入并返回行号")
void shouldRejectWholeBatchWhenAnyRowIsInvalid() throws Exception {
PartnerRepository repository = mock(PartnerRepository.class);
PartnerImportService service = new PartnerImportService(repository);
byte[] file = withRows(service.template(), new String[][] {
{"CUS-100", "测试单位一", "CUSTOMER", "张三", "13800138000", "", "CN", "", "深圳市", "", "测试路 1 号", "", "ACTIVE", ""},
{"CUS-100", "测试单位二", "UNKNOWN", "李四", "13900139000", "bad-email", "C", "", "", "", "", "", "ENABLED", ""}
@@ -5,45 +5,46 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.entity.PartnerEntity;
import com.baiyue.warehouse.repository.PartnerRepository;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(PartnerQueryService.class)
class PartnerQueryServiceTest {
@MockBean private PartnerRepository repository;
@Resource private PartnerQueryService service;
@Test void unsupportedCandidateTypeReturnsBadRequest() {
PartnerRepository repository = mock(PartnerRepository.class);
assertThatThrownBy(() -> new PartnerQueryService(repository).candidates(10001L, null, "CUSTOMER"))
assertThatThrownBy(() -> service.candidates(10001L, null, "CUSTOMER"))
.isInstanceOf(BizException.class).hasMessageContaining("SHIPPER");
}
@Test void missingOrCrossTenantPartnerReturnsNotFound() {
PartnerRepository repository = mock(PartnerRepository.class);
assertThatThrownBy(() -> new PartnerQueryService(repository).active(10001L, 7L))
assertThatThrownBy(() -> service.active(10001L, 7L))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.NOT_FOUND);
}
@Test void 停用往来单位返回409() {
PartnerRepository repository = mock(PartnerRepository.class);
@Test void shouldHandleCase01() {
PartnerEntity partner = new PartnerEntity();
partner.setId(7L); partner.setTenantId(10001L); partner.setStatus("INACTIVE");
when(repository.findOwned(10001L, 7L)).thenReturn(partner);
assertThatThrownBy(() -> new PartnerQueryService(repository).active(10001L, 7L))
assertThatThrownBy(() -> service.active(10001L, 7L))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.CONFLICT);
}
@Test void 启用往来单位正常返回() {
PartnerRepository repository = mock(PartnerRepository.class);
@Test void shouldHandleCase02() {
PartnerEntity partner = new PartnerEntity();
partner.setId(7L); partner.setTenantId(10001L); partner.setStatus("ACTIVE");
when(repository.findOwned(10001L, 7L)).thenReturn(partner);
assertThat(new PartnerQueryService(repository).active(10001L, 7L)).isSameAs(partner);
assertThat(service.active(10001L, 7L)).isSameAs(partner);
}
}
@@ -9,6 +9,9 @@ import com.baiyue.warehouse.repository.ExpressCompanyRepository;
import com.baiyue.warehouse.repository.PartnerRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.dao.DataIntegrityViolationException;
import static org.assertj.core.api.Assertions.assertThat;
@@ -21,21 +24,26 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(classes = {PlatformPartnerQueryService.class, ExpressCompanyQueryService.class})
class PlatformMasterDataManagementServiceTest {
@MockBean private PartnerRepository partnerRepository;
@MockBean private ExpressCompanyRepository expressRepository;
@Resource private PlatformPartnerQueryService partnerService;
@Resource private ExpressCompanyQueryService expressService;
@Test
void 往来单位详情强制租户隔离() {
PartnerRepository repository = mock(PartnerRepository.class);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
void shouldHandleCase01() {
PartnerRepository repository = partnerRepository;
PlatformPartnerQueryService service = partnerService;
assertThatThrownBy(() -> service.detail(10002L, 1L)).isInstanceOf(BizException.class);
verify(repository).findOwned(10002L, 1L);
}
@Test
void 往来单位编码在租户内必须唯一() {
PartnerRepository repository = mock(PartnerRepository.class);
void shouldHandleCase02() {
PartnerRepository repository = partnerRepository;
when(repository.existsCode(10001L, "CUS-001", null)).thenReturn(true);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
PlatformPartnerQueryService service = partnerService;
assertThatThrownBy(() -> service.create(10001L, partnerRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
@@ -43,24 +51,24 @@ class PlatformMasterDataManagementServiceTest {
}
@Test
void 往来单位并发重复编码统一返回业务冲突() {
PartnerRepository repository = mock(PartnerRepository.class);
void shouldHandleCase03() {
PartnerRepository repository = partnerRepository;
doThrow(new DataIntegrityViolationException("duplicate")).when(repository).insert(any());
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
PlatformPartnerQueryService service = partnerService;
assertThatThrownBy(() -> service.create(10001L, partnerRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
}
@Test
void 往来单位新增写入租户和请求字段() {
PartnerRepository repository = mock(PartnerRepository.class);
void shouldHandleCase04() {
PartnerRepository repository = partnerRepository;
doAnswer(invocation -> {
PartnerEntity entity = invocation.getArgument(0);
entity.setId(18L);
return null;
}).when(repository).insert(any());
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
PlatformPartnerQueryService service = partnerService;
assertThat(service.create(10001L, partnerRequest())).isEqualTo(18L);
@@ -75,13 +83,13 @@ class PlatformMasterDataManagementServiceTest {
}
@Test
void 往来单位编辑保留归属并更新字段() {
PartnerRepository repository = mock(PartnerRepository.class);
void shouldHandleCase05() {
PartnerRepository repository = partnerRepository;
PartnerEntity entity = new PartnerEntity();
entity.setId(19L);
entity.setTenantId(10001L);
when(repository.findOwned(10001L, 19L)).thenReturn(entity);
PlatformPartnerQueryService service = new PlatformPartnerQueryService(repository);
PlatformPartnerQueryService service = partnerService;
service.update(10001L, 19L, partnerRequest());
@@ -91,14 +99,14 @@ class PlatformMasterDataManagementServiceTest {
}
@Test
void 往来单位停用保留主数据() {
PartnerRepository repository = mock(PartnerRepository.class);
void shouldHandleCase06() {
PartnerRepository repository = partnerRepository;
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);
PlatformPartnerQueryService service = partnerService;
service.changeStatus(10001L, 8L, "INACTIVE");
@@ -107,19 +115,19 @@ class PlatformMasterDataManagementServiceTest {
}
@Test
void 快递公司详情强制租户隔离() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
void shouldHandleCase07() {
ExpressCompanyRepository repository = expressRepository;
ExpressCompanyQueryService service = expressService;
assertThatThrownBy(() -> service.detail(10002L, 1L)).isInstanceOf(BizException.class);
verify(repository).findOwned(10002L, 1L);
}
@Test
void 快递公司编码在租户内必须唯一() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
void shouldHandleCase08() {
ExpressCompanyRepository repository = expressRepository;
when(repository.existsCode(10001L, "SF", null)).thenReturn(true);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
ExpressCompanyQueryService service = expressService;
assertThatThrownBy(() -> service.create(10001L, expressRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
@@ -129,9 +137,9 @@ class PlatformMasterDataManagementServiceTest {
@Test
@DisplayName("快递公司名称在租户内必须唯一")
void shouldRequireUniqueExpressCompanyNameWithinTenant() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyRepository repository = expressRepository;
when(repository.existsName(10001L, "顺丰速运", null)).thenReturn(true);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
ExpressCompanyQueryService service = expressService;
assertThatThrownBy(() -> service.create(10001L, expressRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("名称");
@@ -141,23 +149,23 @@ class PlatformMasterDataManagementServiceTest {
@Test
@DisplayName("快递公司并发重复编码统一返回业务冲突")
void shouldReturnBusinessConflictForConcurrentDuplicateExpressCompanyCode() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
ExpressCompanyRepository repository = expressRepository;
doThrow(new DataIntegrityViolationException("duplicate")).when(repository).insert(any());
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
ExpressCompanyQueryService service = expressService;
assertThatThrownBy(() -> service.create(10001L, expressRequest()))
.isInstanceOf(BizException.class).hasMessageContaining("编码");
}
@Test
void 快递公司新增写入租户和请求字段() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
void shouldHandleCase09() {
ExpressCompanyRepository repository = expressRepository;
doAnswer(invocation -> {
ExpressCompanyEntity entity = invocation.getArgument(0);
entity.setId(28L);
return null;
}).when(repository).insert(any());
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
ExpressCompanyQueryService service = expressService;
assertThat(service.create(10001L, expressRequest())).isEqualTo(28L);
@@ -170,13 +178,13 @@ class PlatformMasterDataManagementServiceTest {
}
@Test
void 快递公司编辑保留归属并更新字段() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
void shouldHandleCase10() {
ExpressCompanyRepository repository = expressRepository;
ExpressCompanyEntity entity = new ExpressCompanyEntity();
entity.setId(29L);
entity.setTenantId(10001L);
when(repository.findOwned(10001L, 29L)).thenReturn(entity);
ExpressCompanyQueryService service = new ExpressCompanyQueryService(repository);
ExpressCompanyQueryService service = expressService;
service.update(10001L, 29L, expressRequest());
@@ -186,14 +194,14 @@ class PlatformMasterDataManagementServiceTest {
}
@Test
void 快递公司停用保留主数据() {
ExpressCompanyRepository repository = mock(ExpressCompanyRepository.class);
void shouldHandleCase11() {
ExpressCompanyRepository repository = expressRepository;
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);
ExpressCompanyQueryService service = expressService;
service.changeStatus(10001L, 9L, "INACTIVE");
@@ -5,35 +5,34 @@ import com.baiyue.common.core.api.ResultCode;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.WarehouseRepository;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@SpringJUnitConfig(WarehouseApplicationService.class)
class WarehouseApplicationServiceTest {
@MockBean private WarehouseRepository repository;
@Resource private WarehouseApplicationService service;
@Test
void 商户详情只返回启用且属于租户的仓库() {
WarehouseRepository repository=mock(WarehouseRepository.class);
void shouldHandleCase01() {
WarehouseEntity warehouse=new WarehouseEntity(); warehouse.setId(1L); warehouse.setTenantId(10001L); warehouse.setStatus("ACTIVE");
when(repository.findOwned(10001,1)).thenReturn(warehouse);
when(repository.findAddresses(10001,1)).thenReturn(java.util.List.of());
when(repository.findCapabilities(10001,1)).thenReturn(java.util.List.of());
WarehouseApplicationService service=new WarehouseApplicationService(); ReflectionTestUtils.setField(service,"repository",repository);
assertThat(service.activeDetail(10001,1).warehouse()).isSameAs(warehouse);
verify(repository).findOwned(10001,1);
}
@Test
void 跨租户或不存在仓库返回404() {
WarehouseRepository repository=mock(WarehouseRepository.class);
WarehouseApplicationService service=new WarehouseApplicationService(); ReflectionTestUtils.setField(service,"repository",repository);
void shouldHandleCase02() {
assertThatThrownBy(() -> service.activeDetail(10002,1)).isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode()).isEqualTo(ResultCode.NOT_FOUND);
}
@Test
void 停用仓库返回409() {
WarehouseRepository repository=mock(WarehouseRepository.class);
void shouldHandleCase03() {
WarehouseEntity warehouse=new WarehouseEntity(); warehouse.setId(1L); warehouse.setTenantId(10001L); warehouse.setStatus("INACTIVE");
when(repository.findOwned(10001,1)).thenReturn(warehouse);
WarehouseApplicationService service=new WarehouseApplicationService(); ReflectionTestUtils.setField(service,"repository",repository);
assertThatThrownBy(() -> service.activeDetail(10001,1)).isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode()).isEqualTo(ResultCode.CONFLICT);
}
@@ -3,6 +3,9 @@ package com.baiyue.warehouse.service;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.BadSqlGrammarException;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.sql.SQLException;
@@ -12,12 +15,13 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@SpringJUnitConfig(WarehouseSchemaReadinessService.class)
class WarehouseSchemaReadinessServiceTest {
@MockBean private JdbcTemplate jdbcTemplate;
@Resource private WarehouseSchemaReadinessService service;
@Test
void 全部表字段可查询时返回就绪() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
var readiness = new WarehouseSchemaReadinessService(jdbcTemplate).readiness();
void shouldHandleCase01() {
var readiness = service.readiness();
assertThat(readiness.ready()).isTrue();
assertThat(readiness.status()).isEqualTo("UP");
@@ -28,12 +32,11 @@ class WarehouseSchemaReadinessServiceTest {
}
@Test
void 缺表或缺列时返回降级而不是伪报正常() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
void shouldHandleCase02() {
doThrow(new BadSqlGrammarException("probe", "SELECT", new SQLException("missing")))
.when(jdbcTemplate).execute(anyString());
var readiness = new WarehouseSchemaReadinessService(jdbcTemplate).readiness();
var readiness = service.readiness();
assertThat(readiness.ready()).isFalse();
assertThat(readiness.status()).isEqualTo("DEGRADED");