Files
baiyue-firstleg/firstleg/src/main/resources/db/phase3-carrier-schedule-query-schema.sql
T
2026-08-20 09:10:30 +08:00

233 lines
15 KiB
SQL

SET NAMES utf8mb4;
USE baiyue_firstleg;
CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query (
id BIGINT UNSIGNED NOT NULL COMMENT '船司官网查询任务ID',
query_no VARCHAR(40) NOT NULL COMMENT '船司官网查询任务编号',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
ocean_booking_id BIGINT UNSIGNED NOT NULL COMMENT '关联海运订舱ID',
container_booking_id BIGINT UNSIGNED NULL COMMENT '指定查询的柜次ID;整票订舱查询时为空',
container_scope_id BIGINT UNSIGNED GENERATED ALWAYS AS (COALESCE(container_booking_id,0)) STORED COMMENT '柜次唯一作用域计算值;整票订舱查询固定为0',
carrier_code VARCHAR(16) NOT NULL COMMENT '船公司编码:MATSON美森、SML森罗、ZIM以星、COSCO中远海运、EMC长荣、HEDE合德',
tracking_type VARCHAR(32) NOT NULL COMMENT '官网跟踪类型:BOOKING_NO订舱号、BILL_OF_LADING提单号、CONTAINER_NO箱号',
tracking_no VARCHAR(128) NOT NULL COMMENT '提交至船公司官网的跟踪号码',
official_url VARCHAR(500) NOT NULL COMMENT '船公司官网查询入口地址快照',
access_mode VARCHAR(32) NOT NULL COMMENT '官网访问方式:PUBLIC_FORM公开表单、HCAPTCHA人机校验、DYNAMIC_IFRAME动态页面',
status VARCHAR(24) NOT NULL COMMENT '任务状态:READY可查询、MANUAL_REQUIRED需人工处理、SUCCESS已取得有效结果',
window_started_at DATETIME(3) NOT NULL COMMENT '当前三天查询窗口起始时间',
attempt_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '当前查询窗口已执行次数;最多3次',
next_allowed_at DATETIME(3) NOT NULL COMMENT '频控规则允许的下次查询时间',
last_attempt_at DATETIME(3) NULL COMMENT '最近一次查询尝试时间',
last_success_at DATETIME(3) NULL COMMENT '最近一次取得有效结果时间',
source_updated_at DATETIME(3) NULL COMMENT '船公司官网展示的结果更新时间',
expires_at DATETIME(3) NULL COMMENT '当前最近有效结果过期时间;到期后仍保留但标记过期',
result_port_schedule_id BIGINT UNSIGNED NULL COMMENT '最近有效结果对应的船期记录ID',
last_attempt_status VARCHAR(24) NULL COMMENT '最近查询尝试状态快照',
manual_reason VARCHAR(1000) NULL COMMENT '需要人工查询或录入的原因',
last_error_code VARCHAR(64) NULL COMMENT '最近查询失败错误码',
last_error_message VARCHAR(1000) NULL COMMENT '最近查询失败错误说明',
remark VARCHAR(1000) NULL COMMENT '平台内部备注',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建操作人ID',
created_by_name VARCHAR(64) NULL COMMENT '创建操作人姓名快照',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '任务创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改操作人ID',
updated_by_name VARCHAR(64) NULL COMMENT '最后修改操作人姓名快照',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '任务最后更新时间',
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_carrier_schedule_query_no (query_no),
UNIQUE KEY uk_carrier_schedule_query_scope
(customer_id,ocean_booking_id,container_scope_id,carrier_code,tracking_type,tracking_no),
KEY idx_carrier_schedule_query_due (status,next_allowed_at,id),
KEY idx_carrier_schedule_query_customer (customer_id,updated_at,id),
KEY idx_carrier_schedule_query_expiry (expires_at,id),
CONSTRAINT fk_carrier_schedule_query_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
CONSTRAINT fk_carrier_schedule_query_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id),
CONSTRAINT fk_carrier_schedule_query_result FOREIGN KEY (result_port_schedule_id) REFERENCES fl_port_schedule(id),
CONSTRAINT chk_carrier_schedule_query_customer CHECK (customer_id > 0),
CONSTRAINT chk_carrier_schedule_query_carrier CHECK
(carrier_code IN ('MATSON','SML','ZIM','COSCO','EMC','HEDE')),
CONSTRAINT chk_carrier_schedule_query_tracking CHECK
(tracking_type IN ('BOOKING_NO','BILL_OF_LADING','CONTAINER_NO')),
CONSTRAINT chk_carrier_schedule_query_access CHECK
(access_mode IN ('PUBLIC_FORM','HCAPTCHA','DYNAMIC_IFRAME')),
CONSTRAINT chk_carrier_schedule_query_status CHECK
(status IN ('READY','MANUAL_REQUIRED','SUCCESS')),
CONSTRAINT chk_carrier_schedule_query_attempt CHECK (attempt_count BETWEEN 0 AND 3)
) ENGINE=InnoDB COMMENT='六家船公司官网查询任务与三天窗口频控';
CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query_attempt (
id BIGINT UNSIGNED NOT NULL COMMENT '船司官网查询尝试ID',
query_id BIGINT UNSIGNED NOT NULL COMMENT '所属船司官网查询任务ID',
window_attempt_no INT UNSIGNED NOT NULL COMMENT '当前三天窗口内的尝试序号;取值1至3',
carrier_code VARCHAR(16) NOT NULL COMMENT '本次尝试的船公司编码快照',
access_mode VARCHAR(32) NOT NULL COMMENT '本次尝试的官网访问方式快照',
official_url VARCHAR(500) NOT NULL COMMENT '本次尝试使用的官网地址快照',
attempt_status VARCHAR(24) NOT NULL COMMENT '尝试结果:MANUAL_REQUIRED需人工处理、SUCCESS成功、FAILED失败',
coverage_gap TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否存在自动化覆盖缺口:0否、1是',
coverage_gap_code VARCHAR(64) NULL COMMENT '自动化覆盖缺口编码',
manual_reason VARCHAR(1000) NULL COMMENT '转人工处理原因',
error_code VARCHAR(64) NULL COMMENT '查询失败错误码',
error_message VARCHAR(1000) NULL COMMENT '查询失败错误说明',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '执行本次尝试的操作人ID',
operator_name VARCHAR(64) NULL COMMENT '执行操作人姓名快照',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '尝试记录创建时间',
PRIMARY KEY (id),
KEY idx_carrier_schedule_attempt_query (query_id,created_at,id),
CONSTRAINT fk_carrier_schedule_attempt_query FOREIGN KEY (query_id) REFERENCES fl_carrier_schedule_query(id),
CONSTRAINT chk_carrier_schedule_attempt_no CHECK (window_attempt_no BETWEEN 1 AND 3),
CONSTRAINT chk_carrier_schedule_attempt_status CHECK
(attempt_status IN ('MANUAL_REQUIRED','SUCCESS','FAILED'))
) ENGINE=InnoDB COMMENT='船司官网查询尝试不可变审计';
CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query_history (
id BIGINT UNSIGNED NOT NULL COMMENT '船司查询任务历史ID',
query_id BIGINT UNSIGNED NOT NULL COMMENT '所属船司官网查询任务ID',
action VARCHAR(24) NOT NULL COMMENT '历史动作:CREATE创建、ATTEMPT执行查询、MANUAL_RESULT录入人工结果',
from_version INT UNSIGNED NULL COMMENT '变更前任务版本;创建时为空',
to_version INT UNSIGNED NOT NULL COMMENT '变更后任务版本',
reason VARCHAR(1000) NULL COMMENT '操作原因或说明',
before_snapshot JSON NULL COMMENT '变更前任务数据快照;创建时为空',
after_snapshot JSON NOT NULL COMMENT '变更后任务数据快照',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(64) NULL COMMENT '操作人姓名快照',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '历史记录创建时间',
PRIMARY KEY (id),
KEY idx_carrier_schedule_history_query (query_id,created_at,id),
CONSTRAINT fk_carrier_schedule_history_query FOREIGN KEY (query_id) REFERENCES fl_carrier_schedule_query(id),
CONSTRAINT chk_carrier_schedule_history_action CHECK (action IN ('CREATE','ATTEMPT','MANUAL_RESULT'))
) ENGINE=InnoDB COMMENT='船司查询任务不可变历史';
CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query_operation (
id BIGINT UNSIGNED NOT NULL COMMENT '船司查询写操作幂等记录ID',
query_id BIGINT UNSIGNED NULL COMMENT '关联查询任务ID;创建任务落库前允许为空',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '发起写操作的平台操作人ID',
operation VARCHAR(24) NOT NULL COMMENT '幂等写操作类型',
idempotency_key VARCHAR(128) NOT NULL COMMENT '客户端写操作幂等键',
request_hash CHAR(64) NOT NULL COMMENT '规范化请求内容SHA-256摘要;用于识别同键不同请求',
status VARCHAR(16) NOT NULL DEFAULT 'PROCESSING' COMMENT '处理状态:PROCESSING处理中、SUCCESS成功',
result_json JSON NULL COMMENT '成功结果JSON快照;供幂等重放返回',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '幂等记录创建时间',
completed_at DATETIME(3) NULL COMMENT '写操作成功完成时间',
PRIMARY KEY (id),
UNIQUE KEY uk_carrier_schedule_operation (operator_id,idempotency_key),
KEY idx_carrier_schedule_operation_query (query_id,created_at,id),
CONSTRAINT fk_carrier_schedule_operation_query FOREIGN KEY (query_id) REFERENCES fl_carrier_schedule_query(id),
CONSTRAINT chk_carrier_schedule_operation_status CHECK (status IN ('PROCESSING','SUCCESS'))
) ENGINE=InnoDB COMMENT='船司查询写操作幂等与请求摘要';
DROP TRIGGER IF EXISTS trg_carrier_schedule_query_before_insert;
DELIMITER $$
CREATE TRIGGER trg_carrier_schedule_query_before_insert BEFORE INSERT ON fl_carrier_schedule_query FOR EACH ROW
BEGIN
IF NOT EXISTS (SELECT 1 FROM fl_ocean_booking booking
WHERE booking.id=NEW.ocean_booking_id AND booking.customer_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 allocation
WHERE allocation.ocean_booking_id=NEW.ocean_booking_id
AND allocation.container_booking_id=NEW.container_booking_id
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询柜次未分配至订舱';
END IF;
END$$
DELIMITER ;
DROP TRIGGER IF EXISTS trg_carrier_schedule_attempt_before_update;
DROP TRIGGER IF EXISTS trg_carrier_schedule_attempt_before_delete;
DROP TRIGGER IF EXISTS trg_carrier_schedule_history_before_update;
DROP TRIGGER IF EXISTS trg_carrier_schedule_history_before_delete;
DELIMITER $$
CREATE TRIGGER trg_carrier_schedule_attempt_before_update BEFORE UPDATE ON fl_carrier_schedule_query_attempt
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询尝试不可修改'; END$$
CREATE TRIGGER trg_carrier_schedule_attempt_before_delete BEFORE DELETE ON fl_carrier_schedule_query_attempt
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询尝试不可删除'; END$$
CREATE TRIGGER trg_carrier_schedule_history_before_update BEFORE UPDATE ON fl_carrier_schedule_query_history
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询历史不可修改'; END$$
CREATE TRIGGER trg_carrier_schedule_history_before_delete BEFORE DELETE ON fl_carrier_schedule_query_history
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询历史不可删除'; END$$
DELIMITER ;
USE baiyue_iam;
DELETE role_permission FROM iam_role_permission role_permission
JOIN iam_permission permission ON permission.id=role_permission.permission_id
WHERE permission.tenant_id=0
AND permission.permission_code='platform:firstleg:port-schedule-query:view';
DELETE FROM iam_permission
WHERE tenant_id=0 AND permission_code='platform:firstleg:port-schedule-query:view';
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_permission_ids;
DELIMITER $$
CREATE PROCEDURE assert_carrier_schedule_query_permission_ids()
BEGIN
IF EXISTS (
SELECT 1 FROM iam_permission
WHERE (id IN (5102184,5102186,5102187,5102188)
AND permission_code <> CASE id
WHEN 5102184 THEN 'platform:firstleg:port-schedule-query:list'
WHEN 5102186 THEN 'platform:firstleg:port-schedule-query:create'
WHEN 5102187 THEN 'platform:firstleg:port-schedule-query:attempt'
WHEN 5102188 THEN 'platform:firstleg:port-schedule-query:manual'
END)
OR id=5102185
OR (permission_code LIKE 'platform:firstleg:port-schedule-query:%'
AND (tenant_id<>0 OR app_code<>'PLATFORM' OR id NOT IN (5102184,5102186,5102187,5102188)))
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT='三期船司查询权限迁移失败:5102184-5102188 ID或权限码被占用';
END IF;
END$$
DELIMITER ;
CALL assert_carrier_schedule_query_permission_ids();
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_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
(5102184,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:list','查询船司任务',NULL,'GET',40,'ACTIVE',1,0),
(5102186,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:create','创建船司任务',NULL,'POST',60,'ACTIVE',1,0),
(5102187,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:attempt','执行船司查询',NULL,'POST',70,'ACTIVE',1,0),
(5102188,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:manual','录入人工结果',NULL,'POST',80,'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-query:%'
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR');
USE baiyue_firstleg;
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_schema;
DELIMITER $$
CREATE PROCEDURE assert_carrier_schedule_query_schema()
BEGIN
IF (SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema='baiyue_firstleg' AND table_name IN
('fl_carrier_schedule_query','fl_carrier_schedule_query_attempt',
'fl_carrier_schedule_query_history','fl_carrier_schedule_query_operation')) <> 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务表未完整创建';
END IF;
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND id BETWEEN 5102184 AND 5102188
AND permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务权限未完整创建';
END IF;
IF (SELECT COUNT(*)
FROM baiyue_iam.iam_role_permission role_permission
JOIN baiyue_iam.iam_role role ON role.id=role_permission.role_id
JOIN baiyue_iam.iam_permission permission ON permission.id=role_permission.permission_id
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
AND permission.tenant_id=0
AND permission.permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 8 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务角色授权未完整创建';
END IF;
END$$
DELIMITER ;
CALL assert_carrier_schedule_query_schema();
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_schema;