This commit is contained in:
2026-08-20 09:10:36 +08:00
parent 033b5df6b6
commit 508f76924e
58 changed files with 1790 additions and 628 deletions
@@ -1,13 +1,44 @@
# 海外通用资料接口缺口
海外浏览器请求的统一入口已确认为 `gateway-overseas/`(Audience=`lastleg-api`)。国内 `warehouse/` 只维护“海外仓合作仓”公共主数据类型;实际海外仓、地址、能力、库位、实物托盘和资源锁归 `warehouse-overseas-service/`。海外仓候选和仓储资源不得继续从国内 Warehouse API 读取。
本文件记录 Admin 海外通用资料与当前 Java 契约之间的实际差距。前端页面类型只是原型展示模型,不是已经发布的服务端 DTO。
当前仍缺少海外仓报价、汇率管理和车辆管理的已确认归属端点、请求/响应 DTO、错误码或写操作权限资源;统一海外 Base/Token 前端选择机制也尚未接入。
## 已确认的服务入口
因此本模块当前只冻结正式路由、页面结构、字段、承载方式和只读权限,不包含内存示例数据或模拟成功:
- `gateway-overseas` 将 `/api/platform/v1/last-leg/**` 路由到 `lastleg-service`。
- `gateway-overseas` 将 `/api/platform/v1/warehouses/**`、`/api/platform/v1/warehouse-admin/**` 路由到 `warehouse-overseas-service`。
- 网关海外 Token Audience 为 `lastleg-api`。
- 两个海外服务没有提交对应业务的静态 OpenAPI YAML;运行时契约由 Springdoc 注解生成,网关暴露 `/lastleg-service/v3/api-docs/**` 和 `/warehouse-overseas-service/v3/api-docs/**`。
`warehouse-overseas-service` 已有实际海外仓聚合接口:
| 操作 | 端点 | 请求/响应 | 当前限制 |
|---|---|---|---|
| 仓库列表 | `GET /api/platform/v1/warehouse-admin` | 必填 `customerId`;`page`、`size`、`countryCode`、`capabilityCode`、`status`;返回 `PageResult<WarehouseEntity>` | 仅校验平台身份,没有与本模块冻结权限对应的细粒度权限;按客户隔离,不能直接当作平台全局报价仓候选 |
| 仓库详情 | `GET /api/platform/v1/warehouse-admin/{id}` | 必填 `customerId`;返回仓库、地址、能力聚合 | 需要先冻结报价/车辆记录与客户、海外仓的外键口径 |
| 仓库新增/编辑/启停 | `POST`、`PUT /{id}`、`PATCH /{id}/status` | `WarehouseRequests.SaveRequest`;状态仅 `ACTIVE/INACTIVE` | 属于实际海外仓资源,不是国内公共合作仓类型,也不是报价、汇率或车辆主数据接口 |
上述接口不在本模块接入:它们是按客户隔离的实际海外仓资源,不是平台公共合作仓,也不是海外报价、汇率或车辆主数据。业务页面不得为了补候选数据而跨区域拼接这两类仓库资源。
国内 Warehouse 已正式支持公共类型 `OVERSEAS_PARTNER`,Admin 在现有 `/api/platform/v1/warehouse-admin` 仓库页完成筛选、新增和编辑。国内服务另有受 `X-Internal-Service-Token` 保护的 `GET /internal/warehouses/overseas-partners`,仅供未来海外报价服务内部读取 `ACTIVE + OVERSEAS_PARTNER` 候选;前端不得调用该内部端点,报价页面也不跨区域拉取国内合作仓列表。
## 页面与操作缺口
| 页面/操作 | 端点 | DTO | 权限 | 状态机/规则 | 结论 |
|---|---|---|---|---|---|
| 海外仓报价列表、筛选、详情 | 缺失 | 缺少报价概要、仓储费用、中转派送价格及分页 DTO | 已冻结只读权限 `platform:overseas-master-data:quote:list`,服务端未实现 | 需定义生效区间、版本唯一性及当前有效版本规则 | 保持结构化空态 |
| 海外仓报价新增、编辑 | 缺失 | 缺少基础信息、费用明细、派送价格保存 DTO;海外仓必须使用真实 ID,不能只传名称 | create/edit 动作权限未冻结 | 需定义并发版本、日期重叠、费用组合去重与金额精度 | 写按钮禁用 |
| 海外仓报价启停、删除、导出 | 缺失 | 缺少状态命令、删除约束和导出任务 DTO | status/delete/export 动作权限未冻结 | 需定义 `ACTIVE/INACTIVE`、历史引用后的删除规则和导出方式 | 写按钮禁用 |
| 汇率摘要、列表、详情 | 缺失 | 缺少 `yyyy-MM`、USD→CNY 月平均值、更新人/时间及备注 DTO | 已冻结只读权限 `platform:overseas-master-data:exchange-rate:list`,服务端未实现 | 固定方向应为 `1 USD = X CNY`;需定义精度和月份时区 | 保持结构化空态 |
| 汇率新增、覆盖编辑 | 缺失 | 缺少月度汇率保存 DTO | edit 动作权限未冻结 | 需定义同月唯一、覆盖/乐观锁及下游引用后的修改规则 | 写按钮禁用 |
| 车辆列表、详情 | 缺失 | 缺少车辆档案、归属、区域、里程、司机/外派单位、派送订单与更换历史 DTO | 已冻结只读权限 `platform:overseas-master-data:vehicle:list`,服务端未实现 | 需定义车辆状态及其与派送订单的关联来源 | 保持结构化空态 |
| 车辆新增、编辑、删除、导出 | 缺失 | 缺少车辆保存、删除约束和导出 DTO | create/edit/delete/export 动作权限未冻结 | 需定义车牌唯一性、归属变化、里程校验和被引用车辆删除规则 | 写按钮禁用 |
`lastleg-service` 的 `GET /api/platform/v1/last-leg/us-truck-orders` 等接口管理的是本土卡车业务订单,写操作权限为 `lastleg:us-trucking:manage`,状态从 `PENDING_REVIEW` 经收货、分配、派车、运输推进至 `COMPLETED`。它不是车辆档案,不得用订单字段拼装车辆主数据或复用其权限。
## 前端权限边界
- 海外仓报价:`platform:overseas-master-data:quote:list`
- 汇率管理:`platform:overseas-master-data:exchange-rate:list`
- 车辆列表/详情:`platform:overseas-master-data:vehicle:list`
新增、编辑、启停、删除、导出等写操作继续按动作使用独立权限,但编码与接口需等海外区域契约确认后再接入。列表权限只包含对应列表、详情和只读辅助数据访问。
新增、编辑、启停、删除、导出等写操作继续按动作使用独立权限。列表权限只包含对应列表、详情和只读辅助数据访问。
@@ -4,8 +4,8 @@
| 页面 | 操作 | 原型证据 | 原型承载方式 | 原型尺寸/布局 | 当前实现 | 结论 |
|---|---|---|---|---|---|---|
| 合作仓 `gen-partner-warehouse` | 列表/筛选 | `10-general.html:50-56` | 现有仓库管理页内分类页签 | 自有仓筛选、代码/地址、国家;操作列在表格末尾(与仓库稳定页的全局操作列第一约定冲突时服从全局约定) | 不新增重复页面 | 建议类型码 `OVERSEAS_PARTNER` 未生效,不得冒充 `OVERSEAS` |
| 合作仓 | 新增/编辑 | `overlays.html:1731-1756`、`app.js:1655-1701` | 居中弹框 `modal-fba-add` | 最大约 560px;单列标签表单 | 现有仓库页暂不开放该类型 | 后端正式接受合作仓类型后复用现有仓库弹框和写权限 |
| 合作仓 `gen-partner-warehouse` | 列表/筛选 | `10-general.html:50-56` | 现有仓库管理页内分类 | 自有仓筛选、代码/地址、国家;操作列在表格末尾(与仓库稳定页的全局操作列第一约定冲突时服从全局约定) | 现有仓库页新增 `OVERSEAS_PARTNER` 类型筛选,不新增重复页面 | 使用国内公共仓库接口与 `platform:warehouse:list`;普通 `OVERSEAS` 语义不变 |
| 合作仓 | 新增/编辑 | `overlays.html:1731-1756`、`app.js:1655-1701` | 居中弹框 `modal-fba-add` | 最大约 560px;单列标签表单 | 复用现有仓库居中弹框,开放 `OVERSEAS_PARTNER` 新增和编辑 | 分别复用 `platform:warehouse:create`、`platform:warehouse:edit`,不跨区域查询报价候选 |
| 海外仓报价 `gen-overseas-quote` | 列表/筛选 | `10-general.html:152-169`、`app.js:14007-14039` | 独立列表页 | 五项筛选;操作列固定在第一列;横向密集表格 | 正式模块列表页、结构化空态 | 保留原型字段;等待海外查询接口 |
| 海外仓报价 | 详情 | `10-general.html:201-210`、`app.js:14043-14081` | 居中弹框 `modal-gen-overseas-quote-detail` | 约 980px;摘要、仓储作业费用、中转派送价格 | 写入口禁用,详情无记录时不生成示例对象 | 接口接入后使用居中弹框,不改抽屉或独立页 |
| 海外仓报价 | 新增/编辑 | `10-general.html:171-199`、`app.js:14135-14202` | 居中弹框 `modal-gen-overseas-quote` | 最大约 1180px;基础信息、费用明细、派送价格;正文滚动 | 工具栏写按钮禁用 | 等写接口与动作权限确认后接入,不模拟保存成功 |
@@ -73,4 +73,14 @@ describe('overseas master data pending-contract pages', () => {
for (const modal of ['modal-gen-overseas-quote-detail', 'modal-gen-overseas-quote', 'modal-exchange-rate-edit', 'modal-vehicle-edit']) expect(matrix).toContain(modal)
expect(matrix).toContain('原型未定义车辆文件预览、审核/确认流程')
})
it('records audited service boundaries without treating truck orders as vehicle master data', () => {
const gaps = readFileSync(resolve(process.cwd(), 'src/modules/overseas-master-data/CONTRACT_GAPS.md'), 'utf8')
expect(gaps).toContain('/api/platform/v1/warehouse-admin')
expect(gaps).toContain('/api/platform/v1/last-leg/us-truck-orders')
expect(gaps).toContain('它不是车辆档案')
expect(gaps).toContain('/internal/warehouses/overseas-partners')
expect(gaps).toContain('前端不得调用该内部端点')
expect(gaps).toContain('保持结构化空态')
})
})
@@ -19,10 +19,9 @@ describe('overseas master data routes', () => {
expect(overseasMasterDataRoutes.some((route) => route.path.includes('partner-warehouse'))).toBe(false)
})
it('documents but does not activate the suggested partner warehouse type', () => {
it('activates the confirmed partner type in the existing warehouse domain without a duplicate route', () => {
const apiSource = readFileSync(resolve(process.cwd(), 'src/modules/warehouses/api/platformWarehouses.ts'), 'utf8')
const gap = readFileSync(resolve(process.cwd(), 'src/modules/warehouses/PARTNER_WAREHOUSE_CONTRACT_GAP.md'), 'utf8')
expect(gap).toContain('OVERSEAS_PARTNER')
expect(apiSource).not.toContain("'OVERSEAS_PARTNER'")
expect(apiSource).toContain("'OVERSEAS_PARTNER'")
expect(overseasMasterDataRoutes.some((route) => route.path.includes('partner-warehouse'))).toBe(false)
})
})
+7 -1
View File
@@ -1,3 +1,10 @@
/**
* 四期原型展示模型。
*
* 报价、汇率和车辆主数据尚无已确认的海外服务端 DTO;这些类型不能用于猜测
* API 路径或直接序列化请求。真实接口落地后应按 Controller/OpenAPI 单独建立
* API DTO,并在适配层转换为以下页面模型。
*/
export type MasterDataStatus = 'ACTIVE' | 'INACTIVE'
export type QuoteCurrency = 'USD' | 'CNY'
export type VehicleRegion = 'DOMESTIC' | 'OVERSEAS'
@@ -86,4 +93,3 @@ export interface VehicleDetail extends VehicleSummary {
deliveryOrders: VehicleDeliveryOrder[]
replacementHistory: VehicleReplacementHistory[]
}