优化
This commit is contained in:
+2
-4
@@ -22,11 +22,9 @@ public class InternalSupplierController {
|
||||
@GetMapping("/{supplierId}")
|
||||
public ApiResponse<InternalSnapshot> get(@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
|
||||
@PathVariable long supplierId, @RequestParam(defaultValue="false") boolean requireActive,
|
||||
@RequestParam(required=false) String category, @RequestParam(required=false) String scope) {
|
||||
@RequestParam(required=false) String category) {
|
||||
if (token == null || !MessageDigest.isEqual(expected, token.getBytes(StandardCharsets.UTF_8))) throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
|
||||
if (category != null && !category.matches("BOOKING|CUSTOMS|TRUCKING|CERTIFICATE")) throw new BizException(ResultCode.VALIDATION_FAILED, "供应商类别无效");
|
||||
if (scope != null && !scope.matches("DOMESTIC|INTERNATIONAL")) throw new BizException(ResultCode.VALIDATION_FAILED, "订舱范围无效");
|
||||
if (scope != null && !"BOOKING".equals(category)) throw new BizException(ResultCode.VALIDATION_FAILED, "scope仅适用于BOOKING");
|
||||
return ApiResponse.success(service.internalSnapshot(supplierId, requireActive, category, scope));
|
||||
return ApiResponse.success(service.internalSnapshot(supplierId, requireActive, category));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ public final class SupplierDtos {
|
||||
|
||||
public record PageQuery(@Min(1) Integer page, @Min(1) @Max(200) Integer size,
|
||||
@Pattern(regexp = "BOOKING|CUSTOMS|TRUCKING|CERTIFICATE") String category,
|
||||
@Pattern(regexp = "DOMESTIC|INTERNATIONAL") String scope,
|
||||
@Size(max = 80) String region,
|
||||
@Pattern(regexp = "ACTIVE|INACTIVE") String status,
|
||||
String keyword) {
|
||||
@@ -26,7 +25,6 @@ public final class SupplierDtos {
|
||||
public record SaveRequest(
|
||||
@NotBlank @Size(max = 160) String name,
|
||||
@NotBlank @Pattern(regexp = "BOOKING|CUSTOMS|TRUCKING|CERTIFICATE") String category,
|
||||
@Pattern(regexp = "DOMESTIC|INTERNATIONAL") String scope,
|
||||
@NotBlank @Size(max = 80) String region,
|
||||
@NotBlank @Size(max = 80) String legalRepresentative,
|
||||
@NotBlank @Size(max = 80) String contactName,
|
||||
@@ -42,5 +40,5 @@ public final class SupplierDtos {
|
||||
public record StatusRequest(@NotNull @Pattern(regexp = "ACTIVE|INACTIVE") String status) {}
|
||||
|
||||
public record Stats(long booking, long customs, long trucking, long certificate) {}
|
||||
public record InternalSnapshot(long supplierId, String name, String category, String scope, String status) {}
|
||||
public record InternalSnapshot(long supplierId, String name, String category, String status) {}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ public class SupplierEntity extends BaseEntity {
|
||||
private Long tenantId;
|
||||
private String name;
|
||||
private String category;
|
||||
private String scope;
|
||||
private String region;
|
||||
private String legalRepresentative;
|
||||
private String contactName;
|
||||
|
||||
@@ -17,7 +17,6 @@ public class SupplierRepository {
|
||||
public PageResult<SupplierEntity> page(long tenantId, PageQuery query) {
|
||||
LambdaQueryWrapper<SupplierEntity> w = base(tenantId)
|
||||
.eq(StringUtils.hasText(query.category()), SupplierEntity::getCategory, query.category())
|
||||
.eq(StringUtils.hasText(query.scope()), SupplierEntity::getScope, query.scope())
|
||||
.eq(StringUtils.hasText(query.region()), SupplierEntity::getRegion, query.region())
|
||||
.eq(StringUtils.hasText(query.status()), SupplierEntity::getStatus, query.status())
|
||||
.and(StringUtils.hasText(query.keyword()), c -> c.like(SupplierEntity::getName, query.keyword())
|
||||
|
||||
@@ -18,8 +18,6 @@ public class SupplierService {
|
||||
@Resource private SupplierRepository repository;
|
||||
|
||||
public PageResult<SupplierEntity> page(PageQuery query) {
|
||||
if (query.category() == null && query.scope() != null) throw new BizException(ResultCode.VALIDATION_FAILED, "scope仅适用于订舱供应商");
|
||||
if (query.category() != null && !"BOOKING".equals(query.category()) && query.scope() != null) throw new BizException(ResultCode.VALIDATION_FAILED, "scope仅适用于订舱供应商");
|
||||
return repository.page(PUBLIC_TENANT_ID, query);
|
||||
}
|
||||
public SupplierEntity detail(long id) { return owned(id); }
|
||||
@@ -28,7 +26,6 @@ public class SupplierService {
|
||||
@Transactional
|
||||
@BusinessAudit(event = "SUPPLIER_CREATE", resourceType = "SUPPLIER", resourceId = "#id", description = "新增供应商", captureBefore = false)
|
||||
public Long create(long id, SaveRequest request) {
|
||||
validateScope(request);
|
||||
ensureName(request.name(), null);
|
||||
SupplierEntity entity = new SupplierEntity(); entity.setId(id); entity.setTenantId(0L); copy(entity, request);
|
||||
try { repository.insert(entity); } catch (DataIntegrityViolationException e) { throw new BizException(ResultCode.CONFLICT, "供应商名称已存在"); }
|
||||
@@ -38,7 +35,7 @@ public class SupplierService {
|
||||
@Transactional
|
||||
@BusinessAudit(event = "SUPPLIER_UPDATE", resourceType = "SUPPLIER", resourceId = "#id", description = "编辑供应商")
|
||||
public void update(long id, SaveRequest request) {
|
||||
SupplierEntity e = owned(id); validateScope(request); ensureName(request.name(), id); copy(e, request);
|
||||
SupplierEntity e = owned(id); ensureName(request.name(), id); copy(e, request);
|
||||
try { repository.save(e); } catch (DataIntegrityViolationException exception) { throw new BizException(ResultCode.CONFLICT, "供应商名称已存在"); }
|
||||
}
|
||||
|
||||
@@ -46,16 +43,14 @@ public class SupplierService {
|
||||
@BusinessAudit(event = "SUPPLIER_STATUS_CHANGE", resourceType = "SUPPLIER", resourceId = "#id", description = "启停供应商")
|
||||
public void changeStatus(long id, StatusRequest request) { SupplierEntity e = owned(id); if (!request.status().equals(e.getStatus())) { e.setStatus(request.status()); repository.save(e); } }
|
||||
|
||||
public InternalSnapshot internalSnapshot(long id, boolean requireActive, String category, String scope) {
|
||||
public InternalSnapshot internalSnapshot(long id, boolean requireActive, String category) {
|
||||
SupplierEntity e = owned(id);
|
||||
if (requireActive && !"ACTIVE".equals(e.getStatus())) throw new BizException(ResultCode.CONFLICT, "供应商未启用");
|
||||
if (category != null && !category.equals(e.getCategory())) throw new BizException(ResultCode.VALIDATION_FAILED, "供应商类别不匹配");
|
||||
if (scope != null && (!"BOOKING".equals(e.getCategory()) || !scope.equals(e.getScope()))) throw new BizException(ResultCode.VALIDATION_FAILED, "订舱范围不匹配");
|
||||
return new InternalSnapshot(e.getId(), e.getName(), e.getCategory(), e.getScope(), e.getStatus());
|
||||
return new InternalSnapshot(e.getId(), e.getName(), e.getCategory(), e.getStatus());
|
||||
}
|
||||
|
||||
private SupplierEntity owned(long id) { SupplierEntity e = repository.find(0L, id); if (e == null) throw new BizException(ResultCode.NOT_FOUND, "供应商不存在"); return e; }
|
||||
private void ensureName(String name, Long id) { if (repository.existsName(0L, name, id)) throw new BizException(ResultCode.CONFLICT, "供应商名称已存在"); }
|
||||
private static void validateScope(SaveRequest r) { if (r.category().equals("BOOKING") != (r.scope() != null)) throw new BizException(ResultCode.VALIDATION_FAILED, "国内/国外范围仅适用于订舱供应商"); }
|
||||
private static void copy(SupplierEntity e, SaveRequest r) { e.setName(r.name()); e.setCategory(r.category()); e.setScope(r.scope()); e.setRegion(r.region()); e.setLegalRepresentative(r.legalRepresentative()); e.setContactName(r.contactName()); e.setPhone(r.phone()); e.setEmail(r.email()); e.setAddress(r.address()); e.setBusinessLicenseNo(r.businessLicenseNo()); e.setLicenseFileId(r.licenseFileId()); e.setStatus(r.status()); e.setSettlementPeriod(r.settlementPeriod()); e.setRemark(r.remark()); }
|
||||
private static void copy(SupplierEntity e, SaveRequest r) { e.setName(r.name()); e.setCategory(r.category()); e.setRegion(r.region()); e.setLegalRepresentative(r.legalRepresentative()); e.setContactName(r.contactName()); e.setPhone(r.phone()); e.setEmail(r.email()); e.setAddress(r.address()); e.setBusinessLicenseNo(r.businessLicenseNo()); e.setLicenseFileId(r.licenseFileId()); e.setStatus(r.status()); e.setSettlementPeriod(r.settlementPeriod()); e.setRemark(r.remark()); }
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ CREATE TABLE IF NOT EXISTS wh_supplier (
|
||||
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '平台公共作用域,固定为0',
|
||||
name VARCHAR(160) NOT NULL COMMENT '供应商名称',
|
||||
category VARCHAR(20) NOT NULL COMMENT '供应商类别:BOOKING订舱、CUSTOMS报关、TRUCKING拖车、CERTIFICATE认证',
|
||||
scope VARCHAR(20) NULL COMMENT '订舱分类范围:DOMESTIC/INTERNATIONAL',
|
||||
region VARCHAR(80) NOT NULL COMMENT '地区',
|
||||
legal_representative VARCHAR(80) NOT NULL COMMENT '法人代表',
|
||||
contact_name VARCHAR(80) NOT NULL COMMENT '联系人',
|
||||
@@ -30,7 +29,6 @@ CREATE TABLE IF NOT EXISTS wh_supplier (
|
||||
KEY idx_wh_supplier_filter (tenant_id,category,status,region),
|
||||
CONSTRAINT chk_wh_supplier_category CHECK (category IN ('BOOKING','CUSTOMS','TRUCKING','CERTIFICATE')),
|
||||
CONSTRAINT chk_wh_supplier_tenant CHECK (tenant_id = 0),
|
||||
CONSTRAINT chk_wh_supplier_scope CHECK ((category='BOOKING' AND scope IN ('DOMESTIC','INTERNATIONAL')) OR (category<>'BOOKING' AND scope IS NULL)),
|
||||
CONSTRAINT chk_wh_supplier_status CHECK (status IN ('ACTIVE','INACTIVE')),
|
||||
CONSTRAINT chk_wh_supplier_settlement CHECK (settlement_period IN ('MONTHLY_30','MONTHLY_45','MONTHLY_60','MONTHLY_90','PER_TRANSACTION','IMMEDIATE'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台公共供应商业务档案';
|
||||
@@ -46,7 +44,6 @@ BEGIN
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL supplier_add_column_if_missing('scope', "VARCHAR(20) NULL COMMENT '订舱分类范围:DOMESTIC/INTERNATIONAL' AFTER category");
|
||||
CALL supplier_add_column_if_missing('license_file_id', "VARCHAR(128) NULL COMMENT 'File服务营业执照文件ID' AFTER business_license_no");
|
||||
CALL supplier_add_column_if_missing('created_by', "BIGINT UNSIGNED NULL COMMENT '创建人ID'");
|
||||
CALL supplier_add_column_if_missing('created_at', "DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间'");
|
||||
@@ -73,7 +70,6 @@ END$$
|
||||
DELIMITER ;
|
||||
CALL supplier_add_check_if_missing('chk_wh_supplier_category', "category IN ('BOOKING','CUSTOMS','TRUCKING','CERTIFICATE')");
|
||||
CALL supplier_add_check_if_missing('chk_wh_supplier_tenant', "tenant_id = 0");
|
||||
CALL supplier_add_check_if_missing('chk_wh_supplier_scope', "(category='BOOKING' AND scope IN ('DOMESTIC','INTERNATIONAL')) OR (category<>'BOOKING' AND scope IS NULL)");
|
||||
CALL supplier_add_check_if_missing('chk_wh_supplier_status', "status IN ('ACTIVE','INACTIVE')");
|
||||
CALL supplier_add_check_if_missing('chk_wh_supplier_settlement', "settlement_period IN ('MONTHLY_30','MONTHLY_45','MONTHLY_60','MONTHLY_90','PER_TRANSACTION','IMMEDIATE')");
|
||||
DROP PROCEDURE supplier_add_check_if_missing;
|
||||
@@ -84,7 +80,7 @@ CREATE PROCEDURE assert_supplier_schema()
|
||||
BEGIN
|
||||
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='baiyue_warehouse' AND table_name='wh_supplier') <> 1
|
||||
OR (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics WHERE table_schema='baiyue_warehouse' AND table_name='wh_supplier' AND index_name IN ('PRIMARY','uk_wh_supplier_name','idx_wh_supplier_filter')) <> 3
|
||||
OR (SELECT COUNT(*) FROM information_schema.table_constraints WHERE table_schema='baiyue_warehouse' AND table_name='wh_supplier' AND constraint_type='CHECK' AND constraint_name IN ('chk_wh_supplier_category','chk_wh_supplier_tenant','chk_wh_supplier_scope','chk_wh_supplier_status','chk_wh_supplier_settlement')) <> 5 THEN
|
||||
OR (SELECT COUNT(*) FROM information_schema.table_constraints WHERE table_schema='baiyue_warehouse' AND table_name='wh_supplier' AND constraint_type='CHECK' AND constraint_name IN ('chk_wh_supplier_category','chk_wh_supplier_tenant','chk_wh_supplier_status','chk_wh_supplier_settlement')) <> 4 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='供应商表、索引或约束初始化不完整';
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
| `page` | integer | 否 | 默认 `1`,最小 `1` |
|
||||
| `size` | integer | 否 | 默认 `20`,范围 `1..200` |
|
||||
| `category` | enum | 否 | 供应商分类 |
|
||||
| `scope` | enum | 否 | 仅订舱分类适用 |
|
||||
| `region` | string | 否 | 地区精确匹配,最长 80 |
|
||||
| `status` | enum | 否 | 合作状态 |
|
||||
| `keyword` | string | 否 | 按供应商名称或联系人模糊匹配 |
|
||||
@@ -72,7 +71,6 @@
|
||||
| `tenantId` | string | 固定为 `0` |
|
||||
| `name` | string | 供应商名称,平台内唯一 |
|
||||
| `category` | enum | 分类 |
|
||||
| `scope` | enum/null | 订舱供应商必填;其他分类为 `null` |
|
||||
| `region` | string | 地区 |
|
||||
| `legalRepresentative` | string | 法人代表 |
|
||||
| `contactName` | string | 联系人 |
|
||||
@@ -101,7 +99,6 @@
|
||||
{
|
||||
"name": "示例供应商有限公司",
|
||||
"category": "BOOKING",
|
||||
"scope": "DOMESTIC",
|
||||
"region": "宁波",
|
||||
"legalRepresentative": "张三",
|
||||
"contactName": "李经理",
|
||||
@@ -116,7 +113,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
必填字段:`name`、`category`、`region`、`legalRepresentative`、`contactName`、`phone`、`status`、`settlementPeriod`。`BOOKING` 时 `scope` 必填;其他分类 `scope` 必须传 `null` 或省略。
|
||||
必填字段:`name`、`category`、`region`、`legalRepresentative`、`contactName`、`phone`、`status`、`settlementPeriod`。订舱供应商不区分国内或国外。
|
||||
|
||||
新增成功的 `data` 是供应商 ID 字符串。编辑成功的 `data` 为 `null`。
|
||||
|
||||
@@ -152,13 +149,6 @@
|
||||
| `TRUCKING` | 拖车 |
|
||||
| `CERTIFICATE` | 证书 |
|
||||
|
||||
订舱范围:
|
||||
|
||||
| 值 | 展示文案 |
|
||||
|---|---|
|
||||
| `DOMESTIC` | 国内 |
|
||||
| `INTERNATIONAL` | 国外 |
|
||||
|
||||
合作状态:
|
||||
|
||||
| 值 | 展示文案 |
|
||||
@@ -187,7 +177,7 @@
|
||||
| `A0403` | 非平台用户或缺少对应权限 | 显示无权限 |
|
||||
| `A0404` | 供应商不存在 | 提示记录不存在并刷新列表 |
|
||||
| `A0409` | 供应商名称重复或并发唯一键冲突 | 保留表单并提示名称已存在 |
|
||||
| `A0422` | 字段校验失败、订舱范围规则不满足 | 使用 `message`/`data` 显示字段错误 |
|
||||
| `A0422` | 字段校验失败 | 使用 `message`/`data` 显示字段错误 |
|
||||
| `S0500` | 未预期服务异常 | 展示通用错误并记录 `requestId/traceId` |
|
||||
| `S0503` | Warehouse 数据库不可用 | 展示稍后重试并记录 `requestId/traceId` |
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ paths:
|
||||
- { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
|
||||
- { name: size, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 20 } }
|
||||
- { name: category, in: query, schema: { $ref: '#/components/schemas/Category' } }
|
||||
- { name: scope, in: query, description: 仅订舱供应商适用, schema: { $ref: '#/components/schemas/Scope' } }
|
||||
- { name: region, in: query, schema: { type: string } }
|
||||
- { name: status, in: query, schema: { $ref: '#/components/schemas/Status' } }
|
||||
- { name: keyword, in: query, description: 供应商名称或联系人, schema: { type: string } }
|
||||
@@ -57,13 +56,12 @@ paths:
|
||||
/internal/warehouse/v1/suppliers/{supplierId}:
|
||||
get:
|
||||
summary: 内部服务查询并校验供应商快照
|
||||
description: 仅服务间调用,需 X-Internal-Service-Token;不经 Gateway。可要求 ACTIVE,并校验类别与订舱范围。
|
||||
description: 仅服务间调用,需 X-Internal-Service-Token;不经 Gateway。可要求 ACTIVE,并校验供应商类别。
|
||||
security: [{ internalServiceToken: [] }]
|
||||
parameters:
|
||||
- { name: supplierId, in: path, required: true, schema: { type: integer, format: int64, minimum: 1 } }
|
||||
- { name: requireActive, in: query, schema: { type: boolean, default: false } }
|
||||
- { name: category, in: query, schema: { $ref: '#/components/schemas/Category' } }
|
||||
- { name: scope, in: query, schema: { $ref: '#/components/schemas/Scope' } }
|
||||
responses:
|
||||
'200': { description: 成功, content: { application/json: { schema: { $ref: '#/components/schemas/InternalSnapshotResponse' } } } }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
@@ -74,7 +72,6 @@ components:
|
||||
RequiredTenantId: { name: tenantId, in: query, required: true, schema: { type: integer, format: int64, enum: [0] } }
|
||||
schemas:
|
||||
Category: { type: string, enum: [BOOKING, CUSTOMS, TRUCKING, CERTIFICATE] }
|
||||
Scope: { type: string, enum: [DOMESTIC, INTERNATIONAL] }
|
||||
Status: { type: string, enum: [ACTIVE, INACTIVE] }
|
||||
SupplierSave:
|
||||
type: object
|
||||
@@ -82,7 +79,6 @@ components:
|
||||
properties:
|
||||
name: { type: string, maxLength: 160 }
|
||||
category: { $ref: '#/components/schemas/Category' }
|
||||
scope: { $ref: '#/components/schemas/Scope', description: BOOKING 必填,其他分类必须为空 }
|
||||
region: { type: string, maxLength: 80 }
|
||||
legalRepresentative: { type: string, maxLength: 80 }
|
||||
contactName: { type: string, maxLength: 80 }
|
||||
@@ -140,7 +136,6 @@ components:
|
||||
supplierId: { type: integer, format: int64 }
|
||||
name: { type: string }
|
||||
category: { $ref: '#/components/schemas/Category' }
|
||||
scope: { $ref: '#/components/schemas/Scope' }
|
||||
status: { $ref: '#/components/schemas/Status' }
|
||||
InternalSnapshotResponse:
|
||||
allOf: [{ $ref: '#/components/schemas/ApiResponse' }, { type: object, properties: { data: { $ref: '#/components/schemas/InternalSnapshot' } } }]
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.baiyue.warehouse.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class InternalSupplierControllerContractTest {
|
||||
@Test void validatesSupplierByActiveStatusAndCategoryWithoutBookingScope() {
|
||||
assertThat(InternalSupplierController.class.getAnnotation(RequestMapping.class).value())
|
||||
.containsExactly("/internal/warehouse/v1/suppliers");
|
||||
Method get = Arrays.stream(InternalSupplierController.class.getDeclaredMethods())
|
||||
.filter(method -> method.getName().equals("get"))
|
||||
.findFirst().orElseThrow();
|
||||
assertThat(Arrays.stream(get.getParameters()).map(parameter -> parameter.getName()))
|
||||
.containsExactly("token", "supplierId", "requireActive", "category");
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -8,17 +8,18 @@ import java.nio.file.Path;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SupplierSqlContractTest {
|
||||
@Test void freezesPublicSupplierSchemaAndBookingScope() throws IOException {
|
||||
@Test void freezesPublicSupplierSchemaWithoutBookingScope() throws IOException {
|
||||
try (var stream = getClass().getResourceAsStream("/db/warehouse-supplier.sql")) {
|
||||
assertThat(stream).isNotNull();
|
||||
String sql = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
assertThat(sql).contains("CREATE TABLE IF NOT EXISTS wh_supplier", "tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0",
|
||||
"chk_wh_supplier_tenant CHECK (tenant_id = 0)",
|
||||
"category='BOOKING' AND scope IN ('DOMESTIC','INTERNATIONAL')", "license_file_id VARCHAR(128)",
|
||||
"license_file_id VARCHAR(128)",
|
||||
"MONTHLY_30','MONTHLY_45','MONTHLY_60','MONTHLY_90','PER_TRANSACTION','IMMEDIATE",
|
||||
"created_by BIGINT UNSIGNED", "updated_by BIGINT UNSIGNED",
|
||||
"supplier_add_column_if_missing", "uk_wh_supplier_name", "idx_wh_supplier_filter",
|
||||
"assert_supplier_schema");
|
||||
assertThat(sql).doesNotContain("scope VARCHAR", "chk_wh_supplier_scope", "DOMESTIC/INTERNATIONAL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +28,8 @@ class SupplierSqlContractTest {
|
||||
assertThat(sql).contains("CREATE TABLE IF NOT EXISTS wh_supplier", "uk_wh_supplier_name",
|
||||
"idx_wh_supplier_filter", "platform:supplier:list", "platform:supplier:create",
|
||||
"platform:supplier:edit", "platform:supplier:status", "assert_platform_supplier_permissions");
|
||||
String supplierSection = sql.substring(sql.indexOf("-- 平台公共供应商业务档案及权限"));
|
||||
assertThat(supplierSection).doesNotContain("chk_wh_supplier_scope", "订舱分类范围");
|
||||
}
|
||||
|
||||
private static Path resolveUnifiedSql() {
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.baiyue.warehouse.service;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.warehouse.dto.SupplierDtos.SaveRequest;
|
||||
import com.baiyue.warehouse.dto.SupplierDtos.PageQuery;
|
||||
import com.baiyue.warehouse.dto.SupplierDtos.StatusRequest;
|
||||
import com.baiyue.warehouse.entity.SupplierEntity;
|
||||
import com.baiyue.warehouse.repository.SupplierRepository;
|
||||
@@ -13,8 +12,8 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -26,7 +25,7 @@ class SupplierServiceTest {
|
||||
private SaveRequest booking;
|
||||
|
||||
@BeforeEach void setUp() {
|
||||
booking = new SaveRequest("测试供应商", "BOOKING", "DOMESTIC", "宁波", "法人", "联系人",
|
||||
booking = new SaveRequest("测试供应商", "BOOKING", "宁波", "法人", "联系人",
|
||||
"13800000000", "supplier@example.com", "地址", "91330000TEST", "192000000000000001",
|
||||
"ACTIVE", "MONTHLY_30", null);
|
||||
}
|
||||
@@ -37,24 +36,6 @@ class SupplierServiceTest {
|
||||
verify(repository).insert(any(SupplierEntity.class));
|
||||
}
|
||||
|
||||
@Test void rejectsScopeForNonBookingSupplier() {
|
||||
SaveRequest customs = new SaveRequest("报关供应商", "CUSTOMS", "DOMESTIC", "宁波", "法人", "联系人",
|
||||
"13800000000", null, null, null, null, "ACTIVE", "PER_TRANSACTION", null);
|
||||
assertThatThrownBy(() -> service.create(101L, customs)).isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("仅适用于订舱供应商");
|
||||
}
|
||||
|
||||
@Test void requiresScopeForBookingSupplier() {
|
||||
SaveRequest missingScope = new SaveRequest("订舱供应商", "BOOKING", null, "宁波", "法人", "联系人",
|
||||
"13800000000", null, null, null, null, "ACTIVE", "PER_TRANSACTION", null);
|
||||
assertThatThrownBy(() -> service.create(101L, missingScope)).isInstanceOf(BizException.class);
|
||||
}
|
||||
|
||||
@Test void rejectsScopeFilterWithoutBookingCategory() {
|
||||
assertThatThrownBy(() -> service.page(new PageQuery(1, 20, "CUSTOMS", "DOMESTIC", null, null, null)))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("scope仅适用于订舱供应商");
|
||||
}
|
||||
|
||||
@Test void repeatedStatusChangeIsIdempotent() {
|
||||
SupplierEntity entity = new SupplierEntity(); entity.setId(101L); entity.setTenantId(0L); entity.setStatus("ACTIVE");
|
||||
when(repository.find(0L, 101L)).thenReturn(entity);
|
||||
@@ -64,9 +45,23 @@ class SupplierServiceTest {
|
||||
|
||||
@Test void validatesInternalSupplierSnapshot() {
|
||||
SupplierEntity entity = new SupplierEntity(); entity.setId(101L); entity.setTenantId(0L); entity.setName("测试供应商");
|
||||
entity.setCategory("BOOKING"); entity.setScope("DOMESTIC"); entity.setStatus("ACTIVE");
|
||||
entity.setCategory("BOOKING"); entity.setStatus("ACTIVE");
|
||||
when(repository.find(0L, 101L)).thenReturn(entity);
|
||||
var snapshot = service.internalSnapshot(101L, true, "BOOKING", "DOMESTIC");
|
||||
var snapshot = service.internalSnapshot(101L, true, "BOOKING");
|
||||
org.assertj.core.api.Assertions.assertThat(snapshot.supplierId()).isEqualTo(101L);
|
||||
org.assertj.core.api.Assertions.assertThat(snapshot.category()).isEqualTo("BOOKING");
|
||||
org.assertj.core.api.Assertions.assertThat(snapshot.status()).isEqualTo("ACTIVE");
|
||||
}
|
||||
|
||||
@Test void internalSnapshotRequiresActiveSupplierAndMatchingCategory() {
|
||||
SupplierEntity inactive = new SupplierEntity(); inactive.setId(101L); inactive.setTenantId(0L);
|
||||
inactive.setName("停用订舱供应商"); inactive.setCategory("BOOKING"); inactive.setStatus("INACTIVE");
|
||||
when(repository.find(0L, 101L)).thenReturn(inactive);
|
||||
assertThatThrownBy(() -> service.internalSnapshot(101L, true, "BOOKING"))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("未启用");
|
||||
|
||||
inactive.setStatus("ACTIVE");
|
||||
assertThatThrownBy(() -> service.internalSnapshot(101L, true, "CUSTOMS"))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("类别不匹配");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user