feat(appointment): share pickup and receiving followups by container

This commit is contained in:
2026-10-10 08:42:01 +08:00
parent 885cac13a4
commit 24f5ccf7ff
14 changed files with 513 additions and 21 deletions
@@ -1627,8 +1627,8 @@ CREATE TABLE IF NOT EXISTS ll_pda_inventory_move (
CREATE TABLE IF NOT EXISTS ll_appointment_followup (
id BIGINT NOT NULL COMMENT '预约筹划跟进记录ID',
source_key VARCHAR(160) NOT NULL COMMENT '稳定ORDER或BOOKING来源键,不因实收转换而变化',
customer_id VARCHAR(128) NOT NULL COMMENT '服务端核实的来源客户',
source_key VARCHAR(160) NOT NULL COMMENT '稳定ORDER、BOOKING或CONTAINER及接仓点来源键',
customer_id VARCHAR(128) NOT NULL COMMENT '货源客户;整柜共享记录由服务端固定为平台作用域0',
source_order_id BIGINT NULL COMMENT '已形成的真实尾程货源ID,尚未生成时为空',
stage VARCHAR(32) NOT NULL COMMENT '跟进对象:DRAYAGE_CONTACT/TERMINAL_APPOINTMENT/WAREHOUSE_RECEIVING/FBA_RECEIVING',
version INT NOT NULL COMMENT '同一货源及跟进对象的记录版本',
@@ -6,6 +6,108 @@ info:
security:
- bearerAuth: []
paths:
/api/platform/v1/last-leg/appointment-planning/containers:
get:
operationId: appointmentPlanningContainers
summary: 分页读取美国整柜提柜与接仓范围
description: >-
仅平台,同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。
已实际开船且仍有效的FIRST_LEG货物按柜汇总,不限FBA目的地,不要求入仓计划。
仅统计美国接仓点货物;存在未知或非美国接仓点的同柜有效货物时incompleteScope=true,禁止新增整柜跟进。
关键词命中某票时仍返回整柜美国范围数量;任一数量缺失时packages为空。预计到港保留来源快照,不代表实时船司ETA。
parameters:
- {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}}
- {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
- {name: keyword, in: query, schema: {type: string, maxLength: 128}}
responses:
'200':
description: 核对code后读取整柜分页
content:
application/json:
schema:
type: object
properties:
code: {type: string}
message: {type: string}
data:
type: object
properties:
records: {type: array, items: {$ref: '#/components/schemas/Container'}}
page: {type: integer}
size: {type: integer}
total: {type: integer}
pages: {type: integer}
'400': {description: 分页或搜索条件无效}
'403': {description: 缺少读取权限或非平台身份}
'503': {description: 美国仓库目录不可用}
/api/platform/v1/last-leg/appointment-planning/containers/{containerId}:
parameters:
- {$ref: '#/components/parameters/ContainerId'}
- {$ref: '#/components/parameters/ReceivingWarehouse'}
get:
operationId: resolveAppointmentContainer
summary: 核对整柜或接仓点的跟进资格
description: 仅平台,同时要求两项列表权限;接仓点必须属于当前柜次。内部柜次ID仅供关联,不能作为展示柜号。
responses:
'200':
description: code=200时data为已核实上下文
content:
application/json:
schema:
type: object
properties:
code: {type: string}
data: {$ref: '#/components/schemas/ContainerContext'}
'400': {description: 标识无效或接仓点不属于该柜}
'403': {description: 缺少读取权限或非平台身份}
'404': {description: 未找到有效美国整柜来源}
'503': {description: 仓库目录不可用}
/api/platform/v1/last-leg/appointment-planning/containers/{containerId}/follow-ups:
parameters:
- {$ref: '#/components/parameters/ContainerId'}
- {$ref: '#/components/parameters/ReceivingWarehouse'}
get:
operationId: appointmentContainerFollowupHistory
summary: 读取整柜或接仓点共享跟进历史
description: 仅平台,同时要求两项列表权限;已有历史在来源离开活动列表后仍可读取,不按客户拆分。
parameters:
- {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}}
- {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
responses:
'200': {$ref: '#/paths/~1api~1platform~1v1~1last-leg~1appointment-planning~1sources~1{sourceKey}~1follow-ups/get/responses/200'}
'400': {description: 标识或分页无效}
'403': {description: 缺少读取权限或非平台身份}
'404': {description: 无历史且来源不存在}
post:
operationId: appendAppointmentContainerFollowup
summary: 追加整柜提柜联系或指定仓库接仓联系
description: >-
仅平台,两项列表权限及platform:appointment:followup均必需。
未传warehouseId只接受DRAYAGE_CONTACT或TERMINAL_APPOINTMENT;传入时只接受WAREHOUSE_RECEIVING。
服务端产生共享来源键,不接收customerId或操作者字段;同柜跨客户共用一份历史。
版本、持久化幂等和跟进日期规则与货源跟进相同,不产生正式预约或执行结果。
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [stage, expectedVersion, idempotencyKey, result]
properties:
stage: {type: string, enum: [DRAYAGE_CONTACT, TERMINAL_APPOINTMENT, WAREHOUSE_RECEIVING]}
expectedVersion: {type: integer, minimum: 0}
idempotencyKey: {type: string, minLength: 1, maxLength: 64}
contactedParty: {type: string, maxLength: 200, nullable: true}
result: {type: string, minLength: 1, maxLength: 1000}
nextAction: {type: string, maxLength: 500, nullable: true}
followupDate: {type: string, format: date, nullable: true}
responses:
'200': {$ref: '#/paths/~1api~1platform~1v1~1last-leg~1appointment-planning~1sources~1{sourceKey}~1follow-ups/post/responses/200'}
'400': {description: 事项与范围不符或参数无效}
'403': {description: 缺少权限或非平台身份}
'404': {description: 来源不存在}
'409': {description: 范围待核对、版本或幂等内容冲突}
'503': {description: 仓库目录不可用}
/api/platform/v1/last-leg/appointment-planning/cargo:
get:
operationId: appointmentPlanningCargo
@@ -123,7 +225,7 @@ paths:
type: object
required: [stage, expectedVersion, idempotencyKey, result]
properties:
stage: {type: string, enum: [DRAYAGE_CONTACT, TERMINAL_APPOINTMENT, WAREHOUSE_RECEIVING, FBA_RECEIVING]}
stage: {type: string, enum: [FBA_RECEIVING], description: 提柜及接仓统一使用整柜接口,不按客户货源重复登记}
expectedVersion: {type: integer, minimum: 0, description: 初次为0,后续取history.versions中对应事项版本}
idempotencyKey: {type: string, maxLength: 64}
contactedParty: {type: string, maxLength: 200, nullable: true}
@@ -146,9 +248,45 @@ paths:
'404': {description: 来源不存在或不可筹划}
'409': {description: 版本冲突或同键异内容}
components:
parameters:
ContainerId: {name: containerId, in: path, required: true, schema: {type: string, pattern: '^[A-Za-z0-9_-]{1,64}$'}}
ReceivingWarehouse: {name: warehouseId, in: query, schema: {type: string, pattern: '^[0-9]{1,20}$'}, description: 不传表示整柜,传入表示该柜实际接仓点}
securitySchemes:
bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
schemas:
Container:
type: object
properties:
containerBookingId: {type: string}
containerNo: {type: string, nullable: true}
orderCount: {type: integer}
customerCount: {type: integer}
packages: {type: integer, nullable: true}
unknownPackageOrders: {type: integer}
earliestArrivalAt: {type: string, nullable: true}
latestArrivalAt: {type: string, nullable: true}
standardNode: {type: string}
sourceVersion: {type: string, nullable: true}
incompleteScope: {type: boolean}
warehouses:
type: array
items:
type: object
properties:
containerBookingId: {type: string}
warehouseId: {type: string}
warehouseName: {type: string}
orderCount: {type: integer}
customerCount: {type: integer}
ContainerContext:
type: object
properties:
containerBookingId: {type: string}
containerNo: {type: string, nullable: true}
warehouseId: {type: string, nullable: true}
warehouseName: {type: string, nullable: true}
canFollowup: {type: boolean}
blockReasons: {type: array, items: {type: string}}
FollowupRecord:
type: object
properties: