feat(appointments): track forecast impacts and handling history

This commit is contained in:
2026-10-10 15:33:13 +08:00
parent 331f750871
commit 0ac2b849c3
18 changed files with 1323 additions and 0 deletions
@@ -3523,3 +3523,43 @@ CREATE TABLE IF NOT EXISTS ll_procurement_closure_item (
PRIMARY KEY(id), KEY idx_procurement_closure_source(closure_id,source_kind,source_id),
CONSTRAINT chk_procurement_closure_quantity CHECK(quantity>0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常关闭逐来源剩余货物最终去向,不产生新发运';
CREATE TABLE IF NOT EXISTS ll_appointment_impact (
container_booking_id VARCHAR(64) NOT NULL COMMENT '稳定柜次关联键,不作业务编号展示',
impact_no VARCHAR(40) NOT NULL COMMENT '船期核对事项业务号',
baseline_json LONGTEXT NOT NULL COMMENT '已核对基准或首次变化前观察,原来源时间口径',
latest_json LONGTEXT NOT NULL COMMENT '最新已读取观察,未知可为JSON null',
forecast_token CHAR(64) NOT NULL COMMENT '最新观察摘要,处理时防止旧预测结案',
status VARCHAR(20) NOT NULL COMMENT '事项状态:MONITORING监测/OPEN待处理/RESOLVED已完成',
reason VARCHAR(64) NULL COMMENT '预测变化或资料待核实原因',
owner_id VARCHAR(128) NULL COMMENT '认领平台人员ID',
owner_name VARCHAR(200) NULL COMMENT '负责人姓名快照',
version INT NOT NULL COMMENT '观察及人工处理共同版本',
updated_at DATETIME(3) NOT NULL COMMENT 'UTC更新时间',
PRIMARY KEY (container_booking_id),
UNIQUE KEY uk_appointment_impact_no (impact_no),
KEY idx_appointment_impact_work (status,updated_at),
CONSTRAINT chk_appointment_impact_status CHECK (status IN ('MONITORING','OPEN','RESOLVED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='一柜一个稳定船期变化核对事项,不改写预约或运输';
CREATE TABLE IF NOT EXISTS ll_appointment_impact_history (
id BIGINT NOT NULL COMMENT '历史记录标识',
container_booking_id VARCHAR(64) NOT NULL COMMENT '关联柜次标识',
version INT NOT NULL COMMENT '记录版本',
action VARCHAR(20) NOT NULL COMMENT '操作类型:OBSERVED观察/CLAIM认领/RECORD记录/RESOLVE完成',
operator_id VARCHAR(128) NOT NULL COMMENT '操作人标识',
operator_name VARCHAR(200) NULL COMMENT '操作人姓名',
result VARCHAR(1000) NULL COMMENT '人工处理结果;观察记录为空',
next_action VARCHAR(500) NULL COMMENT '下一步处理事项',
followup_date DATE NULL COMMENT '业务跟进日期',
command_key VARCHAR(64) NULL COMMENT '人工命令幂等键;观察记录为空',
request_hash CHAR(64) NULL COMMENT '请求内容摘要',
response_json LONGTEXT NOT NULL COMMENT '该版本完整响应快照,支持精确重放',
created_at DATETIME(3) NOT NULL COMMENT 'UTC记录时间',
PRIMARY KEY (id),
UNIQUE KEY uk_appointment_impact_version (container_booking_id,version),
UNIQUE KEY uk_appointment_impact_command (container_booking_id,operator_id,command_key),
CONSTRAINT fk_appointment_impact_history FOREIGN KEY (container_booking_id) REFERENCES ll_appointment_impact(container_booking_id),
CONSTRAINT chk_appointment_impact_action CHECK (action IN ('OBSERVED','CLAIM','RECORD','RESOLVE'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='船期观察及人工处理追加历史';
@@ -0,0 +1,643 @@
{
"openapi": "3.0.3",
"info": {
"title": "船期变化影响核对",
"version": "1.0.0"
},
"security": [
{
"bearerAuth": []
}
],
"paths": {
"/api/platform/v1/last-leg/appointment-planning/impacts": {
"get": {
"operationId": "listAppointmentImpacts",
"description": "仅平台主体,同时要求platform:appointment:list和platform:overseas-warehouse:last-mile-order:list。按现有美国活动柜次分页,impact为空表示尚未核对。GET不读取远程船期、不创建记录。",
"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": "ApiResponse,业务成功code为字符串200",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/ImpactPage"
}
}
}
}
}
},
"403": {
"description": "非平台主体或缺少权限"
}
}
}
},
"/api/platform/v1/last-leg/appointment-planning/impacts/refresh": {
"post": {
"operationId": "refreshAppointmentImpacts",
"description": "仅平台主体,同时要求platform:appointment:list和platform:overseas-warehouse:last-mile-order:list。写入另需platform:appointment:followup。显式批量核对当前页,一次Firstleg来源查询;范围不完整或来源失败保留原状态并报错。稳定柜次事项和追加观察历史,不改外部确认、订单或车次。来源时间时区未核实,不转换为UTC或推算备妥窗口。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"page": {
"type": "integer",
"minimum": 1
},
"size": {
"type": "integer",
"minimum": 1,
"maximum": 100
},
"keyword": {
"type": "string",
"maxLength": 128
}
},
"required": [
"page",
"size"
]
}
}
}
},
"responses": {
"200": {
"description": "ApiResponse,业务成功code为字符串200",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/Impacts"
}
}
}
}
}
},
"403": {
"description": "非平台主体或缺少权限"
}
}
}
},
"/api/platform/v1/last-leg/appointment-planning/impacts/{id}": {
"get": {
"operationId": "readAppointmentImpact",
"description": "仅平台主体,同时要求platform:appointment:list和platform:overseas-warehouse:last-mile-order:list。直接货物和预约按客户归属关联;运输明细另外要求platform:fleet:trip:list,无权时transport为null。sameTripBookings仅提示同车运输承诺复核,不断言直接延期。明细任一范围超过1000拒绝,不静默截断。历史服务端分页,业务确认时间保持原值。",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "内部柜次关联键,不作界面业务编号"
},
{
"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": {
"description": "ApiResponse,业务成功code为字符串200",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/ImpactDetail"
}
}
}
}
}
},
"403": {
"description": "非平台主体或缺少权限"
}
}
}
},
"/api/platform/v1/last-leg/appointment-planning/impacts/{id}/commands": {
"post": {
"operationId": "handleAppointmentImpact",
"description": "仅平台主体,同时要求platform:appointment:list和platform:overseas-warehouse:last-mile-order:list。写入另需platform:appointment:followup。CLAIM认领当前人;RECORD和RESOLVE必须填写实际结果。其他人已认领、事项版本或来源观察变化时拒绝;同操作者同键同体精确重放原响应。处理完成仅确认核对事项,绝不改约/撤销承运/改库存。处理后新预测变化重新打开同一事项。",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "内部柜次关联键,不作界面业务编号"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"expectedVersion": {
"type": "integer",
"minimum": 0
},
"forecastToken": {
"type": "string"
},
"idempotencyKey": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"action": {
"type": "string",
"enum": [
"CLAIM",
"RECORD",
"RESOLVE"
]
},
"result": {
"type": "string",
"nullable": true,
"maxLength": 1000
},
"nextAction": {
"type": "string",
"nullable": true,
"maxLength": 500
},
"followupDate": {
"type": "string",
"format": "date",
"nullable": true
}
},
"required": [
"expectedVersion",
"forecastToken",
"idempotencyKey",
"action"
]
}
}
}
},
"responses": {
"200": {
"description": "ApiResponse,业务成功code为字符串200",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"data": {
"$ref": "#/components/schemas/Impact"
}
}
}
}
}
},
"403": {
"description": "非平台主体或缺少权限"
}
}
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
},
"schemas": {
"Fact": {
"type": "object",
"properties": {
"containerBookingId": {
"type": "string",
"nullable": true
},
"factId": {
"type": "string",
"nullable": true
},
"providerCode": {
"type": "string",
"nullable": true
},
"sourceReference": {
"type": "string",
"nullable": true
},
"observedAt": {
"type": "string",
"nullable": true
},
"estimatedAt": {
"type": "string",
"nullable": true
},
"actualAt": {
"type": "string",
"nullable": true
}
},
"nullable": true
},
"Impact": {
"type": "object",
"properties": {
"containerBookingId": {
"type": "string"
},
"impactNo": {
"type": "string"
},
"version": {
"type": "integer"
},
"status": {
"type": "string",
"enum": [
"MONITORING",
"OPEN",
"RESOLVED"
]
},
"reason": {
"type": "string",
"nullable": true
},
"ownerId": {
"type": "string",
"nullable": true
},
"ownerName": {
"type": "string",
"nullable": true
},
"baseline": {
"$ref": "#/components/schemas/Fact"
},
"latest": {
"$ref": "#/components/schemas/Fact"
},
"forecastToken": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"ImpactPage": {
"type": "object",
"properties": {
"records": {
"type": "array",
"items": {
"type": "object",
"properties": {
"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",
"nullable": true
},
"sourceVersion": {
"type": "string",
"nullable": true
},
"incompleteScope": {
"type": "boolean"
},
"warehouses": {
"type": "array",
"items": {
"type": "object",
"properties": {
"warehouseId": {
"type": "string"
},
"warehouseName": {
"type": "string",
"nullable": true
},
"orderCount": {
"type": "integer"
},
"customerCount": {
"type": "integer"
}
}
}
}
}
},
"impact": {
"allOf": [
{
"$ref": "#/components/schemas/Impact"
}
],
"nullable": true
}
}
}
},
"total": {
"type": "integer",
"format": "int64"
},
"page": {
"type": "integer"
},
"size": {
"type": "integer"
},
"pages": {
"type": "integer"
}
}
},
"Impacts": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Impact"
}
},
"ImpactDetail": {
"type": "object",
"properties": {
"impact": {
"$ref": "#/components/schemas/Impact"
},
"cargo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"customerId": {
"type": "string"
},
"orderId": {
"type": "string"
},
"inboundCode": {
"type": "string",
"nullable": true
},
"customerName": {
"type": "string",
"nullable": true
},
"packages": {
"type": "integer",
"nullable": true
},
"destination": {
"type": "string",
"nullable": true
}
}
}
},
"appointments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"appointmentNo": {
"type": "string"
},
"status": {
"type": "string"
},
"destination": {
"type": "string",
"nullable": true
},
"confirmedAt": {
"type": "string",
"nullable": true
},
"timezone": {
"type": "string",
"nullable": true
}
}
}
},
"transport": {
"type": "object",
"properties": {
"trips": {
"type": "array",
"items": {
"type": "object",
"properties": {
"tripNo": {
"type": "string"
},
"status": {
"type": "string"
},
"plannedDepartureAt": {
"type": "string",
"nullable": true
}
}
}
},
"sameTripBookings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"tripNo": {
"type": "string"
},
"bookingNo": {
"type": "string"
},
"customerName": {
"type": "string",
"nullable": true
},
"destinationLabel": {
"type": "string",
"nullable": true
},
"status": {
"type": "string"
}
}
}
}
},
"nullable": true
},
"history": {
"type": "object",
"properties": {
"records": {
"type": "array",
"items": {
"type": "object",
"properties": {
"version": {
"type": "integer"
},
"action": {
"type": "string"
},
"operatorName": {
"type": "string",
"nullable": true
},
"result": {
"type": "string",
"nullable": true
},
"nextAction": {
"type": "string",
"nullable": true
},
"followupDate": {
"type": "string",
"format": "date",
"nullable": true
},
"createdAt": {
"type": "string"
}
}
}
},
"total": {
"type": "integer",
"format": "int64"
},
"page": {
"type": "integer"
},
"size": {
"type": "integer"
},
"pages": {
"type": "integer"
}
}
}
}
}
}
}
}