feat(firstleg): close phase3 outbound customs and schedule gates
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
SET NAMES utf8mb4;
|
||||
USE baiyue_firstleg;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_checklist_version (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
checklist_code VARCHAR(40) NOT NULL,
|
||||
version_no VARCHAR(32) NOT NULL,
|
||||
country_code VARCHAR(32) NOT NULL,
|
||||
channel_code VARCHAR(40) NOT NULL,
|
||||
cargo_type VARCHAR(40) NOT NULL,
|
||||
items_json JSON NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
created_by BIGINT UNSIGNED NOT NULL,
|
||||
created_by_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_customs_checklist_version (checklist_code, version_no),
|
||||
KEY idx_customs_checklist_match (country_code, channel_code, cargo_type, status),
|
||||
CONSTRAINT chk_customs_checklist_status CHECK (status IN ('ACTIVE','RETIRED'))
|
||||
) ENGINE=InnoDB COMMENT='三期报关资料清单不可变版本';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_case (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
customs_no VARCHAR(40) NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
order_id BIGINT UNSIGNED NOT NULL,
|
||||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||||
container_booking_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
checklist_version_id BIGINT UNSIGNED NOT NULL,
|
||||
checklist_code VARCHAR(40) NOT NULL,
|
||||
checklist_version_no VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'DOCUMENTS_PENDING',
|
||||
remark VARCHAR(500) NULL,
|
||||
created_by BIGINT UNSIGNED NOT NULL,
|
||||
created_by_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_by BIGINT UNSIGNED NOT NULL,
|
||||
updated_by_name VARCHAR(64) NULL,
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_customs_no (customs_no),
|
||||
UNIQUE KEY uk_customs_case_scope (tenant_id, order_id, inbound_id, container_booking_id),
|
||||
KEY idx_customs_case_status (status, updated_at),
|
||||
KEY idx_customs_case_container (container_booking_id),
|
||||
CONSTRAINT fk_customs_case_checklist FOREIGN KEY (checklist_version_id) REFERENCES fl_customs_checklist_version(id),
|
||||
CONSTRAINT chk_customs_case_status CHECK (status IN ('DOCUMENTS_PENDING','REVIEWING','SUPPLEMENT_REQUIRED','READY_TO_SUBMIT','RECEIPT_PENDING','RECEIPT_AVAILABLE','CLEARANCE_COMPLETED','CANCELLED'))
|
||||
) ENGINE=InnoDB COMMENT='三期订单报关协同聚合';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_document (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL,
|
||||
checklist_item_code VARCHAR(40) NOT NULL,
|
||||
file_id BIGINT UNSIGNED NOT NULL,
|
||||
document_version INT UNSIGNED NOT NULL,
|
||||
replaces_document_id BIGINT UNSIGNED NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_hash CHAR(64) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'UPLOADED',
|
||||
visibility VARCHAR(16) NOT NULL DEFAULT 'CUSTOMER',
|
||||
uploaded_by BIGINT UNSIGNED NOT NULL,
|
||||
uploaded_by_type VARCHAR(16) NOT NULL,
|
||||
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
reviewed_by BIGINT UNSIGNED NULL,
|
||||
reviewed_by_name VARCHAR(64) NULL,
|
||||
reviewed_at DATETIME(3) NULL,
|
||||
review_reason VARCHAR(500) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_customs_document_version (customs_case_id, checklist_item_code, document_version),
|
||||
UNIQUE KEY uk_customs_document_file (customs_case_id, file_id),
|
||||
KEY idx_customs_document_case (customs_case_id, status),
|
||||
CONSTRAINT fk_customs_document_case FOREIGN KEY (customs_case_id) REFERENCES fl_customs_case(id),
|
||||
CONSTRAINT chk_customs_document_status CHECK (status IN ('UPLOADED','APPROVED','RETURNED','SUPERSEDED','FROZEN')),
|
||||
CONSTRAINT chk_customs_document_actor CHECK (uploaded_by_type IN ('PLATFORM','MERCHANT')),
|
||||
CONSTRAINT chk_customs_document_visibility CHECK (visibility IN ('PLATFORM','CUSTOMER'))
|
||||
) ENGINE=InnoDB COMMENT='报关客户文件版本引用';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_supplement_request (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL,
|
||||
source_document_id BIGINT UNSIGNED NOT NULL,
|
||||
resolved_document_id BIGINT UNSIGNED NULL,
|
||||
checklist_item_code VARCHAR(40) NOT NULL,
|
||||
reason VARCHAR(500) NOT NULL,
|
||||
due_at DATETIME(3) NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'OPEN',
|
||||
requested_by BIGINT UNSIGNED NOT NULL,
|
||||
requested_by_name VARCHAR(64) NULL,
|
||||
requested_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
resolved_by BIGINT UNSIGNED NULL,
|
||||
resolved_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_customs_supplement_case (customs_case_id, status, due_at),
|
||||
CONSTRAINT fk_customs_supplement_case FOREIGN KEY (customs_case_id) REFERENCES fl_customs_case(id),
|
||||
CONSTRAINT chk_customs_supplement_status CHECK (status IN ('OPEN','RESOLVED','CANCELLED'))
|
||||
) ENGINE=InnoDB COMMENT='报关补件请求';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_submission_batch (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL,
|
||||
batch_no VARCHAR(40) NOT NULL,
|
||||
batch_version INT UNSIGNED NOT NULL,
|
||||
customs_agent_name VARCHAR(128) NOT NULL,
|
||||
external_reference VARCHAR(128) NULL,
|
||||
file_snapshot_json JSON NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'SUBMITTED',
|
||||
note VARCHAR(500) NULL,
|
||||
submitted_by BIGINT UNSIGNED NOT NULL,
|
||||
submitted_by_name VARCHAR(64) NULL,
|
||||
submitted_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_customs_batch_no (batch_no),
|
||||
UNIQUE KEY uk_customs_batch_version (customs_case_id, batch_version),
|
||||
CONSTRAINT fk_customs_batch_case FOREIGN KEY (customs_case_id) REFERENCES fl_customs_case(id),
|
||||
CONSTRAINT chk_customs_batch_status CHECK (status IN ('SUBMITTED','ACKNOWLEDGED','REJECTED','COMPLETED'))
|
||||
) ENGINE=InnoDB COMMENT='报关代理提交批次及冻结文件版本';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_receipt (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL,
|
||||
submission_batch_id BIGINT UNSIGNED NOT NULL,
|
||||
receipt_type VARCHAR(16) NOT NULL,
|
||||
file_id BIGINT UNSIGNED NOT NULL,
|
||||
receipt_version INT UNSIGNED NOT NULL,
|
||||
replaces_receipt_id BIGINT UNSIGNED NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_hash CHAR(64) NOT NULL,
|
||||
visibility VARCHAR(16) NOT NULL DEFAULT 'PLATFORM',
|
||||
note VARCHAR(500) NULL,
|
||||
uploaded_by BIGINT UNSIGNED NOT NULL,
|
||||
uploaded_by_name VARCHAR(64) NULL,
|
||||
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_customs_receipt_version (submission_batch_id, receipt_type, receipt_version),
|
||||
UNIQUE KEY uk_customs_receipt_file (submission_batch_id, file_id),
|
||||
KEY idx_customs_receipt_case (customs_case_id, visibility, uploaded_at),
|
||||
CONSTRAINT fk_customs_receipt_case FOREIGN KEY (customs_case_id) REFERENCES fl_customs_case(id),
|
||||
CONSTRAINT fk_customs_receipt_batch FOREIGN KEY (submission_batch_id) REFERENCES fl_customs_submission_batch(id),
|
||||
CONSTRAINT chk_customs_receipt_type CHECK (receipt_type IN ('RECEIVED','DECLARED','REJECTED','INSPECTION','RELEASED','OTHER')),
|
||||
CONSTRAINT chk_customs_receipt_visibility CHECK (visibility IN ('PLATFORM','CUSTOMER'))
|
||||
) ENGINE=InnoDB COMMENT='报关代理回执版本与客户可见范围';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_history (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL,
|
||||
action VARCHAR(40) NOT NULL,
|
||||
from_status VARCHAR(32) NULL,
|
||||
to_status VARCHAR(32) NULL,
|
||||
resource_type VARCHAR(32) NOT NULL,
|
||||
resource_id BIGINT UNSIGNED NOT NULL,
|
||||
note VARCHAR(500) NULL,
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
operator_name VARCHAR(64) NULL,
|
||||
operator_type VARCHAR(16) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_customs_history_case (customs_case_id, created_at, id),
|
||||
CONSTRAINT fk_customs_history_case FOREIGN KEY (customs_case_id) REFERENCES fl_customs_case(id),
|
||||
CONSTRAINT chk_customs_history_actor CHECK (operator_type IN ('PLATFORM','MERCHANT'))
|
||||
) ENGINE=InnoDB COMMENT='报关协同稳定历史';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_operation (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash CHAR(64) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'PROCESSING',
|
||||
result_type VARCHAR(24) NULL,
|
||||
result_id BIGINT UNSIGNED NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
completed_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_customs_operation (operator_id, action, idempotency_key),
|
||||
CONSTRAINT chk_customs_operation_status CHECK (status IN ('PROCESSING','SUCCESS'))
|
||||
) ENGINE=InnoDB COMMENT='报关协同命令幂等记录';
|
||||
|
||||
USE baiyue_iam;
|
||||
DROP PROCEDURE IF EXISTS assert_phase3_customs_permission_ids;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE assert_phase3_customs_permission_ids()
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM iam_permission
|
||||
WHERE (id BETWEEN 5102170 AND 5102175
|
||||
AND permission_code <> CASE id
|
||||
WHEN 5102170 THEN 'platform:firstleg:customs:list'
|
||||
WHEN 5102171 THEN 'platform:firstleg:customs:view'
|
||||
WHEN 5102172 THEN 'platform:firstleg:customs:manage'
|
||||
WHEN 5102173 THEN 'platform:firstleg:customs:review'
|
||||
WHEN 5102174 THEN 'platform:firstleg:customs:submit'
|
||||
WHEN 5102175 THEN 'platform:firstleg:customs:receipt'
|
||||
END)
|
||||
OR (permission_code LIKE 'platform:firstleg:customs:%'
|
||||
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT BETWEEN 5102170 AND 5102175))
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '三期报关权限迁移失败:5102170-5102175 ID 或权限码被其他语义占用';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL assert_phase3_customs_permission_ids();
|
||||
DROP PROCEDURE IF EXISTS assert_phase3_customs_permission_ids;
|
||||
|
||||
INSERT INTO iam_permission
|
||||
(id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,
|
||||
route_path,http_method,sort_no,status,built_in,deleted)
|
||||
VALUES
|
||||
(5102170,0,'PLATFORM','MENU',5102000,'platform:firstleg:customs:list','报关协同','/first-leg/customs','GET',100,'ACTIVE',1,0),
|
||||
(5102171,0,'PLATFORM','BUTTON',5102170,'platform:firstleg:customs:view','查看报关协同',NULL,'GET',10,'ACTIVE',1,0),
|
||||
(5102172,0,'PLATFORM','BUTTON',5102170,'platform:firstleg:customs:manage','维护报关资料',NULL,'POST',20,'ACTIVE',1,0),
|
||||
(5102173,0,'PLATFORM','BUTTON',5102170,'platform:firstleg:customs:review','审核退回报关资料',NULL,'POST',30,'ACTIVE',1,0),
|
||||
(5102174,0,'PLATFORM','BUTTON',5102170,'platform:firstleg:customs:submit','提交报关代理',NULL,'POST',40,'ACTIVE',1,0),
|
||||
(5102175,0,'PLATFORM','BUTTON',5102170,'platform:firstleg:customs:receipt','维护报关回执',NULL,'POST',50,'ACTIVE',1,0)
|
||||
ON DUPLICATE KEY UPDATE tenant_id=0,app_code='PLATFORM',permission_type=VALUES(permission_type),
|
||||
permission_code=VALUES(permission_code),permission_name=VALUES(permission_name),parent_id=VALUES(parent_id),
|
||||
route_path=VALUES(route_path),http_method=VALUES(http_method),sort_no=VALUES(sort_no),status='ACTIVE',built_in=1,deleted=0;
|
||||
|
||||
INSERT IGNORE INTO iam_role_permission(role_id,permission_id,tenant_id)
|
||||
SELECT role.id,permission.id,0 FROM iam_role role JOIN iam_permission permission
|
||||
ON permission.tenant_id=0 AND permission.app_code='PLATFORM'
|
||||
AND permission.permission_code LIKE 'platform:firstleg:customs:%'
|
||||
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR');
|
||||
|
||||
USE baiyue_firstleg;
|
||||
@@ -1,13 +1,13 @@
|
||||
SET NAMES utf8mb4;
|
||||
USE baiyue_firstleg;
|
||||
|
||||
/* 装柜证据和派送完成;不设置证据完整度门禁,允许后续覆盖编辑。 */
|
||||
/* 装柜证据、PDA 拣货复核和派送完成。业务服务强制五类真实文件证据齐全后才能完成装柜。 */
|
||||
CREATE TABLE IF NOT EXISTS fl_loading_evidence (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||||
evidence_type VARCHAR(32) NOT NULL COMMENT 'PHOTO_STEP_1..4/SEAL',
|
||||
file_id BIGINT UNSIGNED NULL COMMENT 'File 服务逻辑文件ID',
|
||||
file_id BIGINT UNSIGNED NOT NULL COMMENT 'File 服务逻辑文件ID',
|
||||
file_name VARCHAR(255) NULL,
|
||||
seal_no VARCHAR(64) NULL,
|
||||
remark VARCHAR(1000) NULL,
|
||||
@@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS fl_loading_evidence (
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_loading_evidence_type (tenant_id, inbound_id, evidence_type),
|
||||
UNIQUE KEY uk_loading_evidence_seal (tenant_id, seal_no),
|
||||
KEY idx_loading_evidence_time (tenant_id, inbound_id, updated_at),
|
||||
CONSTRAINT fk_loading_evidence_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||||
) ENGINE=InnoDB COMMENT='订单装柜四步照片和铅封证据';
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
-- Phase 3 domestic outbound aggregate and dispatch gate. MySQL 8, idempotent.
|
||||
USE baiyue_firstleg;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_outbound_dispatch (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单引用,不复制订单主数据',
|
||||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单主对象',
|
||||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '柜次引用',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'READY_FOR_DISPATCH',
|
||||
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
confirmed_by BIGINT UNSIGNED NULL,
|
||||
confirmed_by_name VARCHAR(64) NULL,
|
||||
confirmed_at DATETIME(3) NULL,
|
||||
reversed_by BIGINT UNSIGNED NULL,
|
||||
reversed_by_name VARCHAR(64) NULL,
|
||||
reversed_at DATETIME(3) NULL,
|
||||
remark VARCHAR(500) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_outbound_dispatch_owner (tenant_id,inbound_id,container_booking_id),
|
||||
KEY idx_outbound_dispatch_order (tenant_id,order_id,updated_at),
|
||||
KEY idx_outbound_dispatch_container (container_booking_id,status,updated_at),
|
||||
CONSTRAINT fk_outbound_dispatch_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order(id),
|
||||
CONSTRAINT fk_outbound_dispatch_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id),
|
||||
CONSTRAINT chk_outbound_dispatch_status CHECK (status IN ('READY_FOR_DISPATCH','WAITING_FOR_LOADING'))
|
||||
) ENGINE=InnoDB COMMENT='三期国内出仓派车状态,不重复创建出仓单';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_outbound_dispatch_history (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
dispatch_id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||||
container_booking_id BIGINT UNSIGNED NOT NULL,
|
||||
action VARCHAR(32) NOT NULL,
|
||||
from_status VARCHAR(32) NULL,
|
||||
to_status VARCHAR(32) NOT NULL,
|
||||
remark VARCHAR(500) NULL,
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
operator_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_outbound_dispatch_history (tenant_id,inbound_id,container_booking_id,created_at,id),
|
||||
CONSTRAINT fk_outbound_dispatch_history FOREIGN KEY (dispatch_id) REFERENCES fl_outbound_dispatch(id)
|
||||
) ENGINE=InnoDB COMMENT='国内出仓追加式审计历史';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_outbound_dispatch_operation (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
dispatch_id BIGINT UNSIGNED NULL COMMENT '首次声明幂等键时允许为空',
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
operation VARCHAR(32) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash CHAR(64) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'PROCESSING',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
completed_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_outbound_dispatch_operation (operator_id,operation,idempotency_key),
|
||||
KEY idx_outbound_dispatch_operation_resource (dispatch_id,created_at),
|
||||
CONSTRAINT fk_outbound_dispatch_operation FOREIGN KEY (dispatch_id) REFERENCES fl_outbound_dispatch(id),
|
||||
CONSTRAINT chk_outbound_dispatch_operation_status CHECK (status IN ('PROCESSING','SUCCESS'))
|
||||
) ENGINE=InnoDB COMMENT='国内出仓写操作幂等声明';
|
||||
|
||||
USE baiyue_iam;
|
||||
DROP PROCEDURE IF EXISTS assert_outbound_dispatch_permission_ids_available;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE assert_outbound_dispatch_permission_ids_available()
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM iam_permission
|
||||
WHERE (id BETWEEN 5102160 AND 5102164
|
||||
AND permission_code <> CASE id
|
||||
WHEN 5102160 THEN 'platform:firstleg:outbound-dispatch:list'
|
||||
WHEN 5102161 THEN 'platform:firstleg:outbound-dispatch:view'
|
||||
WHEN 5102162 THEN 'platform:firstleg:outbound-dispatch:check'
|
||||
WHEN 5102163 THEN 'platform:firstleg:outbound-dispatch:confirm'
|
||||
WHEN 5102164 THEN 'platform:firstleg:outbound-dispatch:reverse'
|
||||
END)
|
||||
OR (permission_code LIKE 'platform:firstleg:outbound-dispatch:%'
|
||||
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT BETWEEN 5102160 AND 5102164))
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '三期出仓权限迁移失败:5102160-5102164 ID 或权限码被其他语义占用';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL assert_outbound_dispatch_permission_ids_available();
|
||||
DROP PROCEDURE IF EXISTS assert_outbound_dispatch_permission_ids_available;
|
||||
|
||||
INSERT INTO iam_permission
|
||||
(id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,
|
||||
route_path,http_method,sort_no,status,built_in,deleted)
|
||||
VALUES
|
||||
(5102160,0,'PLATFORM','MENU',5102000,'platform:firstleg:outbound-dispatch:list','国内出仓','/first-leg/outbound-dispatches','GET',95,'ACTIVE',1,0),
|
||||
(5102161,0,'PLATFORM','BUTTON',5102160,'platform:firstleg:outbound-dispatch:view','查看出仓聚合',NULL,'GET',10,'ACTIVE',1,0),
|
||||
(5102162,0,'PLATFORM','BUTTON',5102160,'platform:firstleg:outbound-dispatch:check','检查出仓门禁',NULL,'GET',20,'ACTIVE',1,0),
|
||||
(5102163,0,'PLATFORM','BUTTON',5102160,'platform:firstleg:outbound-dispatch:confirm','确认国内出仓',NULL,'POST',30,'ACTIVE',1,0),
|
||||
(5102164,0,'PLATFORM','BUTTON',5102160,'platform:firstleg:outbound-dispatch:reverse','撤销国内出仓',NULL,'POST',40,'ACTIVE',1,0)
|
||||
ON DUPLICATE KEY UPDATE tenant_id=0,app_code='PLATFORM',permission_type=VALUES(permission_type),
|
||||
permission_code=VALUES(permission_code),permission_name=VALUES(permission_name),parent_id=VALUES(parent_id),
|
||||
route_path=VALUES(route_path),http_method=VALUES(http_method),sort_no=VALUES(sort_no),status='ACTIVE',built_in=1,deleted=0;
|
||||
|
||||
INSERT IGNORE INTO iam_role_permission(role_id,permission_id,tenant_id)
|
||||
SELECT role.id,permission.id,0 FROM iam_role role JOIN iam_permission permission
|
||||
ON permission.tenant_id=0 AND permission.app_code='PLATFORM'
|
||||
AND permission.permission_code LIKE 'platform:firstleg:outbound-dispatch:%'
|
||||
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR');
|
||||
|
||||
USE baiyue_firstleg;
|
||||
@@ -0,0 +1,168 @@
|
||||
SET NAMES utf8mb4;
|
||||
USE baiyue_firstleg;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_port_schedule (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
schedule_no VARCHAR(40) NOT NULL,
|
||||
customer_id BIGINT UNSIGNED NOT NULL COMMENT '当前产品客户ID',
|
||||
ocean_booking_id BIGINT UNSIGNED NOT NULL,
|
||||
container_booking_id BIGINT UNSIGNED NULL,
|
||||
source_type VARCHAR(24) NOT NULL COMMENT 'MANUAL/CARRIER/TERMINAL/API',
|
||||
source_name VARCHAR(128) NOT NULL,
|
||||
source_ref VARCHAR(255) NOT NULL,
|
||||
vessel_name VARCHAR(128) NOT NULL,
|
||||
voyage_no VARCHAR(64) NOT NULL,
|
||||
departure_port_code VARCHAR(32) NOT NULL,
|
||||
departure_port_name VARCHAR(128) NULL,
|
||||
destination_port_code VARCHAR(32) NOT NULL,
|
||||
destination_port_name VARCHAR(128) NULL,
|
||||
etd_at DATETIME(3) NULL,
|
||||
eta_at DATETIME(3) NULL,
|
||||
booking_cutoff_at DATETIME(3) NULL COMMENT '截单时间',
|
||||
customs_cutoff_at DATETIME(3) NULL COMMENT '截关时间',
|
||||
port_cutoff_at DATETIME(3) NULL COMMENT '截港时间',
|
||||
observed_at DATETIME(3) NOT NULL COMMENT '来源数据观测时间',
|
||||
expires_at DATETIME(3) NOT NULL COMMENT '超过该时间即由API计算为过期',
|
||||
remark VARCHAR(1000) NULL,
|
||||
created_by BIGINT UNSIGNED NOT NULL,
|
||||
created_by_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_by BIGINT UNSIGNED NOT NULL,
|
||||
updated_by_name VARCHAR(64) NULL,
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_port_schedule_no (schedule_no),
|
||||
UNIQUE KEY uk_port_schedule_source (source_type,source_name,source_ref),
|
||||
KEY idx_port_schedule_customer (customer_id,observed_at,id),
|
||||
KEY idx_port_schedule_booking (ocean_booking_id,observed_at,id),
|
||||
KEY idx_port_schedule_container (container_booking_id,observed_at,id),
|
||||
KEY idx_port_schedule_expiry (expires_at,id),
|
||||
CONSTRAINT fk_port_schedule_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||||
CONSTRAINT fk_port_schedule_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id),
|
||||
CONSTRAINT chk_port_schedule_customer CHECK (customer_id > 0),
|
||||
CONSTRAINT chk_port_schedule_source_type CHECK (source_type IN ('MANUAL','CARRIER','TERMINAL','API')),
|
||||
CONSTRAINT chk_port_schedule_eta CHECK (etd_at IS NULL OR eta_at IS NULL OR eta_at >= etd_at),
|
||||
CONSTRAINT chk_port_schedule_expiry CHECK (expires_at > observed_at),
|
||||
CONSTRAINT chk_port_schedule_booking_cutoff CHECK
|
||||
(booking_cutoff_at IS NULL OR etd_at IS NULL OR booking_cutoff_at <= etd_at),
|
||||
CONSTRAINT chk_port_schedule_customs_cutoff CHECK
|
||||
(customs_cutoff_at IS NULL OR etd_at IS NULL OR customs_cutoff_at <= etd_at),
|
||||
CONSTRAINT chk_port_schedule_port_cutoff CHECK
|
||||
(port_cutoff_at IS NULL OR etd_at IS NULL OR port_cutoff_at <= etd_at)
|
||||
) ENGINE=InnoDB COMMENT='独立港务船期来源聚合,不替代订舱快照';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_port_schedule_history (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
port_schedule_id BIGINT UNSIGNED NOT NULL,
|
||||
action VARCHAR(24) NOT NULL,
|
||||
from_version INT UNSIGNED NULL,
|
||||
to_version INT UNSIGNED NOT NULL,
|
||||
reason VARCHAR(1000) NULL,
|
||||
before_snapshot JSON NULL,
|
||||
after_snapshot JSON NOT NULL,
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
operator_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_port_schedule_history (port_schedule_id,created_at,id),
|
||||
CONSTRAINT fk_port_schedule_history FOREIGN KEY (port_schedule_id) REFERENCES fl_port_schedule(id),
|
||||
CONSTRAINT chk_port_schedule_history_action CHECK (action IN ('CREATE','UPDATE'))
|
||||
) ENGINE=InnoDB COMMENT='港务船期不可变变更历史';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_port_schedule_operation (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
port_schedule_id BIGINT UNSIGNED NULL COMMENT '创建请求完成前允许为空',
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
operation VARCHAR(24) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash CHAR(64) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'PROCESSING',
|
||||
result_json JSON NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
completed_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_port_schedule_operation (operator_id,idempotency_key),
|
||||
KEY idx_port_schedule_operation_resource (port_schedule_id,created_at),
|
||||
CONSTRAINT fk_port_schedule_operation FOREIGN KEY (port_schedule_id) REFERENCES fl_port_schedule(id),
|
||||
CONSTRAINT chk_port_schedule_operation_status CHECK (status IN ('PROCESSING','SUCCESS'))
|
||||
) ENGINE=InnoDB COMMENT='港务船期写操作幂等与请求哈希';
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_port_schedule_before_insert;
|
||||
DROP TRIGGER IF EXISTS trg_port_schedule_before_update;
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER trg_port_schedule_before_insert BEFORE INSERT ON fl_port_schedule FOR EACH ROW
|
||||
BEGIN
|
||||
IF (SELECT customer_id FROM fl_ocean_booking WHERE id=NEW.ocean_booking_id) <> NEW.customer_id THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船期客户与订舱客户不一致';
|
||||
END IF;
|
||||
IF NEW.container_booking_id IS NOT NULL AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_ocean_booking_container_allocation
|
||||
WHERE ocean_booking_id=NEW.ocean_booking_id AND container_booking_id=NEW.container_booking_id
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船期柜次未分配至订舱';
|
||||
END IF;
|
||||
END$$
|
||||
CREATE TRIGGER trg_port_schedule_before_update BEFORE UPDATE ON fl_port_schedule FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.customer_id <> OLD.customer_id OR NEW.ocean_booking_id <> OLD.ocean_booking_id
|
||||
OR NOT (NEW.container_booking_id <=> OLD.container_booking_id) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船期客户、订舱和柜次归属不可变更';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_port_schedule_history_before_update;
|
||||
DROP TRIGGER IF EXISTS trg_port_schedule_history_before_delete;
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER trg_port_schedule_history_before_update BEFORE UPDATE ON fl_port_schedule_history FOR EACH ROW
|
||||
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='港务船期历史不可修改'; END$$
|
||||
CREATE TRIGGER trg_port_schedule_history_before_delete BEFORE DELETE ON fl_port_schedule_history FOR EACH ROW
|
||||
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='港务船期历史不可删除'; END$$
|
||||
DELIMITER ;
|
||||
|
||||
USE baiyue_iam;
|
||||
DROP PROCEDURE IF EXISTS assert_port_schedule_permission_ids_available;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE assert_port_schedule_permission_ids_available()
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM iam_permission
|
||||
WHERE (id BETWEEN 5102180 AND 5102183
|
||||
AND permission_code <> CASE id
|
||||
WHEN 5102180 THEN 'platform:firstleg:port-schedule:list'
|
||||
WHEN 5102181 THEN 'platform:firstleg:port-schedule:view'
|
||||
WHEN 5102182 THEN 'platform:firstleg:port-schedule:create'
|
||||
WHEN 5102183 THEN 'platform:firstleg:port-schedule:edit'
|
||||
END)
|
||||
OR (permission_code LIKE 'platform:firstleg:port-schedule:%'
|
||||
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT BETWEEN 5102180 AND 5102183))
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '三期港务船期权限迁移失败:5102180-5102183 ID 或权限码被其他语义占用';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL assert_port_schedule_permission_ids_available();
|
||||
DROP PROCEDURE IF EXISTS assert_port_schedule_permission_ids_available;
|
||||
|
||||
INSERT INTO iam_permission
|
||||
(id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,
|
||||
route_path,http_method,sort_no,status,built_in,deleted)
|
||||
VALUES
|
||||
(5102180,0,'PLATFORM','MENU',5102000,'platform:firstleg:port-schedule:list','港务船期','/first-leg/port-schedules','GET',120,'ACTIVE',1,0),
|
||||
(5102181,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule:view','查看港务船期',NULL,'GET',10,'ACTIVE',1,0),
|
||||
(5102182,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule:create','新增港务船期',NULL,'POST',20,'ACTIVE',1,0),
|
||||
(5102183,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule:edit','更新港务船期',NULL,'PUT',30,'ACTIVE',1,0)
|
||||
ON DUPLICATE KEY UPDATE tenant_id=0,app_code='PLATFORM',permission_type=VALUES(permission_type),
|
||||
permission_code=VALUES(permission_code),permission_name=VALUES(permission_name),parent_id=VALUES(parent_id),
|
||||
route_path=VALUES(route_path),http_method=VALUES(http_method),sort_no=VALUES(sort_no),
|
||||
status='ACTIVE',built_in=1,deleted=0;
|
||||
|
||||
INSERT IGNORE INTO iam_role_permission(role_id,permission_id,tenant_id)
|
||||
SELECT role.id,permission.id,0 FROM iam_role role JOIN iam_permission permission
|
||||
ON permission.tenant_id=0 AND permission.app_code='PLATFORM'
|
||||
AND permission.permission_code LIKE 'platform:firstleg:port-schedule:%'
|
||||
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR');
|
||||
|
||||
USE baiyue_firstleg;
|
||||
@@ -0,0 +1,93 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Phase 3 Customs Collaboration API
|
||||
version: 1.0.0
|
||||
description: Immutable checklist and file versions, supplement requests, frozen agent batches, receipts and history.
|
||||
paths:
|
||||
/api/platform/v1/customs/checklist-versions:
|
||||
post:
|
||||
summary: Create an immutable customs document checklist version
|
||||
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
responses: {'200': {description: Checklist version}}
|
||||
/api/platform/v1/customs/cases:
|
||||
get:
|
||||
summary: Page customs cases
|
||||
responses: {'200': {description: Page result}}
|
||||
post:
|
||||
summary: Create a customer/order/inbound/container scoped customs case
|
||||
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
responses: {'200': {description: Customs case detail}}
|
||||
/api/platform/v1/customs/cases/{caseId}:
|
||||
parameters: [{$ref: '#/components/parameters/CaseId'}]
|
||||
get:
|
||||
summary: Query complete customs detail and stable history
|
||||
responses: {'200': {description: Detail}}
|
||||
/api/platform/v1/customs/cases/{caseId}/documents:
|
||||
parameters: [{$ref: '#/components/parameters/CaseId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
post:
|
||||
summary: Reference a file validated against customer, CUSTOMS_DOCUMENT and case ID
|
||||
responses: {'200': {description: New document version}}
|
||||
/api/platform/v1/customs/cases/{caseId}/documents/upload:
|
||||
parameters: [{$ref: '#/components/parameters/CaseId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
post:
|
||||
summary: Upload a document and create a new immutable version
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
required: [itemCode, expectedVersion, file]
|
||||
properties:
|
||||
itemCode: {type: string}
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
file: {type: string, format: binary}
|
||||
responses: {'200': {description: New document version}}
|
||||
/api/platform/v1/customs/cases/{caseId}/documents/{documentId}/review:
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/CaseId'}
|
||||
- {name: documentId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {$ref: '#/components/parameters/IdempotencyKey'}
|
||||
post:
|
||||
summary: Approve a current file version or return it with a supplement task
|
||||
responses: {'200': {description: Updated detail}}
|
||||
/api/platform/v1/customs/cases/{caseId}/submission-batches:
|
||||
parameters: [{$ref: '#/components/parameters/CaseId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
post:
|
||||
summary: Submit to customs agent and freeze approved file versions in a batch snapshot
|
||||
responses: {'200': {description: Submission batch}}
|
||||
/api/platform/v1/customs/submission-batches/{batchId}/receipts:
|
||||
parameters:
|
||||
- {name: batchId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {$ref: '#/components/parameters/IdempotencyKey'}
|
||||
post:
|
||||
summary: Reference a customer-owned receipt file with explicit visibility
|
||||
responses: {'200': {description: Receipt version}}
|
||||
/api/platform/v1/customs/submission-batches/{batchId}/receipts/upload:
|
||||
post:
|
||||
summary: Upload a receipt version; RELEASED closes the customs dispatch gate
|
||||
responses: {'200': {description: Receipt version}}
|
||||
/api/merchant/v1/customs/orders/{orderId}:
|
||||
get:
|
||||
summary: Query the authenticated customer's customs case by order
|
||||
responses: {'200': {description: Customer-filtered detail}}
|
||||
/api/merchant/v1/customs/cases/{caseId}/documents/upload:
|
||||
post:
|
||||
summary: Upload or replace a checklist document within authenticated customer scope
|
||||
responses: {'200': {description: New document version}}
|
||||
/api/merchant/v1/customs/cases/{caseId}/receipts/{receiptId}/download:
|
||||
get:
|
||||
summary: Download only CUSTOMER-visible receipts from the authenticated customer's case
|
||||
responses: {'200': {description: Authorized file stream}}
|
||||
components:
|
||||
parameters:
|
||||
IdempotencyKey:
|
||||
name: Idempotency-Key
|
||||
in: header
|
||||
required: true
|
||||
schema: {type: string, minLength: 1, maxLength: 128}
|
||||
CaseId:
|
||||
name: caseId
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
@@ -0,0 +1,86 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Phase 3 Loading Evidence and Picking API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence:
|
||||
get:
|
||||
summary: List loading evidence bound to one customer inbound
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}]
|
||||
responses:
|
||||
'200': {description: Business envelope containing the stable evidence array}
|
||||
post:
|
||||
summary: Bind one real File service object to a loading evidence slot
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/UpsertEvidenceRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope; ownership, type and version failures use business error codes}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/picking/start:
|
||||
post:
|
||||
summary: Start the inbound-level PDA picking review task
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/PickingRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope containing IN_PROGRESS task}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/picking/complete:
|
||||
post:
|
||||
summary: Complete a started PDA picking task when planned and picked counts match
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/PickingRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope containing COMPLETED task}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/picking:
|
||||
get:
|
||||
summary: Read the inbound-level PDA picking task
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}]
|
||||
responses:
|
||||
'200': {description: Business envelope containing task or null}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/complete:
|
||||
post:
|
||||
summary: Complete loading only after picking and PHOTO_STEP_1..4 plus SEAL evidence are complete
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/CompleteLoadingRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope containing LOADED status}
|
||||
components:
|
||||
parameters:
|
||||
InboundId: {name: inboundId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
TenantId: {name: tenantId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
schemas:
|
||||
UpsertEvidenceRequest:
|
||||
type: object
|
||||
required: [expectedVersion, evidenceType, fileId]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
evidenceType: {type: string, enum: [PHOTO_STEP_1, PHOTO_STEP_2, PHOTO_STEP_3, PHOTO_STEP_4, SEAL]}
|
||||
fileId: {type: integer, format: int64, minimum: 1}
|
||||
fileName: {type: string, maxLength: 255, deprecated: true, description: Ignored; server uses File metadata}
|
||||
sealNo: {type: string, maxLength: 64, description: Required when evidenceType is SEAL}
|
||||
remark: {type: string, maxLength: 1000}
|
||||
PickingRequest:
|
||||
type: object
|
||||
required: [expectedVersion, expectedCount, pickedCount]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
expectedCount: {type: integer, minimum: 1}
|
||||
pickedCount: {type: integer, minimum: 0}
|
||||
remark: {type: string, maxLength: 1000}
|
||||
CompleteLoadingRequest:
|
||||
type: object
|
||||
required: [expectedVersion]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
remark: {type: string, maxLength: 1000}
|
||||
@@ -0,0 +1,69 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Phase 3 Domestic Outbound Dispatch API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/api/platform/v1/first-leg/outbound-dispatches:
|
||||
get:
|
||||
summary: Query outbound aggregates by order, inbound or container
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/OrderId'}
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {$ref: '#/components/parameters/ContainerBookingId'}
|
||||
responses:
|
||||
'200': {description: Business envelope containing matching aggregates}
|
||||
/api/platform/v1/first-leg/outbound-dispatches/detail:
|
||||
get:
|
||||
summary: Query one aggregate and its unified timeline
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/OrderId'}
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {$ref: '#/components/parameters/ContainerBookingId'}
|
||||
responses:
|
||||
'200': {description: Business envelope containing aggregate detail}
|
||||
/api/platform/v1/first-leg/outbound-dispatches/check-gate:
|
||||
get:
|
||||
summary: Evaluate the fail-closed domestic outbound gate
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/OrderId'}
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {$ref: '#/components/parameters/ContainerBookingId'}
|
||||
responses:
|
||||
'200': {description: Business envelope containing every gate item}
|
||||
/api/platform/v1/first-leg/outbound-dispatches/confirm-dispatch:
|
||||
post:
|
||||
summary: Confirm domestic dispatch and enter WAITING_FOR_LOADING
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/IdempotencyKey'}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope; gate or version failures use business error code}
|
||||
/api/platform/v1/first-leg/outbound-dispatches/reverse-dispatch:
|
||||
post:
|
||||
summary: Reverse WAITING_FOR_LOADING to LOADED
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/IdempotencyKey'}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope containing reverted aggregate}
|
||||
components:
|
||||
parameters:
|
||||
OrderId: {name: orderId, in: query, schema: {type: integer, format: int64, minimum: 1}}
|
||||
InboundId: {name: inboundId, in: query, schema: {type: integer, format: int64, minimum: 1}}
|
||||
ContainerBookingId: {name: containerBookingId, in: query, schema: {type: integer, format: int64, minimum: 1}}
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
schemas:
|
||||
ActionRequest:
|
||||
type: object
|
||||
required: [inboundId, containerBookingId, expectedVersion]
|
||||
properties:
|
||||
inboundId: {type: integer, format: int64, minimum: 1}
|
||||
containerBookingId: {type: integer, format: int64, minimum: 1}
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
remark: {type: string, maxLength: 500}
|
||||
@@ -0,0 +1,114 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Platform Port Schedule API
|
||||
version: 1.0.0
|
||||
description: Independent port and vessel schedule observations linked to ocean bookings and allocated containers.
|
||||
paths:
|
||||
/api/platform/v1/port-schedules:
|
||||
get:
|
||||
summary: Query schedules by customer, ocean booking, allocated container or expiry
|
||||
parameters:
|
||||
- {name: customerId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: oceanBookingId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: containerBookingId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: expired, in: query, schema: {type: boolean}}
|
||||
- {name: keyword, in: query, schema: {type: string, maxLength: 128}}
|
||||
- {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}}
|
||||
- {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 200, default: 20}}
|
||||
responses:
|
||||
'200':
|
||||
description: HTTP 200 business envelope containing a page of schedule views
|
||||
post:
|
||||
summary: Create a source observation linked to a real ocean booking
|
||||
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CreateRequest'}
|
||||
responses:
|
||||
'200': {description: Created detail in the standard business envelope}
|
||||
/api/platform/v1/port-schedules/{portScheduleId}:
|
||||
parameters: [{$ref: '#/components/parameters/PortScheduleId'}]
|
||||
get:
|
||||
summary: Get schedule and immutable history
|
||||
responses:
|
||||
'200': {description: Schedule detail with computed expired flag}
|
||||
put:
|
||||
summary: Update source observation with optimistic locking
|
||||
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UpdateRequest'}
|
||||
responses:
|
||||
'200': {description: Updated detail or A0409 conflict in the business envelope}
|
||||
/api/platform/v1/port-schedules/{portScheduleId}/history:
|
||||
parameters: [{$ref: '#/components/parameters/PortScheduleId'}]
|
||||
get:
|
||||
summary: Query immutable source observation history
|
||||
responses:
|
||||
'200': {description: Ordered CREATE and UPDATE history records}
|
||||
components:
|
||||
parameters:
|
||||
PortScheduleId:
|
||||
name: portScheduleId
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
IdempotencyKey:
|
||||
name: Idempotency-Key
|
||||
in: header
|
||||
required: true
|
||||
schema: {type: string, minLength: 1, maxLength: 128}
|
||||
schemas:
|
||||
ScheduleData:
|
||||
type: object
|
||||
required:
|
||||
- customerId
|
||||
- oceanBookingId
|
||||
- sourceType
|
||||
- sourceName
|
||||
- sourceRef
|
||||
- vesselName
|
||||
- voyageNo
|
||||
- departurePortCode
|
||||
- destinationPortCode
|
||||
- observedAt
|
||||
- expiresAt
|
||||
properties:
|
||||
customerId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
oceanBookingId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
containerBookingId: {type: string, nullable: true, pattern: '^[1-9][0-9]*$'}
|
||||
sourceType: {type: string, enum: [MANUAL, CARRIER, TERMINAL, API]}
|
||||
sourceName: {type: string, maxLength: 128}
|
||||
sourceRef: {type: string, maxLength: 255}
|
||||
vesselName: {type: string, maxLength: 128}
|
||||
voyageNo: {type: string, maxLength: 64}
|
||||
departurePortCode: {type: string, maxLength: 32}
|
||||
departurePortName: {type: string, nullable: true, maxLength: 128}
|
||||
destinationPortCode: {type: string, maxLength: 32}
|
||||
destinationPortName: {type: string, nullable: true, maxLength: 128}
|
||||
etdAt: {type: string, format: date-time, nullable: true}
|
||||
etaAt: {type: string, format: date-time, nullable: true}
|
||||
bookingCutoffAt: {type: string, format: date-time, nullable: true, description: Booking cutoff}
|
||||
customsCutoffAt: {type: string, format: date-time, nullable: true, description: Customs cutoff}
|
||||
portCutoffAt: {type: string, format: date-time, nullable: true, description: Port gate cutoff}
|
||||
observedAt: {type: string, format: date-time}
|
||||
expiresAt: {type: string, format: date-time}
|
||||
remark: {type: string, nullable: true, maxLength: 1000}
|
||||
CreateRequest:
|
||||
type: object
|
||||
required: [expectedVersion, data]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0, maximum: 0}
|
||||
data: {$ref: '#/components/schemas/ScheduleData'}
|
||||
reason: {type: string, nullable: true, maxLength: 1000}
|
||||
UpdateRequest:
|
||||
type: object
|
||||
required: [expectedVersion, data]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
data: {$ref: '#/components/schemas/ScheduleData'}
|
||||
reason: {type: string, nullable: true, maxLength: 1000}
|
||||
Reference in New Issue
Block a user