fix: complete October 22 workflow feedback and request failure recovery

This commit is contained in:
2026-10-22 23:06:00 +08:00
parent f701456de1
commit a16a85e14a
85 changed files with 1518 additions and 277 deletions
@@ -232,7 +232,7 @@ DROP TRIGGER IF EXISTS trg_ocean_booking_before_update;
DELIMITER $$
CREATE TRIGGER trg_ocean_booking_before_update BEFORE UPDATE ON fl_ocean_booking FOR EACH ROW
BEGIN
-- 已放舱只允许实货量及其审计元数据变化,运输和预报快照保持冻结。
-- 已放舱只允许预计到港、实货量及审计元数据变化,其余运输和预报快照保持冻结。
IF OLD.status = 'RELEASED' AND NOT (
(NEW.id <=> OLD.id)
AND (NEW.booking_no <=> OLD.booking_no)
@@ -257,7 +257,6 @@ BEGIN
AND (NEW.destination_port_name <=> OLD.destination_port_name)
AND (NEW.place_of_delivery <=> OLD.place_of_delivery)
AND (NEW.estimated_sailing_at <=> OLD.estimated_sailing_at)
AND (NEW.estimated_arrival_at <=> OLD.estimated_arrival_at)
AND (NEW.remark <=> OLD.remark)
AND (NEW.freight_term <=> OLD.freight_term)
AND (NEW.service_term <=> OLD.service_term)
@@ -272,7 +271,7 @@ BEGIN
AND (NEW.created_at <=> OLD.created_at)
AND NEW.version = OLD.version + 1
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已放舱运输资料不可修改,仅允许补录实货量';
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已放舱仅允许维护预计到港及补录实货量';
END IF;
IF OLD.status <> NEW.status AND NOT (
(OLD.status='DRAFT' AND NEW.status='RELEASED')) THEN
@@ -35,7 +35,7 @@ CREATE TABLE IF NOT EXISTS fl_document_sequence (
CREATE TABLE IF NOT EXISTS fl_container_booking (
id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID(雪花ID)',
container_no VARCHAR(64) NULL COMMENT '国内柜次真实集装箱号权威快照;ISO 6346标准格式;同一订舱内唯一,不同提单允许复用;订舱关联可选写入、提柜后可原子补录、预装纠错命令可修改',
container_no VARCHAR(64) NULL COMMENT '国内柜次真实集装箱号权威快照;订舱关联不限制ISO格式;同一订舱内唯一,不同提单允许复用;订舱关联可选写入、提柜后可原子补录、预装纠错命令可修改',
cabinet_no VARCHAR(64) NOT NULL COMMENT '平台唯一内部柜号',
source_type VARCHAR(24) NOT NULL DEFAULT 'SIMULATION' COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜',
record_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '记录状态:ACTIVE可操作、MERGED已合并历史',
@@ -100,9 +100,7 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
CONSTRAINT chk_container_booking_record_status CHECK (record_status IN ('ACTIVE','MERGED')),
CONSTRAINT chk_container_booking_capacity CHECK (capacity_cbm IS NULL OR capacity_cbm > 0),
CONSTRAINT chk_container_booking_weight CHECK (max_weight_kg IS NULL OR max_weight_kg > 0),
CONSTRAINT chk_container_booking_pickup_code CHECK (pickup_code IS NULL OR pickup_code REGEXP '^[0-9]{1,64}$'),
CONSTRAINT chk_container_booking_iso6346 CHECK
(container_no IS NULL OR container_no REGEXP '^[A-Z]{3}[UJZ][0-9]{7}$')
CONSTRAINT chk_container_booking_pickup_code CHECK (pickup_code IS NULL OR pickup_code REGEXP '^[0-9]{1,64}$')
) ENGINE=InnoDB COMMENT='平台货柜预订资料;预装状态唯一来源为装柜计划';
CREATE TABLE IF NOT EXISTS fl_container_event_inbox (
@@ -228,11 +226,6 @@ BEGIN
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_record_status
CHECK (record_status IN ('ACTIVE','MERGED'));
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_iso6346') THEN
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_iso6346
CHECK (container_no IS NULL OR container_no REGEXP '^[A-Z]{3}[UJZ][0-9]{7}$');
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_pickup_code') THEN
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_pickup_code
@@ -118,9 +118,9 @@ paths:
x-permission: platform:firstleg:customs:list
parameters: [{$ref: '#/components/parameters/DocumentId'}]
responses: {'200': {$ref: '#/components/responses/BinaryResponse'}}
/api/platform/v1/customs/loading-plans/documents/{documentId}/receipt/download:
/api/platform/v1/customs/loading-plans/documents/{documentId}/completion/download:
get:
summary: 平台下载报关行回执
summary: 平台下载预录单
x-permission: platform:firstleg:customs:list
parameters: [{$ref: '#/components/parameters/DocumentId'}]
responses: {'200': {$ref: '#/components/responses/BinaryResponse'}}
@@ -607,7 +607,7 @@ paths:
/api/platform/v1/containers/{id}/ocean-booking-link:
post:
summary: 将内部模拟柜关联到已发布订舱需求
description: 需要 platform:firstleg:container:edit;请求指定 RELEASED oceanBookingId 和未满配 requirementId,containerNo 可空。首次关联允许 WAIT_LOADING 或已有预排货的 EDITING 模拟柜,保留原 containerBookingId/cabinetNo/loadingPlanId/items,原子写入订舱、运输和范围快照并创建 allocation;提柜后由拖车链调用柜次服务原子补录真实柜号。若关联请求已提供 containerNo,服务端仍规范化并校验 ISO 6346 结构、设备类别和校验位,并保证同一订舱内唯一;不同提单允许复用同一集装箱号。存在跨仓、跨渠道、柜型规格不一致、超量、同提单重复柜号、PDA、有效装柜证据、业务单证或 LOADING/COMPLETED 等事实时拒绝。
description: 需要 platform:firstleg:container:edit;请求指定 RELEASED oceanBookingId 和未满配 requirementId,containerNo 可空。首次关联允许 WAIT_LOADING 或已有预排货的 EDITING 模拟柜,保留原 containerBookingId/cabinetNo/loadingPlanId/items,原子写入订舱、运输和范围快照并创建 allocation;提柜后由拖车链调用柜次服务原子补录真实柜号。若关联请求已提供 containerNo,服务端仅移除空白并转大写,不校验 ISO 6346 长度、结构或校验位,并保证同一订舱内唯一;不同提单允许复用同一集装箱号。关联接受的柜号在运输信息未修改柜号时不重复执行ISO校验;确认预装和创建PDA任务前仍校验ISO格式,并提示在预装运输信息中纠错,防止实物作业后才发现无法完成。存在跨仓、跨渠道、柜型规格不一致、超量、同提单重复柜号、PDA、有效装柜证据、业务单证或 LOADING/COMPLETED 等事实时拒绝。
parameters: [{$ref: '#/components/parameters/ContainerBookingId'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ContainerOceanBookingLinkRequest'}}}}
responses: {'200': {description: 关联后的同一模拟柜 containerBookingId/cabinetNo;列表仍只有这一条 ACTIVE 操作身份}}
@@ -2947,7 +2947,7 @@ components:
properties:
oceanBookingId: {type: integer, format: int64, minimum: 1, description: RELEASED订舱ID}
requirementId: {type: integer, format: int64, minimum: 1, description: 未满配的订舱柜型需求ID}
containerNo: {type: string, nullable: true, maxLength: 64, description: 可选;港口提柜后可由拖车链另行原子补录。非空时服务端移除格式空白、转大写并校验ISO 6346结构、设备类别、校验位和同一订舱内唯一性}
containerNo: {type: string, nullable: true, maxLength: 64, description: 可选;港口提柜后可由拖车链另行原子补录。关联时移除格式空白并转大写,不校验ISO长度、结构或校验位,仍保证同一订舱内唯一;确认预装及创建PDA任务前需纠正为符合ISO格式的真实柜号}
expectedVersion: {type: integer, minimum: 0}
ContainerFeeAdjustRequest:
type: object
@@ -50,7 +50,7 @@ paths:
summary: 修改预装运输信息
description: >-
可在 EDITING 或 LOADING 阶段首次补录真实集装箱号;已有柜号的纠正仅限仍为 EDITING、PDA任务尚未领取且无任何物理作业事实时,并必须提供原因和当前版本;
containerNo 为 null 表示保持当前值,非空时服务端移除格式空白、转大写并校验ISO 6346结构、设备类别和校验位,空白字符串非法且禁止清空。
containerNo 为 null 或规范化后等于当前值时保留关联入口已接受的柜号,不重复校验格式;首次录入或纠正为不同值时服务端移除格式空白、转大写并校验ISO 6346结构、设备类别和校验位,空白字符串非法且禁止清空。
x-permission: platform:firstleg:preload:edit
parameters:
- {$ref: '#/components/parameters/PlanId'}
@@ -29,6 +29,14 @@ paths:
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}}}
responses: {'200': {description: Deleted}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/estimated-arrival:
put:
summary: 放舱后维护预计到港
description: 仅允许RELEASED订舱按当前版本与幂等键维护estimatedArrivalAt,不得清空或早于预计开船时间;其他运输、预报、关系方、柜型与附件仍冻结,保存前后快照及操作人。不修改实际到港或计划到仓。
x-permission: platform:firstleg:ocean-booking:edit
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/EstimatedArrivalRequest'}}}}
responses: {'200': {description: 已保存的RELEASED订舱详情,包含当前版本和预计到港}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/actual-cargo:
put:
summary: Supplement manual actual cargo on a RELEASED booking
@@ -217,6 +225,13 @@ components:
containerRequirements: {type: array, nullable: true, items: {$ref: '#/components/schemas/ContainerRequirementRequest'}}
PartyRequest: {type: object, required: [partyType, partyName], properties: {partyType: {type: string, enum: [SHIPPER, CONSIGNEE, NOTIFY_PARTY]}, partyName: {type: string}, address: {type: string, nullable: true}, contactName: {type: string, nullable: true}, contactPhone: {type: string, nullable: true}, contactEmail: {type: string, format: email, maxLength: 255, nullable: true}, taxNo: {type: string, maxLength: 128, nullable: true}}}
ContainerRequirementRequest: {type: object, required: [containerType, containerKind, quantity, capacityCbm, maxWeightKg], properties: {containerType: {type: string, enum: [40GP, 40HQ, 45HQ], default: 40HQ}, containerKind: {type: string}, quantity: {type: integer, minimum: 1}, capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, description: 统一实装上限;40GP=58、40HQ=68、45HQ=86}, maxWeightKg: {type: number, enum: [19500], description: 统一货重上限}}}
EstimatedArrivalRequest:
type: object
additionalProperties: false
required: [expectedVersion, estimatedArrivalAt]
properties:
expectedVersion: {type: integer, minimum: 0}
estimatedArrivalAt: {type: string, format: date-time}
ActualCargoRequest:
type: object
additionalProperties: false