10202 lines
608 KiB
SQL
10202 lines
608 KiB
SQL
-- 柏悦头程一期、二期数据库统一增量脚本
|
||
-- 固定发布入口:后续数据库变更只在本文件末尾按日期追加,不再新增部署入口脚本。
|
||
-- 适用:MySQL 8.0+;支持空库初始化和存量库幂等补齐。
|
||
-- 涉及:baiyue_auth、baiyue_iam、baiyue_order、baiyue_warehouse、baiyue_firstleg、baiyue_file、baiyue_audit、baiyue_event_bridge。
|
||
-- 安全边界:不清表、不删业务数据、不创建固定密码账号、不写入演示仓库或演示订单。权限资源属于系统配置,会幂等写入。
|
||
-- MySQL DDL 会隐式提交,跨库脚本不能整体回滚;执行账号必须具有上述数据库的 CREATE/ALTER/INDEX/INSERT/UPDATE 权限。
|
||
-- 使用 MySQL Client、Workbench、DataGrip 或 Navicat 的“运行脚本”完整执行,客户端必须支持 DELIMITER。
|
||
SET NAMES utf8mb4;
|
||
SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
|
||
-- 来源:warehouse\src\main\resources\db\phase3-pallet-location-transfer-idempotency.sql
|
||
CREATE DATABASE IF NOT EXISTS baiyue_warehouse
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_warehouse;
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_pallet_location_transfer_operation (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '调拨幂等操作ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '仓储公共资源域,固定为0',
|
||
source_service VARCHAR(40) NOT NULL COMMENT '来源服务',
|
||
source_business_id BIGINT UNSIGNED NOT NULL COMMENT '来源入仓单ID',
|
||
target_business_id BIGINT UNSIGNED NOT NULL COMMENT '目标子入仓单ID',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '调用方幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '规范化请求SHA-256摘要',
|
||
operation_status VARCHAR(20) NOT NULL DEFAULT 'PROCESSING' COMMENT 'PROCESSING处理中、COMPLETED已完成',
|
||
response_snapshot JSON NULL COMMENT '完成后的调拨结果快照,用于重复请求重放',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_pallet_location_transfer_idempotency
|
||
(tenant_id, source_service, source_business_id, target_business_id, idempotency_key),
|
||
KEY idx_pallet_location_transfer_source (tenant_id, source_service, source_business_id, operation_status)
|
||
) ENGINE=InnoDB COMMENT='托盘库位归属转移的持久化幂等操作记录';
|
||
|
||
-- 来源:warehouse\src\main\resources\db\phase3-pallet-location-release-idempotency.sql
|
||
CREATE TABLE IF NOT EXISTS wh_pallet_location_release_operation (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '精确释放幂等操作ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '仓储公共资源作用域,固定为0',
|
||
source_service VARCHAR(40) NOT NULL COMMENT '来源服务',
|
||
business_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '调用方幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '规范化请求SHA-256摘要',
|
||
operation_status VARCHAR(20) NOT NULL DEFAULT 'PROCESSING' COMMENT 'PROCESSING处理中,COMPLETED已完成',
|
||
response_snapshot JSON NULL COMMENT '完成后的精确释放结果快照,用于重复请求重放',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_pallet_location_release_idempotency
|
||
(tenant_id, source_service, business_id, idempotency_key),
|
||
KEY idx_pallet_location_release_business (tenant_id, source_service, business_id, operation_status)
|
||
) ENGINE=InnoDB COMMENT='PDA已拣满托盘库位精确释放的持久化幂等操作记录';
|
||
|
||
-- 来源:warehouse\src\main\resources\db\phase3-pallet-location-settlement-idempotency.sql
|
||
CREATE TABLE IF NOT EXISTS wh_pallet_location_settlement_operation (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA资源结算幂等操作ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '仓储公共资源作用域,固定为0',
|
||
source_service VARCHAR(40) NOT NULL COMMENT '来源服务',
|
||
source_business_id BIGINT UNSIGNED NOT NULL COMMENT '主入库单ID',
|
||
target_business_id BIGINT UNSIGNED NOT NULL COMMENT '二级子入库单ID;全拣完成时为0,仅作审计快照',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT 'PDA结算幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '规范化请求SHA-256摘要',
|
||
operation_status VARCHAR(20) NOT NULL DEFAULT 'PROCESSING' COMMENT 'PROCESSING处理中,COMPLETED已完成',
|
||
response_snapshot JSON NULL COMMENT '完成后的释放和转移组合结果快照,用于重复请求重放',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_pallet_location_settlement_idempotency
|
||
(tenant_id, source_service, source_business_id, idempotency_key),
|
||
KEY idx_pallet_location_settlement_source
|
||
(tenant_id, source_service, source_business_id, operation_status)
|
||
) ENGINE=InnoDB COMMENT='PDA拣货资源原子结算的持久化幂等操作记录';
|
||
|
||
-- 兼容已执行过旧脚本的测试库:目标子单不属于幂等键,同一来源单据和幂等键只能对应一条请求。
|
||
ALTER TABLE wh_pallet_location_settlement_operation
|
||
DROP INDEX uk_pallet_location_settlement_idempotency,
|
||
ADD UNIQUE KEY uk_pallet_location_settlement_idempotency
|
||
(tenant_id, source_service, source_business_id, idempotency_key);
|
||
|
||
SELECT VERSION() AS mysql_version, 'START' AS unified_migration_status;
|
||
|
||
-- ============================================================================
|
||
-- 来源:audit-service\src\main\resources\db\audit.sql
|
||
-- ============================================================================
|
||
CREATE DATABASE IF NOT EXISTS baiyue_audit
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_audit;
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_audit_log (
|
||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '审计日志自增ID',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '审计事件编码',
|
||
resource_type VARCHAR(64) NOT NULL COMMENT '被操作资源类型',
|
||
resource_id VARCHAR(128) DEFAULT NULL COMMENT '被操作资源ID',
|
||
description VARCHAR(255) DEFAULT NULL COMMENT '操作说明',
|
||
request_id VARCHAR(128) DEFAULT NULL COMMENT '请求ID,用于单次请求追踪',
|
||
trace_id VARCHAR(128) DEFAULT NULL COMMENT '分布式链路追踪ID',
|
||
tenant_id BIGINT DEFAULT NULL COMMENT '业务数据所属租户ID',
|
||
operator_tenant_id BIGINT DEFAULT NULL COMMENT '操作主体所属租户ID',
|
||
user_id BIGINT DEFAULT NULL COMMENT '操作用户ID',
|
||
username VARCHAR(128) DEFAULT NULL COMMENT '操作用户名快照',
|
||
app_code VARCHAR(64) DEFAULT NULL COMMENT '操作来源应用端',
|
||
client_type VARCHAR(32) DEFAULT NULL COMMENT '客户端类型',
|
||
http_method VARCHAR(16) DEFAULT NULL COMMENT 'HTTP请求方法',
|
||
request_uri VARCHAR(512) DEFAULT NULL COMMENT '请求URI',
|
||
client_ip VARCHAR(64) DEFAULT NULL COMMENT '客户端IP',
|
||
duration_ms BIGINT NOT NULL DEFAULT 0 COMMENT '请求耗时(毫秒)',
|
||
success TINYINT NOT NULL DEFAULT 0 COMMENT '操作是否成功',
|
||
error_code VARCHAR(64) DEFAULT NULL COMMENT '失败时的错误码',
|
||
error_message VARCHAR(512) DEFAULT NULL COMMENT '脱敏后的失败信息',
|
||
before_data JSON DEFAULT NULL COMMENT '变更前数据快照(已脱敏)',
|
||
after_data JSON DEFAULT NULL COMMENT '变更后数据快照(已脱敏)',
|
||
service_name VARCHAR(64) DEFAULT NULL COMMENT '产生日志的服务名',
|
||
occurred_at DATETIME(3) NOT NULL COMMENT '业务操作发生时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '审计服务落库时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_audit_tenant_time (tenant_id, occurred_at),
|
||
KEY idx_audit_event_time (event_type, occurred_at),
|
||
KEY idx_audit_user_time (user_id, occurred_at),
|
||
KEY idx_audit_request (request_id)
|
||
) ENGINE=InnoDB COMMENT='统一业务审计日志表:追加记录敏感操作的主体、租户、资源、结果及前后快照;不同于消息Outbox和消费幂等表';
|
||
|
||
-- ============================================================================
|
||
-- 来源:event-bridge-service\src\main\resources\db\event-bridge.sql
|
||
-- ============================================================================
|
||
CREATE DATABASE IF NOT EXISTS baiyue_event_bridge DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||
USE baiyue_event_bridge;
|
||
|
||
CREATE TABLE IF NOT EXISTS bridge_outbox (
|
||
event_id VARCHAR(128) NOT NULL COMMENT '事件全局标识',
|
||
idempotency_key VARCHAR(191) NOT NULL COMMENT '发送幂等键',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '事件类型',
|
||
event_version INT NOT NULL COMMENT '事件契约版本',
|
||
source_region VARCHAR(32) NOT NULL COMMENT '来源区域',
|
||
target_region VARCHAR(32) NOT NULL COMMENT '目标区域',
|
||
tenant_id VARCHAR(128) NOT NULL COMMENT '租户标识',
|
||
aggregate_type VARCHAR(64) NOT NULL COMMENT '聚合类型',
|
||
aggregate_id VARCHAR(128) NOT NULL COMMENT '聚合标识',
|
||
aggregate_version BIGINT NOT NULL COMMENT '聚合版本',
|
||
handover_version BIGINT NULL COMMENT '跨区交接版本',
|
||
trace_id VARCHAR(128) NOT NULL COMMENT '链路标识',
|
||
correlation_id VARCHAR(128) NOT NULL COMMENT '业务关联标识',
|
||
causation_id VARCHAR(128) NULL COMMENT '上游事件标识',
|
||
occurred_at DATETIME(3) NOT NULL COMMENT '业务事实发生时间',
|
||
payload_hash VARCHAR(71) NOT NULL COMMENT '规范化负载摘要',
|
||
payload_json JSON NOT NULL COMMENT '事件负载',
|
||
status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '投递状态',
|
||
retry_count INT NOT NULL DEFAULT 0 COMMENT '已重试次数',
|
||
next_retry_at DATETIME(3) NULL COMMENT '下次重试时间',
|
||
lease_owner VARCHAR(128) NULL COMMENT '租约持有者',
|
||
lease_until DATETIME(3) NULL COMMENT '租约截止时间',
|
||
last_error VARCHAR(1000) NULL COMMENT '最后错误',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
delivered_at DATETIME(3) NULL COMMENT '投递成功时间',
|
||
PRIMARY KEY (event_id),
|
||
UNIQUE KEY uk_bridge_outbox_idempotency (idempotency_key),
|
||
KEY idx_bridge_outbox_schedule (target_region, status, next_retry_at, lease_until)
|
||
) ENGINE=InnoDB COMMENT='跨区域事件待投递箱';
|
||
|
||
CREATE TABLE IF NOT EXISTS bridge_inbox (
|
||
event_id VARCHAR(128) NOT NULL COMMENT '事件全局标识',
|
||
idempotency_key VARCHAR(191) NOT NULL COMMENT '接收幂等键',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '事件类型',
|
||
event_version INT NOT NULL COMMENT '事件契约版本',
|
||
source_region VARCHAR(32) NOT NULL COMMENT '来源区域',
|
||
target_region VARCHAR(32) NOT NULL COMMENT '目标区域',
|
||
tenant_id VARCHAR(128) NOT NULL COMMENT '租户标识',
|
||
aggregate_type VARCHAR(64) NOT NULL COMMENT '聚合类型',
|
||
aggregate_id VARCHAR(128) NOT NULL COMMENT '聚合标识',
|
||
aggregate_version BIGINT NOT NULL COMMENT '聚合版本',
|
||
handover_version BIGINT NULL COMMENT '跨区交接版本',
|
||
trace_id VARCHAR(128) NOT NULL COMMENT '链路标识',
|
||
correlation_id VARCHAR(128) NOT NULL COMMENT '业务关联标识',
|
||
causation_id VARCHAR(128) NULL COMMENT '上游事件标识',
|
||
occurred_at DATETIME(3) NOT NULL COMMENT '业务事实发生时间',
|
||
payload_hash VARCHAR(71) NOT NULL COMMENT '规范化负载摘要',
|
||
payload_json JSON NOT NULL COMMENT '事件负载',
|
||
status VARCHAR(32) NOT NULL DEFAULT 'RECEIVED' COMMENT '消费状态',
|
||
lease_owner VARCHAR(128) NULL COMMENT '消费租约持有者',
|
||
lease_until DATETIME(3) NULL COMMENT '消费租约截止时间',
|
||
retry_count INT NOT NULL DEFAULT 0 COMMENT '消费重试次数',
|
||
next_retry_at DATETIME(3) NULL COMMENT '下次消费时间',
|
||
last_error VARCHAR(1000) NULL COMMENT '最后错误',
|
||
received_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '接收时间',
|
||
processed_at DATETIME(3) NULL COMMENT '消费完成时间',
|
||
PRIMARY KEY (event_id),
|
||
UNIQUE KEY uk_bridge_inbox_idempotency (idempotency_key),
|
||
KEY idx_bridge_inbox_consume (status, next_retry_at, lease_until)
|
||
) ENGINE=InnoDB COMMENT='跨区域事件已接收箱';
|
||
|
||
CREATE TABLE IF NOT EXISTS bridge_delivery_attempt (
|
||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||
event_id VARCHAR(128) NOT NULL COMMENT '事件全局标识',
|
||
source_region VARCHAR(32) NOT NULL COMMENT '来源区域',
|
||
target_region VARCHAR(32) NOT NULL COMMENT '目标区域',
|
||
attempt_no INT NOT NULL COMMENT '投递序号',
|
||
status VARCHAR(32) NOT NULL COMMENT '投递结果',
|
||
response_code VARCHAR(64) NULL COMMENT '对端响应码',
|
||
error_message VARCHAR(1000) NULL COMMENT '错误信息',
|
||
started_at DATETIME(3) NOT NULL COMMENT '开始时间',
|
||
finished_at DATETIME(3) NULL COMMENT '完成时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_bridge_attempt (event_id, attempt_no),
|
||
KEY idx_bridge_attempt_event (event_id, started_at)
|
||
) ENGINE=InnoDB COMMENT='跨区域事件投递尝试记录';
|
||
|
||
-- ============================================================================
|
||
-- 来源:auth\src\main\resources\db\auth.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
CREATE DATABASE IF NOT EXISTS baiyue_auth
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_auth;
|
||
|
||
CREATE TABLE IF NOT EXISTS auth_account (
|
||
id BIGINT NOT NULL COMMENT '统一登录账号ID(雪花ID)',
|
||
username VARCHAR(64) NOT NULL COMMENT '全局唯一登录名',
|
||
password_hash VARCHAR(255) NOT NULL COMMENT 'BCrypt密码哈希,禁止保存明文密码',
|
||
display_name VARCHAR(64) NOT NULL COMMENT '登录后展示名称',
|
||
account_status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '账号状态:ACTIVE、DISABLED、LOCKED',
|
||
failed_login_count INT NOT NULL DEFAULT 0 COMMENT '连续登录失败次数',
|
||
locked_until DATETIME(3) DEFAULT NULL COMMENT '临时锁定截止时间',
|
||
token_version INT NOT NULL DEFAULT 1 COMMENT '令牌版本,递增后可使历史令牌失效',
|
||
password_change_required TINYINT NOT NULL DEFAULT 0 COMMENT '管理员重置后是否强制下次登录改密',
|
||
default_tenant_id BIGINT NOT NULL COMMENT '默认业务租户ID',
|
||
operator_tenant_id BIGINT NOT NULL COMMENT '操作主体租户ID,平台端与商户端身份隔离',
|
||
default_membership_id BIGINT DEFAULT NULL COMMENT '默认租户成员关系ID',
|
||
default_app_code VARCHAR(20) NOT NULL COMMENT '默认应用端:MERCHANT或PLATFORM',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
last_login_at DATETIME(3) DEFAULT NULL COMMENT '最近一次成功登录时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_auth_account_username (username),
|
||
KEY idx_auth_account_status (account_status, locked_until)
|
||
) ENGINE=InnoDB COMMENT='统一登录账号表:保存平台端和商户端共用的认证凭据及账号安全状态';
|
||
|
||
-- 存量认证库补齐管理员重置密码后的强制改密标记(可重复执行)。
|
||
SET @auth_password_change_required_sql = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'auth_account'
|
||
AND column_name = 'password_change_required'),
|
||
'SELECT 1',
|
||
"ALTER TABLE auth_account ADD COLUMN password_change_required TINYINT NOT NULL DEFAULT 0 COMMENT '管理员重置后是否强制下次登录改密' AFTER token_version"
|
||
);
|
||
PREPARE auth_password_change_required_stmt FROM @auth_password_change_required_sql;
|
||
EXECUTE auth_password_change_required_stmt;
|
||
DEALLOCATE PREPARE auth_password_change_required_stmt;
|
||
|
||
CREATE TABLE IF NOT EXISTS auth_refresh_token (
|
||
id BIGINT NOT NULL COMMENT '刷新令牌记录ID(雪花ID)',
|
||
token_hash CHAR(64) NOT NULL COMMENT '刷新令牌SHA-256摘要,不保存令牌明文',
|
||
user_id BIGINT NOT NULL COMMENT '认证账号ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '令牌当前业务租户ID',
|
||
operator_tenant_id BIGINT NOT NULL COMMENT '令牌操作主体租户ID',
|
||
app_code VARCHAR(20) NOT NULL COMMENT '令牌所属应用端',
|
||
expires_at DATETIME(3) NOT NULL COMMENT '到期时间',
|
||
revoked_at DATETIME(3) DEFAULT NULL COMMENT '吊销时间,非空表示不可继续使用',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '签发时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_auth_refresh_hash (token_hash),
|
||
KEY idx_auth_refresh_user (user_id, revoked_at, expires_at),
|
||
KEY idx_auth_refresh_expire (expires_at, revoked_at)
|
||
) ENGINE=InnoDB COMMENT='刷新令牌表:保存令牌摘要、租户上下文、有效期和轮换/吊销状态';
|
||
|
||
-- 初始管理员密码必须由部署脚本生成 BCrypt 哈希后显式写入,仓库内禁止提供默认密码。
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\iam.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
-- IAM 服务独占数据库:负责租户、成员、双端角色、菜单/页面/按钮/API权限和数据范围。
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_iam
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_iam;
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_tenant (
|
||
id BIGINT NOT NULL COMMENT '租户ID(雪花ID)',
|
||
tenant_code VARCHAR(64) NOT NULL COMMENT '全局唯一租户编码',
|
||
tenant_name VARCHAR(128) NOT NULL COMMENT '租户名称',
|
||
tenant_type VARCHAR(20) NOT NULL DEFAULT 'MERCHANT' COMMENT '租户类型',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'PROFILE_REQUIRED' COMMENT '租户状态:PROFILE_REQUIRED、PENDING、ACTIVE、REJECTED、DISABLED',
|
||
legal_name VARCHAR(128) NULL COMMENT '企业法定名称',
|
||
legal_representative VARCHAR(64) NULL COMMENT '法定代表人',
|
||
unified_social_credit_code VARCHAR(32) NULL COMMENT '统一社会信用代码',
|
||
contact_name VARCHAR(64) NULL COMMENT '企业联系人',
|
||
contact_phone VARCHAR(32) NULL COMMENT '联系电话',
|
||
contact_email VARCHAR(128) NULL COMMENT '联系邮箱',
|
||
service_package VARCHAR(64) NOT NULL DEFAULT '基础版' COMMENT '客户服务套餐',
|
||
contact_wechat VARCHAR(64) NULL COMMENT '联系微信',
|
||
sales_owner_name VARCHAR(64) NULL COMMENT '销售负责人姓名',
|
||
payment_term_days SMALLINT UNSIGNED NULL COMMENT '账期天数',
|
||
enterprise_type VARCHAR(64) NULL COMMENT '企业类型',
|
||
established_date DATE NULL COMMENT '成立日期',
|
||
registered_capital DECIMAL(18,2) NULL COMMENT '注册资本',
|
||
registered_capital_currency CHAR(3) NULL COMMENT '注册资本币种 ISO 4217',
|
||
registered_address VARCHAR(500) NULL COMMENT '注册地址文本',
|
||
internal_remark VARCHAR(1000) NULL COMMENT '平台内部备注',
|
||
approved_at DATETIME(3) NULL COMMENT '审核通过时间',
|
||
approved_by BIGINT NULL COMMENT '审核人ID',
|
||
reject_reason VARCHAR(500) NULL COMMENT '驳回原因',
|
||
created_by BIGINT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_iam_tenant_code (tenant_code),
|
||
UNIQUE KEY uk_iam_tenant_credit (unified_social_credit_code),
|
||
KEY idx_iam_tenant_status (status, created_at)
|
||
) ENGINE=InnoDB COMMENT='企业租户主表:作为SaaS数据隔离边界,保存商户企业资料、审核结果和启停状态';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_tenant_review (
|
||
id BIGINT NOT NULL COMMENT '审核记录ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '被审核租户ID',
|
||
review_type VARCHAR(20) NOT NULL DEFAULT 'REGISTER' COMMENT '审核类型',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT '审核状态',
|
||
reviewer_id BIGINT NULL COMMENT '审核人ID',
|
||
review_comment VARCHAR(500) NULL COMMENT '审核意见',
|
||
reviewed_at DATETIME(3) NULL COMMENT '审核完成时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '进入审核队列时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_review_queue (status, created_at),
|
||
KEY idx_review_tenant (tenant_id)
|
||
) ENGINE=InnoDB COMMENT='租户审核记录表:保存企业注册、资料变更等审核过程和审核意见';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_tenant_address (
|
||
id BIGINT NOT NULL COMMENT '租户地址ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '所属租户ID',
|
||
address_type VARCHAR(20) NOT NULL COMMENT '地址类型',
|
||
country_code CHAR(2) NOT NULL DEFAULT 'CN' COMMENT '国家二字码',
|
||
province VARCHAR(64) NULL COMMENT '省/州',
|
||
city VARCHAR(64) NULL COMMENT '城市',
|
||
district VARCHAR(64) NULL COMMENT '区县',
|
||
detail VARCHAR(255) NOT NULL COMMENT '详细地址',
|
||
postal_code VARCHAR(16) NULL COMMENT '邮政编码',
|
||
is_default TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认地址',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
PRIMARY KEY (id),
|
||
KEY idx_address_tenant (tenant_id, address_type)
|
||
) ENGINE=InnoDB COMMENT='租户地址表:保存企业注册地址、办公地址等租户级资料';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_tenant_qualification (
|
||
id BIGINT NOT NULL COMMENT '资质记录ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '所属租户ID',
|
||
qualification_type VARCHAR(32) NOT NULL COMMENT '资质类型',
|
||
certificate_no VARCHAR(128) NULL COMMENT '证书编号',
|
||
document_url VARCHAR(500) NULL COMMENT '资质文件存储地址',
|
||
valid_from DATE NULL COMMENT '有效期开始日期',
|
||
valid_to DATE NULL COMMENT '有效期结束日期',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT '资质审核状态',
|
||
review_comment VARCHAR(500) NULL COMMENT '审核意见',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
PRIMARY KEY (id),
|
||
KEY idx_qualification_tenant (tenant_id, status)
|
||
) ENGINE=InnoDB COMMENT='租户资质表:保存企业证照、有效期、文件地址和审核状态';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_user (
|
||
id BIGINT NOT NULL COMMENT 'IAM用户资料ID',
|
||
auth_user_id BIGINT NOT NULL COMMENT '认证服务账号逻辑ID',
|
||
user_type VARCHAR(20) NOT NULL DEFAULT 'ENTERPRISE' COMMENT '用户类型',
|
||
display_name VARCHAR(64) NOT NULL COMMENT '用户展示名称',
|
||
phone VARCHAR(32) NULL COMMENT '手机号',
|
||
email VARCHAR(128) NULL COMMENT '邮箱',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '用户资料状态',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_iam_user_auth (auth_user_id),
|
||
KEY idx_iam_user_status (status)
|
||
) ENGINE=InnoDB COMMENT='IAM用户资料表:连接统一认证账号与业务身份资料,不保存密码';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_membership (
|
||
id BIGINT NOT NULL COMMENT '租户成员关系ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '所属租户ID',
|
||
user_id BIGINT NOT NULL COMMENT 'IAM用户资料ID',
|
||
member_type VARCHAR(20) NOT NULL DEFAULT 'EMPLOYEE' COMMENT '成员类型',
|
||
member_no VARCHAR(64) NULL COMMENT '租户内部成员编号',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '成员状态',
|
||
joined_at DATETIME(3) NULL COMMENT '加入租户时间',
|
||
left_at DATETIME(3) NULL COMMENT '离开租户时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_membership_tenant_user (tenant_id, user_id),
|
||
UNIQUE KEY uk_membership_tenant_no (tenant_id, member_no),
|
||
KEY idx_membership_user (user_id, status),
|
||
KEY idx_membership_tenant (tenant_id, status)
|
||
) ENGINE=InnoDB COMMENT='租户成员关系表:表示一个用户在某个企业租户中的成员身份,是分配商户端角色的主体';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_role (
|
||
id BIGINT NOT NULL COMMENT '角色ID',
|
||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '角色所属租户;0表示平台预置角色',
|
||
app_code VARCHAR(20) NOT NULL COMMENT '应用端:MERCHANT或PLATFORM',
|
||
role_code VARCHAR(64) NOT NULL COMMENT '作用域内唯一角色编码',
|
||
role_name VARCHAR(128) NOT NULL COMMENT '角色名称',
|
||
role_type VARCHAR(20) NOT NULL DEFAULT 'CUSTOM' COMMENT '角色类型:SYSTEM或CUSTOM',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '角色状态',
|
||
description VARCHAR(255) NULL COMMENT '角色用途说明',
|
||
created_by BIGINT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT NULL COMMENT '更新人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_role_scope_code (tenant_id, app_code, role_code),
|
||
KEY idx_role_tenant (tenant_id, app_code, status)
|
||
) ENGINE=InnoDB COMMENT='角色表:按租户和应用端隔离商户端、平台后台两套角色体系';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_membership_role (
|
||
membership_id BIGINT NOT NULL COMMENT '租户成员关系ID',
|
||
role_id BIGINT NOT NULL COMMENT '角色ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '冗余租户ID,用于租户隔离校验和查询',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '授权时间',
|
||
PRIMARY KEY (membership_id, role_id),
|
||
KEY idx_mr_tenant (tenant_id)
|
||
) ENGINE=InnoDB COMMENT='成员角色关联表:将租户成员分配到对应应用端的一个或多个角色';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_permission (
|
||
id BIGINT NOT NULL COMMENT '权限资源ID',
|
||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '权限所属租户;0表示平台预置权限',
|
||
app_code VARCHAR(20) NOT NULL COMMENT '所属应用端:MERCHANT或PLATFORM',
|
||
permission_type VARCHAR(20) NOT NULL COMMENT '权限类型:MENU、PAGE、BUTTON或API',
|
||
parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父权限ID,用于构造菜单和页面树',
|
||
permission_code VARCHAR(128) NOT NULL COMMENT '鉴权使用的唯一权限码',
|
||
permission_name VARCHAR(128) NOT NULL COMMENT '权限名称',
|
||
route_path VARCHAR(255) NULL COMMENT '前端路由或API路径',
|
||
http_method VARCHAR(16) NULL COMMENT 'API权限对应的HTTP方法',
|
||
sort_no INT NOT NULL DEFAULT 0 COMMENT '同级显示顺序',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '权限状态',
|
||
built_in TINYINT NOT NULL DEFAULT 0 COMMENT '是否为系统预置权限;预置权限不可通过管理接口修改',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_permission_scope_code (tenant_id, app_code, permission_code),
|
||
KEY idx_permission_parent (parent_id),
|
||
KEY idx_permission_api (http_method, route_path)
|
||
) ENGINE=InnoDB COMMENT='统一权限资源表:支持商户端和平台后台两套菜单、页面、按钮及API权限,权限最细可控制到按钮';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_role_permission (
|
||
role_id BIGINT NOT NULL COMMENT '角色ID',
|
||
permission_id BIGINT NOT NULL COMMENT '权限资源ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '冗余租户ID,用于隔离校验和查询',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '授权时间',
|
||
PRIMARY KEY (role_id, permission_id),
|
||
KEY idx_rp_tenant (tenant_id)
|
||
) ENGINE=InnoDB COMMENT='角色权限关联表:定义角色可访问的菜单、页面、按钮和API资源';
|
||
|
||
CREATE TABLE IF NOT EXISTS iam_data_scope (
|
||
id BIGINT NOT NULL COMMENT '数据范围规则ID',
|
||
role_id BIGINT NOT NULL COMMENT '角色ID',
|
||
tenant_id BIGINT NOT NULL COMMENT '规则所属租户ID',
|
||
scope_type VARCHAR(20) NOT NULL DEFAULT 'SELF' COMMENT '范围类型:SELF、DEPARTMENT、TENANT或CUSTOM',
|
||
scope_expression JSON NULL COMMENT '自定义数据范围表达式',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_scope_role (role_id)
|
||
) ENGINE=InnoDB COMMENT='角色数据范围表:在功能权限之外限制角色可查看和操作的租户内数据集合';
|
||
|
||
-- 存量数据库重复执行初始化脚本时,不重复创建审核状态索引。
|
||
SET @review_status_index_sql = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'iam_tenant_review'
|
||
AND index_name = 'idx_tenant_review_status'),
|
||
'SELECT 1',
|
||
'CREATE INDEX idx_tenant_review_status ON iam_tenant_review (tenant_id, status)'
|
||
);
|
||
PREPARE review_status_index_stmt FROM @review_status_index_sql;
|
||
EXECUTE review_status_index_stmt;
|
||
DEALLOCATE PREPARE review_status_index_stmt;
|
||
|
||
-- ============================================================================
|
||
-- 来源:order\src\main\resources\db\order.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
-- 订单服务独占数据库。其他服务只能通过 OpenAPI 或领域事件获取订单数据。
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_order
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_order;
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '订单ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
order_no VARCHAR(32) NOT NULL COMMENT '租户内唯一订单号',
|
||
inbound_code VARCHAR(40) NULL COMMENT '客户提交后生成的入仓码,草稿为空',
|
||
customer_id BIGINT UNSIGNED NULL COMMENT '客户逻辑ID',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '下单时客户名称快照',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'DRAFT' COMMENT '订单业务状态',
|
||
transport_mode VARCHAR(16) NOT NULL COMMENT '运输方式',
|
||
trade_term VARCHAR(16) NULL COMMENT '贸易条款',
|
||
destination_country_code CHAR(2) NOT NULL COMMENT '目的国家二字码',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓逻辑ID',
|
||
destination_warehouse_name VARCHAR(128) NOT NULL COMMENT '目的仓名称快照',
|
||
expected_ship_date DATE NULL COMMENT '预计发运日期',
|
||
customer_remark VARCHAR(1000) NULL COMMENT '客户可见备注',
|
||
pickup_method VARCHAR(24) NULL COMMENT '取货方式',
|
||
receiving_warehouse_id BIGINT UNSIGNED NULL COMMENT '国内收货仓ID',
|
||
receiving_warehouse_name VARCHAR(128) NULL COMMENT '国内收货仓快照',
|
||
receiving_warehouse_address VARCHAR(512) NULL COMMENT '国内收货仓完整地址快照',
|
||
receiving_time VARCHAR(128) NULL COMMENT '国内收货仓收货时间快照',
|
||
channel_code VARCHAR(64) NULL COMMENT '渠道编码',
|
||
channel_name VARCHAR(128) NULL COMMENT '渠道名称快照',
|
||
carrier_id BIGINT UNSIGNED NULL COMMENT '船司逻辑ID快照',
|
||
carrier_name VARCHAR(128) NULL COMMENT '船司名称快照',
|
||
tax_mode VARCHAR(24) NULL COMMENT '买单或退税',
|
||
pricing_tax_mode VARCHAR(16) NULL COMMENT '报价包税维度:TAX_INCLUDED或TAX_EXCLUDED;独立于tax_mode',
|
||
container_mode VARCHAR(24) NULL COMMENT '整柜或散货',
|
||
customs_inspection TINYINT(1) NULL COMMENT '是否商检',
|
||
destination_type VARCHAR(24) NULL COMMENT 'FBA/OVERSEAS/PRIVATE',
|
||
amazon_warehouse_code VARCHAR(32) NULL COMMENT '亚马逊仓库编码',
|
||
shipment_id VARCHAR(64) NULL COMMENT 'Amazon Shipment ID',
|
||
reference_id VARCHAR(64) NULL COMMENT 'Amazon Reference ID',
|
||
express_company VARCHAR(64) NULL COMMENT '履约快递公司名称快照',
|
||
express_tracking_no VARCHAR(128) NULL COMMENT '履约快递单号',
|
||
submitted_at DATETIME(3) NULL COMMENT '提交时间',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_order_tenant_no (tenant_id, order_no),
|
||
UNIQUE KEY uk_order_tenant_inbound_code (tenant_id, inbound_code),
|
||
KEY idx_order_tenant_status_time (tenant_id, status, created_at),
|
||
KEY idx_order_tenant_customer (tenant_id, customer_id),
|
||
KEY idx_order_tenant_warehouse (tenant_id, destination_warehouse_id)
|
||
) ENGINE=InnoDB COMMENT='头程订单主表:保存商户委托、目的仓、运输方式和订单当前状态';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_party (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '参与方快照ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
party_type VARCHAR(16) NOT NULL COMMENT '参与方类型:SHIPPER或CONSIGNEE',
|
||
company_name VARCHAR(128) NOT NULL COMMENT '公司名称快照',
|
||
contact_name VARCHAR(64) NOT NULL COMMENT '联系人快照',
|
||
phone VARCHAR(32) NOT NULL COMMENT '联系电话快照',
|
||
email VARCHAR(128) NULL COMMENT '联系邮箱快照',
|
||
country_code CHAR(2) NOT NULL COMMENT '国家二字码',
|
||
province VARCHAR(64) NULL COMMENT '省/州',
|
||
city VARCHAR(64) NOT NULL COMMENT '城市',
|
||
district VARCHAR(64) NULL COMMENT '区县',
|
||
address_line VARCHAR(255) NOT NULL COMMENT '详细地址',
|
||
postal_code VARCHAR(20) NULL COMMENT '邮政编码',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_party_order_type (tenant_id, order_id, party_type),
|
||
CONSTRAINT fk_party_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单收发货方快照表:固化下单时发货人和收货人信息,避免主数据变化影响历史订单';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_cargo (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '货物明细ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
item_no INT NOT NULL COMMENT '订单内明细序号',
|
||
cargo_name VARCHAR(128) NOT NULL COMMENT '货物名称',
|
||
hs_code VARCHAR(20) NULL COMMENT '海关商品编码',
|
||
package_type VARCHAR(32) NOT NULL COMMENT '包装类型',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '包装件数',
|
||
gross_weight_kg DECIMAL(18,3) NOT NULL COMMENT '毛重(千克)',
|
||
volume_cbm DECIMAL(18,4) NOT NULL COMMENT '体积(立方米)',
|
||
length_cm DECIMAL(12,2) NULL COMMENT '单件长度(厘米)',
|
||
width_cm DECIMAL(12,2) NULL COMMENT '单件宽度(厘米)',
|
||
height_cm DECIMAL(12,2) NULL COMMENT '单件高度(厘米)',
|
||
declared_value DECIMAL(18,2) NULL COMMENT '申报价值',
|
||
currency CHAR(3) NULL COMMENT '币种三字码',
|
||
dangerous_goods TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否危险品',
|
||
cargo_remark VARCHAR(500) NULL COMMENT '货物备注',
|
||
sku_no VARCHAR(64) NULL COMMENT 'SKU',
|
||
upc_no VARCHAR(64) NULL COMMENT 'UPC',
|
||
cargo_name_en VARCHAR(128) NULL COMMENT '英文品名',
|
||
customs_hs_code VARCHAR(20) NULL COMMENT '目的国清关HS CODE',
|
||
units_per_package INT UNSIGNED NULL COMMENT '每箱件数',
|
||
total_quantity INT UNSIGNED NULL COMMENT '总件数',
|
||
net_weight_kg DECIMAL(18,3) NULL COMMENT '净重kg',
|
||
unit_declared_value DECIMAL(18,2) NULL COMMENT '单件申报价值',
|
||
material VARCHAR(128) NULL COMMENT '材质',
|
||
product_usage VARCHAR(128) NULL COMMENT '用途',
|
||
model VARCHAR(64) NULL COMMENT '型号',
|
||
brand VARCHAR(64) NULL COMMENT '品牌',
|
||
product_length_cm DECIMAL(12,2) NULL COMMENT '产品长度(厘米)',
|
||
product_width_cm DECIMAL(12,2) NULL COMMENT '产品宽度(厘米)',
|
||
product_height_cm DECIMAL(12,2) NULL COMMENT '产品高度(厘米)',
|
||
shipment_id VARCHAR(64) NULL COMMENT '货物级 Amazon Shipment ID',
|
||
reference_id VARCHAR(64) NULL COMMENT '货物级 Amazon Reference ID',
|
||
product_image_file_id BIGINT UNSIGNED NULL COMMENT '文件中心产品图片逻辑ID',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_cargo_item (tenant_id, order_id, item_no),
|
||
KEY idx_cargo_order (tenant_id, order_id),
|
||
CONSTRAINT fk_cargo_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单货物明细表:记录件数、重量、体积、申报价值和危险品属性';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_internal_note (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '内部备注ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
content VARCHAR(2000) NOT NULL COMMENT '仅内部人员可见的备注内容',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
KEY idx_note_order_time (tenant_id, order_id, created_at),
|
||
CONSTRAINT fk_note_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单内部备注表:保存平台运营或履约人员的内部协作记录,不向商户客户展示';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_status_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '状态历史ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
from_status VARCHAR(24) NULL COMMENT '变更前状态,首次创建可为空',
|
||
to_status VARCHAR(24) NOT NULL COMMENT '变更后状态',
|
||
action VARCHAR(24) NOT NULL COMMENT '触发状态变化的业务动作',
|
||
reason VARCHAR(500) NULL COMMENT '变更原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
occurred_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '状态变化时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_history_order_time (tenant_id, order_id, occurred_at),
|
||
CONSTRAINT fk_history_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单状态不可变历史表:按时间记录每次状态迁移、动作、原因和操作人';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_outbox_event (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '领域事件全局ID,同时作为消费幂等键',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '事件所属租户ID',
|
||
aggregate_type VARCHAR(32) NOT NULL COMMENT '聚合类型,例如 ORDER',
|
||
aggregate_id BIGINT UNSIGNED NOT NULL COMMENT '发生变更的聚合根ID,例如订单ID',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '领域事件类型,例如 ORDER_SUBMITTED',
|
||
payload_json JSON NOT NULL COMMENT '供下游消费的事件负载快照',
|
||
publish_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '发布状态:PENDING、PUBLISHED或FAILED',
|
||
retry_count INT NOT NULL DEFAULT 0 COMMENT '投递失败后的累计重试次数',
|
||
next_retry_at DATETIME(3) NULL COMMENT '允许再次投递的最早时间',
|
||
published_at DATETIME(3) NULL COMMENT '成功发布到消息中间件的时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '事件在本地事务中的创建时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_outbox_publish (publish_status, next_retry_at, created_at)
|
||
) ENGINE=InnoDB COMMENT='订单事务Outbox表:与订单业务变更在同一本地事务写入,供后台发布器可靠投递RocketMQ,保证数据库状态与领域事件最终一致;不是普通操作日志';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_idempotency_record (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '幂等记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '租户ID',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作者ID',
|
||
operation VARCHAR(32) NOT NULL COMMENT '幂等操作类型',
|
||
resource_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '资源ID,创建草稿时为0',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '客户端幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '请求内容SHA-256摘要',
|
||
status VARCHAR(16) NOT NULL COMMENT '处理状态:PROCESSING或COMPLETED',
|
||
result_order_id BIGINT UNSIGNED NULL COMMENT '成功结果订单ID',
|
||
completed_at DATETIME(3) NULL COMMENT '处理完成时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_order_idempotency_scope (tenant_id, operator_id, operation, idempotency_key),
|
||
KEY idx_order_idempotency_created (created_at)
|
||
) ENGINE=InnoDB COMMENT='订单写接口持久化幂等记录:唯一键保证同租户同操作者并发请求只执行一次';
|
||
|
||
-- ============================================================================
|
||
-- 来源:warehouse\src\main\resources\db\warehouse.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
CREATE DATABASE IF NOT EXISTS baiyue_warehouse
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_warehouse;
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_warehouse (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库固定使用 tenant_id=0',
|
||
warehouse_code VARCHAR(50) NOT NULL COMMENT '平台内唯一仓库编码',
|
||
warehouse_name VARCHAR(120) NOT NULL COMMENT '仓库名称',
|
||
warehouse_type VARCHAR(32) NOT NULL DEFAULT 'DOMESTIC' COMMENT '公共仓库类型:DOMESTIC、FBA、OVERSEAS',
|
||
country_code CHAR(2) NOT NULL COMMENT '所在国家二字码',
|
||
timezone VARCHAR(64) NOT NULL COMMENT '仓库所在地IANA时区',
|
||
contact_name VARCHAR(64) DEFAULT NULL COMMENT '仓库联系人',
|
||
contact_phone VARCHAR(32) DEFAULT NULL COMMENT '联系电话',
|
||
contact_email VARCHAR(128) DEFAULT NULL COMMENT '联系邮箱',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '仓库状态',
|
||
remark VARCHAR(500) DEFAULT NULL COMMENT '仓库备注',
|
||
created_by BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED DEFAULT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_wh_tenant_code (tenant_id, warehouse_code, deleted),
|
||
KEY idx_wh_tenant_status (tenant_id, status, deleted),
|
||
KEY idx_wh_tenant_type_status (tenant_id, warehouse_type, status, deleted),
|
||
KEY idx_wh_country (tenant_id, country_code, deleted)
|
||
) ENGINE=InnoDB COMMENT='平台公共仓库主数据表:统一归属平台作用域 tenant_id=0';
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_warehouse_address (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '仓库地址ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库地址固定使用 tenant_id=0',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
|
||
address_type VARCHAR(20) NOT NULL DEFAULT 'OPERATING' COMMENT '地址类型,例如OPERATING或RETURN',
|
||
country_code CHAR(2) NOT NULL COMMENT '国家二字码',
|
||
state_province VARCHAR(100) DEFAULT NULL COMMENT '省或州',
|
||
city VARCHAR(100) NOT NULL COMMENT '城市',
|
||
district VARCHAR(100) DEFAULT NULL COMMENT '区县',
|
||
postal_code VARCHAR(20) DEFAULT NULL COMMENT '邮政编码',
|
||
address_line1 VARCHAR(255) NOT NULL COMMENT '主地址行',
|
||
address_line2 VARCHAR(255) DEFAULT NULL COMMENT '补充地址行',
|
||
latitude DECIMAL(10,7) DEFAULT NULL COMMENT '纬度',
|
||
longitude DECIMAL(10,7) DEFAULT NULL COMMENT '经度',
|
||
created_by BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED DEFAULT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_address_type (tenant_id, warehouse_id, address_type, deleted),
|
||
KEY idx_address_warehouse (tenant_id, warehouse_id, deleted)
|
||
) ENGINE=InnoDB COMMENT='仓库地址表:保存运营、退货等不同用途的地址及可选经纬度';
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_service_capability (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '服务能力ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库服务能力固定使用 tenant_id=0',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
|
||
capability_code VARCHAR(50) NOT NULL COMMENT '能力编码:RECEIVING、STORAGE、PICK_PACK、RETURN等',
|
||
capability_name VARCHAR(100) NOT NULL COMMENT '能力名称',
|
||
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||
cutoff_time TIME DEFAULT NULL COMMENT '当日作业截单时间',
|
||
description VARCHAR(500) DEFAULT NULL COMMENT '能力说明',
|
||
created_by BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED DEFAULT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_capability (tenant_id, warehouse_id, capability_code, deleted),
|
||
KEY idx_capability_query (tenant_id, warehouse_id, enabled, deleted)
|
||
) ENGINE=InnoDB COMMENT='仓库服务能力表:声明仓库可提供的收货、仓储、拣包、退货等能力及截单时间';
|
||
|
||
-- 不使用跨服务数据库外键;仓库聚合内的归属由服务代码通过 tenant_id 与 warehouse_id 共同校验。
|
||
|
||
-- ============================================================================
|
||
-- 来源:warehouse\src\main\resources\db\warehouse-partner.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_warehouse;
|
||
CREATE TABLE IF NOT EXISTS wh_partner (
|
||
id BIGINT UNSIGNED NOT NULL, tenant_id BIGINT UNSIGNED NOT NULL,
|
||
data_scope_id BIGINT UNSIGNED NOT NULL COMMENT '所属业务数据范围',
|
||
partner_code VARCHAR(50) NOT NULL, partner_name VARCHAR(128) NOT NULL,
|
||
partner_type VARCHAR(20) NOT NULL COMMENT 'CUSTOMER、SUPPLIER或BOTH',
|
||
contact_name VARCHAR(64) NOT NULL, contact_phone VARCHAR(32) NOT NULL,
|
||
contact_email VARCHAR(128) NULL, country_code CHAR(2) NOT NULL,
|
||
province VARCHAR(64) NULL, city VARCHAR(64) NOT NULL, district VARCHAR(64) NULL,
|
||
address_line VARCHAR(255) NOT NULL, postal_code VARCHAR(20) NULL,
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', remark VARCHAR(500) NULL,
|
||
created_by BIGINT UNSIGNED NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_by BIGINT UNSIGNED NULL,
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0, version INT NOT NULL DEFAULT 0,
|
||
PRIMARY KEY(id), UNIQUE KEY uk_wh_partner_tenant_code(tenant_id,partner_code,deleted),
|
||
KEY idx_wh_partner_tenant_status(tenant_id,status,deleted)
|
||
) ENGINE=InnoDB COMMENT='平台统一维护、供全部客户共享的往来单位主数据';
|
||
CREATE TABLE IF NOT EXISTS wh_partner_type (
|
||
partner_id BIGINT UNSIGNED NOT NULL, tenant_id BIGINT UNSIGNED NOT NULL,
|
||
data_scope_id BIGINT UNSIGNED NOT NULL, partner_type VARCHAR(32) NOT NULL,
|
||
PRIMARY KEY(partner_id,partner_type),
|
||
KEY idx_wh_partner_type_scope(tenant_id,data_scope_id,partner_type,partner_id)
|
||
) ENGINE=InnoDB COMMENT='平台共享往来单位稳定业务类型';
|
||
INSERT INTO wh_partner(id,tenant_id,data_scope_id,partner_code,partner_name,partner_type,contact_name,contact_phone,contact_email,country_code,province,city,district,address_line,postal_code,status)
|
||
VALUES(191000000000000001,0,0,'CUS-001','深圳示例贸易有限公司','CUSTOMER','张三','13800138000','contact@example.com','CN','广东省','深圳市','宝安区','航城街道示例路1号','518000','ACTIVE')
|
||
ON DUPLICATE KEY UPDATE tenant_id=VALUES(tenant_id),data_scope_id=VALUES(data_scope_id),partner_name=VALUES(partner_name),status='ACTIVE';
|
||
INSERT INTO wh_partner_type(partner_id,tenant_id,data_scope_id,partner_type)
|
||
VALUES
|
||
(191000000000000001,0,0,'CUSTOMER'),
|
||
(191000000000000001,0,0,'SHIPPER')
|
||
ON DUPLICATE KEY UPDATE tenant_id=VALUES(tenant_id),data_scope_id=VALUES(data_scope_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:warehouse\src\main\resources\db\warehouse-express-company.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_warehouse;
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_express_company (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '快递公司记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共快递公司固定使用 tenant_id=0',
|
||
company_code VARCHAR(50) NOT NULL COMMENT '平台内唯一快递公司编码',
|
||
company_name VARCHAR(128) NOT NULL COMMENT '快递公司名称',
|
||
service_phone VARCHAR(32) DEFAULT NULL COMMENT '客服电话',
|
||
website VARCHAR(255) DEFAULT NULL COMMENT '官方网站',
|
||
tracking_url_template VARCHAR(500) DEFAULT NULL COMMENT '轨迹查询地址模板,可包含{trackingNo}',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT '快递公司状态',
|
||
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||
created_by BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED DEFAULT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_wh_express_tenant_code (tenant_id, company_code, deleted),
|
||
UNIQUE KEY uk_wh_express_tenant_name (tenant_id, company_name, deleted),
|
||
KEY idx_wh_express_tenant_status (tenant_id, status, deleted)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='平台公共快递公司主数据,固定归属 tenant_id=0';
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\firstleg.sql
|
||
-- ============================================================================
|
||
-- 头程履约服务独占数据库。
|
||
CREATE DATABASE IF NOT EXISTS baiyue_firstleg
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_firstleg;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_fulfillment_task (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '履约任务ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务中的订单逻辑ID,不建立跨库外键',
|
||
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
|
||
order_snapshot_json JSON NOT NULL COMMENT '创建履约任务时所需的订单业务快照',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '履约任务当前状态',
|
||
assignee_id BIGINT UNSIGNED NULL COMMENT '当前负责人ID',
|
||
assignee_name VARCHAR(64) NULL COMMENT '当前负责人名称快照',
|
||
assigned_at DATETIME(3) NULL COMMENT '最近指派时间',
|
||
started_at DATETIME(3) NULL COMMENT '开始履约时间',
|
||
completed_at DATETIME(3) NULL COMMENT '履约完成时间',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人或系统主体ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_task_tenant_order (tenant_id, order_id),
|
||
KEY idx_task_tenant_created (tenant_id, created_at),
|
||
KEY idx_task_assignee_status (tenant_id, assignee_id, status, created_at)
|
||
) ENGINE=InnoDB COMMENT='头程履约任务表:接收订单事件后形成独立履约任务,管理指派、执行和完成状态';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_assignment_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '指派历史ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
task_id BIGINT UNSIGNED NOT NULL COMMENT '所属履约任务ID',
|
||
previous_assignee_id BIGINT UNSIGNED NULL COMMENT '原负责人ID',
|
||
assignee_id BIGINT UNSIGNED NOT NULL COMMENT '新负责人ID',
|
||
assignee_name VARCHAR(64) NOT NULL COMMENT '新负责人名称快照',
|
||
reason VARCHAR(500) NULL COMMENT '指派或改派原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '执行指派的操作人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '指派发生时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_assignment_task_time (tenant_id, task_id, created_at),
|
||
CONSTRAINT fk_assignment_task FOREIGN KEY (task_id) REFERENCES fl_fulfillment_task (id)
|
||
) ENGINE=InnoDB COMMENT='头程任务指派历史表:不可变记录每次负责人变更及原因,用于追踪和审计';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_consumed_event (
|
||
event_id BIGINT UNSIGNED NOT NULL COMMENT '已消费领域事件ID,与生产端Outbox事件ID一致',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '已消费事件类型',
|
||
consumed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '首次成功消费时间',
|
||
PRIMARY KEY (event_id)
|
||
) ENGINE=InnoDB COMMENT='头程消息消费幂等表:记录已处理事件并按事件ID去重,防止RocketMQ重复投递造成重复建单;不是消息发布表';
|
||
|
||
-- ============================================================================
|
||
-- 来源:file\src\main\resources\db\file.sql
|
||
-- ============================================================================
|
||
-- 文件中心独占数据库,其他服务只保存文件逻辑ID。
|
||
CREATE DATABASE IF NOT EXISTS baiyue_file
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_file;
|
||
|
||
CREATE TABLE IF NOT EXISTS file_object (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '文件ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
business_type VARCHAR(32) NULL COMMENT '关联业务类型',
|
||
business_id VARCHAR(64) NULL COMMENT '关联业务逻辑ID',
|
||
original_name VARCHAR(255) NOT NULL COMMENT '原始文件名',
|
||
object_key VARCHAR(512) NOT NULL COMMENT '对象存储键',
|
||
storage_provider VARCHAR(32) NOT NULL COMMENT '存储提供方',
|
||
bucket_name VARCHAR(128) NOT NULL COMMENT '存储桶名称',
|
||
content_type VARCHAR(128) NOT NULL COMMENT '文件内容类型',
|
||
size_bytes BIGINT UNSIGNED NOT NULL COMMENT '文件字节数',
|
||
sha256 CHAR(64) NULL COMMENT '文件SHA-256摘要',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'ACTIVE' COMMENT '文件状态',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_file_tenant_object (tenant_id, object_key),
|
||
KEY idx_file_tenant_business (tenant_id, business_type, business_id),
|
||
KEY idx_file_tenant_created (tenant_id, created_at)
|
||
) ENGINE=InnoDB COMMENT='文件元数据表';
|
||
|
||
CREATE TABLE IF NOT EXISTS file_download_token (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '下载令牌ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
file_id BIGINT UNSIGNED NOT NULL COMMENT '文件逻辑ID',
|
||
token_hash CHAR(64) NOT NULL COMMENT '下载令牌SHA-256摘要',
|
||
expires_at DATETIME(3) NOT NULL COMMENT '过期时间',
|
||
max_downloads INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '最大下载次数',
|
||
download_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已下载次数',
|
||
revoked_at DATETIME(3) NULL COMMENT '撤销时间',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_download_token_hash (token_hash),
|
||
KEY idx_download_file (tenant_id, file_id),
|
||
KEY idx_download_expiry (expires_at)
|
||
) ENGINE=InnoDB COMMENT='临时下载授权令牌表';
|
||
|
||
CREATE TABLE IF NOT EXISTS file_download_log (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '下载日志ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
file_id BIGINT UNSIGNED NOT NULL COMMENT '文件逻辑ID',
|
||
user_id BIGINT UNSIGNED NULL COMMENT '下载用户ID',
|
||
request_id VARCHAR(64) NULL COMMENT '请求ID',
|
||
client_ip VARCHAR(64) NULL COMMENT '客户端IP',
|
||
result VARCHAR(16) NOT NULL COMMENT '下载结果',
|
||
failure_reason VARCHAR(500) NULL COMMENT '失败原因',
|
||
downloaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '下载时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_download_log_file (tenant_id, file_id, downloaded_at),
|
||
KEY idx_download_log_user (tenant_id, user_id, downloaded_at)
|
||
) ENGINE=InnoDB COMMENT='文件下载审计日志表';
|
||
|
||
CREATE TABLE IF NOT EXISTS file_export_task (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '导出任务ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '申请人所属租户ID,平台任务为0',
|
||
app_code VARCHAR(24) NOT NULL COMMENT '申请端:PLATFORM或MERCHANT',
|
||
task_no VARCHAR(40) NOT NULL COMMENT '导出任务编号',
|
||
export_type VARCHAR(64) NOT NULL COMMENT '导出类型',
|
||
format VARCHAR(16) NOT NULL DEFAULT 'XLSX' COMMENT '导出格式',
|
||
task_name VARCHAR(128) NOT NULL COMMENT '任务名称',
|
||
parameters_json JSON NOT NULL COMMENT '筛选条件快照',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/RUNNING/SUCCEEDED/FAILED/EXPIRED',
|
||
total_rows BIGINT UNSIGNED NULL COMMENT '预计总行数',
|
||
processed_rows BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已处理行数',
|
||
file_name VARCHAR(255) NULL COMMENT '下载文件名',
|
||
object_key VARCHAR(512) NULL COMMENT '存储对象键',
|
||
content_type VARCHAR(128) NULL COMMENT '文件类型',
|
||
size_bytes BIGINT UNSIGNED NULL COMMENT '文件字节数',
|
||
failure_message VARCHAR(1000) NULL COMMENT '失败原因',
|
||
dispatch_version INT NOT NULL DEFAULT 1 COMMENT '派发版本,仅执行重试时递增',
|
||
attempt_count INT NOT NULL DEFAULT 0 COMMENT '已执行尝试次数',
|
||
max_attempts INT NOT NULL DEFAULT 3 COMMENT '自动重试上限',
|
||
next_retry_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '允许执行的最早时间',
|
||
lease_owner VARCHAR(128) NULL COMMENT '当前执行实例标识',
|
||
lease_until DATETIME(3) NULL COMMENT '执行租约截止时间',
|
||
last_heartbeat_at DATETIME(3) NULL COMMENT '最后心跳时间',
|
||
execution_deadline_at DATETIME(3) NULL COMMENT '本次执行截止时间',
|
||
requested_by BIGINT UNSIGNED NOT NULL COMMENT '申请用户ID',
|
||
requested_username VARCHAR(128) NULL COMMENT '申请账号快照',
|
||
requested_at DATETIME(3) NOT NULL COMMENT '申请时间',
|
||
started_at DATETIME(3) NULL COMMENT '开始时间',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
expires_at DATETIME(3) NULL COMMENT '文件过期时间',
|
||
created_by BIGINT UNSIGNED NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_file_export_task_no (task_no),
|
||
KEY idx_file_export_owner (tenant_id, app_code, requested_by, requested_at),
|
||
KEY idx_file_export_status (status, requested_at),
|
||
KEY idx_file_export_dispatch (status, next_retry_at, id),
|
||
KEY idx_file_export_lease (status, lease_until),
|
||
KEY idx_file_export_expiry (status, expires_at)
|
||
) ENGINE=InnoDB COMMENT='公共异步导出任务表';
|
||
|
||
CREATE TABLE IF NOT EXISTS file_export_task_guard (
|
||
guard_key VARCHAR(64) NOT NULL COMMENT '任务创建串行化锁键',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||
PRIMARY KEY (guard_key)
|
||
) ENGINE=InnoDB COMMENT='导出任务创建并发保护表:低频创建入口使用单行锁保证重复提交及活动上限';
|
||
|
||
INSERT IGNORE INTO file_export_task_guard (guard_key) VALUES ('GLOBAL_CREATE');
|
||
|
||
CREATE TABLE IF NOT EXISTS file_export_outbox (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'Outbox记录ID',
|
||
event_id VARCHAR(64) NOT NULL COMMENT '投递意图唯一标识',
|
||
task_id BIGINT UNSIGNED NOT NULL COMMENT '关联导出任务ID',
|
||
dispatch_version INT NOT NULL COMMENT '任务派发版本',
|
||
topic VARCHAR(128) NOT NULL COMMENT '目标Topic',
|
||
tag VARCHAR(64) NOT NULL COMMENT '消息Tag',
|
||
message_key VARCHAR(64) NOT NULL COMMENT '消息Key',
|
||
payload_json JSON NOT NULL COMMENT '命令消息体',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/CLAIMED/SENT/QUARANTINED',
|
||
attempt_count INT NOT NULL DEFAULT 0 COMMENT '累计发送尝试次数',
|
||
next_retry_at DATETIME(3) NOT NULL COMMENT '允许发送的最早时间',
|
||
lease_owner VARCHAR(128) NULL COMMENT '领取实例标识',
|
||
lease_until DATETIME(3) NULL COMMENT '领取租约截止时间',
|
||
sent_at DATETIME(3) NULL COMMENT '发送成功时间',
|
||
last_error VARCHAR(1000) NULL COMMENT '最后发送错误',
|
||
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_file_export_outbox_event (event_id),
|
||
UNIQUE KEY uk_file_export_outbox_task_version (task_id, dispatch_version),
|
||
KEY idx_file_export_outbox_publish (status, next_retry_at, id),
|
||
KEY idx_file_export_outbox_task (task_id, dispatch_version, status, sent_at)
|
||
) ENGINE=InnoDB COMMENT='导出命令事务Outbox表';
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase1-admin-rbac.sql
|
||
-- ============================================================================
|
||
-- Phase-one administrator identities and RBAC seed data.
|
||
-- Scope is intentionally limited to first-level menus, second-level menus and buttons.
|
||
-- PAGE and API permission resources are not part of phase one.
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
-- Keep the role table aligned with the shared BaseEntity audit fields.
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'iam_role' AND column_name = 'created_by'),
|
||
'SELECT 1',
|
||
'ALTER TABLE iam_role ADD COLUMN created_by BIGINT NULL AFTER description'
|
||
);
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'iam_role' AND column_name = 'updated_by'),
|
||
'SELECT 1',
|
||
'ALTER TABLE iam_role ADD COLUMN updated_by BIGINT NULL AFTER created_at'
|
||
);
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
INSERT INTO iam_tenant (
|
||
id, tenant_code, tenant_name, tenant_type, status, legal_name,
|
||
unified_social_credit_code, contact_name, contact_phone, contact_email,
|
||
approved_at, approved_by, created_by, deleted, version
|
||
) VALUES
|
||
(0, 'PLATFORM', '虫虫出海平台', 'PLATFORM', 'ACTIVE', '虫虫出海平台',
|
||
NULL, '平台管理员', NULL, NULL, NOW(3), 1000001, 1000001, 0, 0),
|
||
(10001, 'PHASE1_DEMO', '一期演示企业', 'MERCHANT', 'ACTIVE', '一期演示企业有限公司',
|
||
'91310000PHASE10001', '企业管理员', '13800000000', 'merchant@example.com',
|
||
NOW(3), 1000001, 1000001, 0, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
tenant_name = VALUES(tenant_name), tenant_type = VALUES(tenant_type),
|
||
status = 'ACTIVE', legal_name = VALUES(legal_name),
|
||
contact_name = VALUES(contact_name), contact_phone = VALUES(contact_phone),
|
||
contact_email = VALUES(contact_email), approved_at = COALESCE(approved_at, NOW(3)),
|
||
approved_by = VALUES(approved_by), deleted = 0;
|
||
|
||
INSERT INTO iam_user (
|
||
id, auth_user_id, user_type, display_name, phone, email, status, deleted, version
|
||
) VALUES
|
||
(2000001, 1000001, 'INTERNAL', '平台管理员', NULL, NULL, 'ACTIVE', 0, 0),
|
||
(2000002, 1000002, 'ENTERPRISE', '一期演示企业管理员', '13800000000',
|
||
'merchant@example.com', 'ACTIVE', 0, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
display_name = VALUES(display_name), phone = VALUES(phone), email = VALUES(email),
|
||
status = 'ACTIVE', deleted = 0;
|
||
|
||
INSERT INTO iam_membership (
|
||
id, tenant_id, user_id, member_type, member_no, status, joined_at, deleted, version
|
||
) VALUES
|
||
(3000001, 0, 2000001, 'EMPLOYEE', 'PLATFORM-ADMIN-001', 'ACTIVE', NOW(3), 0, 0),
|
||
(3000002, 10001, 2000002, 'OWNER', 'MERCHANT-OWNER-001', 'ACTIVE', NOW(3), 0, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
member_type = VALUES(member_type), member_no = VALUES(member_no),
|
||
status = 'ACTIVE', joined_at = COALESCE(joined_at, NOW(3)), left_at = NULL, deleted = 0;
|
||
|
||
INSERT INTO iam_role (
|
||
id, tenant_id, app_code, role_code, role_name, role_type, status, description, deleted, version
|
||
) VALUES
|
||
(4000001, 0, 'PLATFORM', 'PLATFORM_ADMIN', '平台超级管理员', 'SYSTEM', 'ACTIVE', '一期平台全部菜单和按钮', 0, 0),
|
||
(4000002, 0, 'PLATFORM', 'CUSTOMER_REVIEWER', '企业审核员', 'SYSTEM', 'ACTIVE', '企业资料与入驻审核', 0, 0),
|
||
(4000003, 0, 'PLATFORM', 'ORDER_OPERATOR', '平台订单运营', 'SYSTEM', 'ACTIVE', '头程订单审核与履约处理', 0, 0),
|
||
(4000101, 10001, 'MERCHANT', 'MERCHANT_OWNER', '企业管理员', 'SYSTEM', 'ACTIVE', '本企业一期全部菜单和按钮', 0, 0),
|
||
(4000102, 10001, 'MERCHANT', 'MERCHANT_ORDER_MANAGER', '企业订单管理员', 'SYSTEM', 'ACTIVE', '本企业头程订单操作', 0, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
role_name = VALUES(role_name), role_type = VALUES(role_type), status = 'ACTIVE',
|
||
description = VALUES(description), deleted = 0;
|
||
|
||
-- Platform: level-one menus.
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, deleted)
|
||
VALUES
|
||
(5100000, 0, 'PLATFORM', 'MENU', 0, 'platform:customer', '客户管理', '/customers', NULL, 10, 'ACTIVE', 0),
|
||
(5101000, 0, 'PLATFORM', 'MENU', 0, 'platform:system', '系统管理', '/system', NULL, 20, 'ACTIVE', 0),
|
||
(5102000, 0, 'PLATFORM', 'MENU', 0, 'platform:order', '头程订单', '/first-leg-orders', NULL, 30, 'ACTIVE', 0),
|
||
(5103000, 0, 'PLATFORM', 'MENU', 0, 'platform:master-data', '基础资料', '/master-data', NULL, 40, 'ACTIVE', 0),
|
||
(5104000, 0, 'PLATFORM', 'MENU', 0, 'platform:audit', '审计管理', '/audit', NULL, 50, 'ACTIVE', 0),
|
||
-- Platform: level-two menus.
|
||
(5100010, 0, 'PLATFORM', 'MENU', 5100000, 'platform:customer:list', '企业客户', '/customers/list', NULL, 10, 'ACTIVE', 0),
|
||
(5101010, 0, 'PLATFORM', 'MENU', 5101000, 'platform:user:list', '内部用户', '/system/users', NULL, 10, 'ACTIVE', 0),
|
||
(5101020, 0, 'PLATFORM', 'MENU', 5101000, 'platform:role:list', '角色管理', '/system/roles', NULL, 20, 'ACTIVE', 0),
|
||
(5101030, 0, 'PLATFORM', 'MENU', 5101000, 'platform:permission:list', '菜单按钮', '/system/permissions', NULL, 30, 'ACTIVE', 0),
|
||
(5102010, 0, 'PLATFORM', 'MENU', 5102000, 'platform:order:list', '订单列表', '/first-leg-orders/list', NULL, 10, 'ACTIVE', 0),
|
||
(5102020, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:task:list', '履约任务', '/first-leg-tasks/list', NULL, 20, 'ACTIVE', 0),
|
||
(5103010, 0, 'PLATFORM', 'MENU', 5103000, 'platform:warehouse:list', '国内仓/FBA仓', '/master-data/warehouses', NULL, 10, 'ACTIVE', 0),
|
||
(5103020, 0, 'PLATFORM', 'MENU', 5103000, 'platform:express:list', '快递公司', '/master-data/express', NULL, 20, 'ACTIVE', 0),
|
||
(5103030, 0, 'PLATFORM', 'MENU', 5103000, 'platform:counterparty:list', '往来单位', '/master-data/counterparties', NULL, 30, 'ACTIVE', 0),
|
||
(5103040, 0, 'PLATFORM', 'MENU', 5103000, 'platform:download:list', '下载中心', '/downloads', NULL, 40, 'ACTIVE', 0),
|
||
(5104010, 0, 'PLATFORM', 'MENU', 5104000, 'platform:audit:list', '操作日志', '/audit/logs', NULL, 10, 'ACTIVE', 0),
|
||
-- Platform: buttons.
|
||
(5100020, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:review', '客户审核', NULL, NULL, 50, 'ACTIVE', 0),
|
||
(5100021, 0, 'PLATFORM', 'BUTTON', 5100020, 'platform:customer:approve', '审核通过', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5100022, 0, 'PLATFORM', 'BUTTON', 5100020, 'platform:customer:reject', '审核驳回', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5100015, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:export', '导出企业客户', NULL, NULL, 40, 'ACTIVE', 0),
|
||
(5103041, 0, 'PLATFORM', 'BUTTON', 5103040, 'platform:download:all', '查看全部下载任务', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5101011, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:create', '新增用户', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5101012, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:edit', '编辑用户', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5101013, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:status', '启停用户', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5101014, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:reset-password', '重置密码', NULL, NULL, 40, 'ACTIVE', 0),
|
||
(5101015, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:grant-role', '分配用户角色', NULL, NULL, 50, 'ACTIVE', 0),
|
||
(5101021, 0, 'PLATFORM', 'BUTTON', 5101020, 'platform:role:create', '新增角色', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5101022, 0, 'PLATFORM', 'BUTTON', 5101020, 'platform:role:grant', '角色授权', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5101031, 0, 'PLATFORM', 'BUTTON', 5101030, 'platform:permission:create', '新增菜单按钮', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5102011, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:approve', '订单审核通过', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5102012, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:reject', '订单驳回', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5102013, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:accept', '订单受理', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5102014, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:process', '开始处理', NULL, NULL, 40, 'ACTIVE', 0),
|
||
(5102015, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:complete', '完成订单', NULL, NULL, 50, 'ACTIVE', 0),
|
||
(5102016, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:cancel', '取消订单', NULL, NULL, 60, 'ACTIVE', 0),
|
||
(5102017, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:note', '内部备注', NULL, NULL, 70, 'ACTIVE', 0),
|
||
(5102018, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:fulfillment', '登记履约快递', NULL, NULL, 80, 'ACTIVE', 0),
|
||
(5102021, 0, 'PLATFORM', 'BUTTON', 5102020, 'platform:firstleg:task:view', '查看履约任务', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5102022, 0, 'PLATFORM', 'BUTTON', 5102020, 'platform:firstleg:assignment:history', '查看分配历史', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5102023, 0, 'PLATFORM', 'BUTTON', 5102020, 'platform:firstleg:assignment:assign', '分配履约任务', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5103011, 0, 'PLATFORM', 'BUTTON', 5103010, 'platform:warehouse:create', '新增仓库', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5103012, 0, 'PLATFORM', 'BUTTON', 5103010, 'platform:warehouse:edit', '编辑仓库', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5103013, 0, 'PLATFORM', 'BUTTON', 5103010, 'platform:warehouse:status', '启用停用', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5103021, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:create', '新增快递公司', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5103022, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:edit', '编辑快递公司', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5103023, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:status', '启用停用快递公司', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5103024, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:import', '批量导入快递公司', NULL, NULL, 40, 'ACTIVE', 0),
|
||
(5103031, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:create', '新增往来单位', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5103032, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:edit', '编辑往来单位', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5103033, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:status', '启用停用往来单位', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5103034, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:import', '批量导入往来单位', NULL, NULL, 40, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = NULL, sort_no = VALUES(sort_no), status = 'ACTIVE', deleted = 0;
|
||
|
||
-- 平台代客创建、编辑草稿权限;可重复执行并纳入已有平台管理员和订单运营角色授权。
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, deleted)
|
||
VALUES
|
||
(5102019, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:create', '平台代客创建订单', NULL, NULL, 90, 'ACTIVE', 0),
|
||
(5102024, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:edit', '平台编辑订单草稿', NULL, NULL, 100, 'ACTIVE', 0),
|
||
(5102025, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:submit', '平台代客提交订单', NULL, NULL, 110, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id), status = 'ACTIVE', deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0 FROM iam_permission
|
||
WHERE id IN (5102019, 5102024, 5102025)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000003, id, 0 FROM iam_permission
|
||
WHERE id IN (5102019, 5102024, 5102025)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- Merchant: level-one menus, level-two menus and buttons.
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, route_path, http_method, sort_no, status, deleted)
|
||
VALUES
|
||
(5200000, 10001, 'MERCHANT', 'MENU', 0, 'merchant:enterprise', '企业管理', '/enterprise', NULL, 10, 'ACTIVE', 0),
|
||
(5201000, 10001, 'MERCHANT', 'MENU', 0, 'merchant:order', '头程订单', '/first-leg-orders', NULL, 20, 'ACTIVE', 0),
|
||
(5202000, 10001, 'MERCHANT', 'MENU', 0, 'merchant:master-data', '基础资料', '/master-data', NULL, 30, 'ACTIVE', 0),
|
||
(5200010, 10001, 'MERCHANT', 'MENU', 5200000, 'merchant:profile:view', '企业资料', '/enterprise/profile', NULL, 10, 'ACTIVE', 0),
|
||
(5200020, 10001, 'MERCHANT', 'MENU', 5200000, 'merchant:member:list', '企业成员', '/enterprise/members', NULL, 20, 'ACTIVE', 0),
|
||
(5200030, 10001, 'MERCHANT', 'MENU', 5200000, 'merchant:role:list', '角色管理', '/enterprise/roles', NULL, 30, 'ACTIVE', 0),
|
||
(5201010, 10001, 'MERCHANT', 'MENU', 5201000, 'merchant:order:list', '我的订单', '/first-leg-orders/list', NULL, 10, 'ACTIVE', 0),
|
||
(5201020, 10001, 'MERCHANT', 'MENU', 5201000, 'merchant:order:create-page', '新建订单', '/first-leg-orders/create', NULL, 20, 'ACTIVE', 0),
|
||
(5202010, 10001, 'MERCHANT', 'MENU', 5202000, 'merchant:warehouse:list', '收货仓选择', '/master-data/warehouses', NULL, 10, 'ACTIVE', 0),
|
||
(5202020, 10001, 'MERCHANT', 'MENU', 5202000, 'merchant:counterparty:list', '往来单位', '/master-data/counterparties', NULL, 20, 'ACTIVE', 0),
|
||
(5200011, 10001, 'MERCHANT', 'BUTTON', 5200010, 'merchant:profile:edit', '编辑企业资料', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5200012, 10001, 'MERCHANT', 'BUTTON', 5200010, 'merchant:qualification:upload', '上传企业资质', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5200021, 10001, 'MERCHANT', 'BUTTON', 5200020, 'merchant:member:create', '新增成员', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5200022, 10001, 'MERCHANT', 'BUTTON', 5200020, 'merchant:member:grant-role', '分配角色', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5200031, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:create', '新增角色', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5200032, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:grant', '角色授权', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5201011, 10001, 'MERCHANT', 'BUTTON', 5201010, 'merchant:order:view', '查看订单', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5201012, 10001, 'MERCHANT', 'BUTTON', 5201010, 'merchant:order:cancel', '取消订单', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5201021, 10001, 'MERCHANT', 'BUTTON', 5201020, 'merchant:order:create', '创建草稿', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5201022, 10001, 'MERCHANT', 'BUTTON', 5201020, 'merchant:order:edit', '编辑草稿', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5201023, 10001, 'MERCHANT', 'BUTTON', 5201020, 'merchant:order:submit', '提交订单', NULL, NULL, 30, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = NULL, sort_no = VALUES(sort_no), status = 'ACTIVE', deleted = 0;
|
||
|
||
INSERT INTO iam_membership_role (membership_id, role_id, tenant_id) VALUES
|
||
(3000001, 4000001, 0),
|
||
(3000002, 4000101, 10001)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- Full administrator roles receive all phase-one permissions in their application scope.
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0 FROM iam_permission
|
||
WHERE app_code = 'PLATFORM' AND tenant_id = 0 AND permission_type IN ('MENU', 'BUTTON')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000101, id, 10001 FROM iam_permission
|
||
WHERE app_code = 'MERCHANT' AND tenant_id = 10001 AND permission_type IN ('MENU', 'BUTTON')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- Limited platform and merchant operational roles.
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000002, id, 0 FROM iam_permission
|
||
WHERE permission_code IN (
|
||
'platform:customer', 'platform:customer:list', 'platform:customer:review',
|
||
'platform:customer:approve', 'platform:customer:reject', 'platform:customer:export',
|
||
'platform:master-data', 'platform:download:list'
|
||
)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000003, id, 0 FROM iam_permission
|
||
WHERE permission_code LIKE 'platform:order%' OR permission_code LIKE 'platform:firstleg%'
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000102, id, 10001 FROM iam_permission
|
||
WHERE permission_code LIKE 'merchant:order%' OR permission_code IN ('merchant:master-data', 'merchant:warehouse:list')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_data_scope (id, role_id, tenant_id, scope_type, scope_expression) VALUES
|
||
(6000001, 4000001, 0, 'TENANT', NULL),
|
||
(6000002, 4000002, 0, 'TENANT', NULL),
|
||
(6000003, 4000003, 0, 'TENANT', NULL),
|
||
(6000101, 4000101, 10001, 'TENANT', NULL),
|
||
(6000102, 4000102, 10001, 'TENANT', NULL)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id), scope_type = VALUES(scope_type), scope_expression = NULL;
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase1-onboarding.sql
|
||
-- ============================================================================
|
||
-- Phase-one merchant onboarding state migration.
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
ALTER TABLE iam_tenant MODIFY COLUMN status VARCHAR(20) NOT NULL DEFAULT 'PROFILE_REQUIRED'
|
||
COMMENT 'PROFILE_REQUIRED, PENDING, ACTIVE, REJECTED or DISABLED';
|
||
DROP PROCEDURE IF EXISTS phase1_iam_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase1_iam_add_column(
|
||
IN p_table_name VARCHAR(64), IN p_column_name VARCHAR(64), IN p_definition VARCHAR(1000)
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = p_table_name AND COLUMN_NAME = p_column_name
|
||
) THEN
|
||
SET @phase1_iam_sql = CONCAT('ALTER TABLE `', p_table_name, '` ADD COLUMN `',
|
||
p_column_name, '` ', p_definition);
|
||
PREPARE phase1_iam_stmt FROM @phase1_iam_sql;
|
||
EXECUTE phase1_iam_stmt;
|
||
DEALLOCATE PREPARE phase1_iam_stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase1_iam_add_column('iam_tenant', 'approved_at', "DATETIME(3) NULL COMMENT 'Approval time'");
|
||
CALL phase1_iam_add_column('iam_tenant', 'approved_by', "BIGINT NULL COMMENT 'Reviewer ID'");
|
||
CALL phase1_iam_add_column('iam_tenant', 'reject_reason', "VARCHAR(500) NULL COMMENT 'Rejection reason'");
|
||
CALL phase1_iam_add_column('iam_tenant', 'legal_representative', "VARCHAR(64) NULL COMMENT '法定代表人' AFTER legal_name");
|
||
CALL phase1_iam_add_column('iam_tenant_qualification', 'review_comment', "VARCHAR(500) NULL COMMENT 'Review comment'");
|
||
CALL phase1_iam_add_column('iam_membership', 'created_by', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Creator ID'");
|
||
CALL phase1_iam_add_column('iam_membership', 'updated_by', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Updater ID'");
|
||
DROP PROCEDURE phase1_iam_add_column;
|
||
|
||
-- A global merchant owner template is assigned to newly registered enterprises.
|
||
INSERT INTO iam_role (
|
||
id, tenant_id, app_code, role_code, role_name, role_type, status,
|
||
description, created_by, updated_by, deleted, version
|
||
) VALUES (
|
||
4000100, 0, 'MERCHANT', 'MERCHANT_OWNER', '企业管理员', 'SYSTEM', 'ACTIVE',
|
||
'一期企业全部菜单和操作按钮模板', 1000001, 1000001, 0, 0
|
||
)
|
||
ON DUPLICATE KEY UPDATE status='ACTIVE', deleted=0;
|
||
|
||
-- Clone the一期 merchant menu/button template into the global scope.
|
||
INSERT INTO iam_permission (
|
||
id, tenant_id, app_code, permission_type, parent_id, permission_code,
|
||
permission_name, route_path, http_method, sort_no, status, deleted
|
||
)
|
||
SELECT id + 100000, 0, app_code, permission_type,
|
||
CASE WHEN parent_id = 0 THEN 0 ELSE parent_id + 100000 END,
|
||
permission_code, permission_name, route_path, NULL, sort_no, 'ACTIVE', 0
|
||
FROM iam_permission
|
||
WHERE tenant_id = 10001 AND app_code = 'MERCHANT'
|
||
AND permission_type IN ('MENU', 'BUTTON')
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name=VALUES(permission_name), route_path=VALUES(route_path),
|
||
parent_id=VALUES(parent_id), sort_no=VALUES(sort_no), status='ACTIVE', deleted=0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT owner_role.id, permission.id, 0
|
||
FROM iam_role owner_role
|
||
JOIN iam_permission permission
|
||
ON permission.app_code='MERCHANT' AND permission.tenant_id=0
|
||
AND permission.permission_type IN ('MENU','BUTTON')
|
||
AND permission.status='ACTIVE' AND permission.deleted=0
|
||
WHERE owner_role.tenant_id=0 AND owner_role.app_code='MERCHANT'
|
||
AND owner_role.role_code='MERCHANT_OWNER'
|
||
AND owner_role.status='ACTIVE' AND owner_role.deleted=0
|
||
ON DUPLICATE KEY UPDATE tenant_id=VALUES(tenant_id);
|
||
|
||
-- 不再写入伪造的资质 URL。资质必须先由 File 服务保存真实图片,再由 IAM 记录文件地址和审核结果。
|
||
-- 已部署环境中的 /phase1-seed/ 历史记录按 docs/企业资质文件迁移说明.md 处理,本脚本不得将其改成不存在的 UUID。
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase1-member-permissions.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
-- 企业成员管理按钮权限增量;部署后应将权限按需授予企业管理员角色。
|
||
USE baiyue_iam;
|
||
|
||
-- 为存量数据库补齐企业内成员编号唯一约束,防止并发重复创建。
|
||
SET @member_no_index_sql = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'iam_membership'
|
||
AND index_name = 'uk_membership_tenant_no'),
|
||
'SELECT 1',
|
||
'ALTER TABLE iam_membership ADD UNIQUE KEY uk_membership_tenant_no (tenant_id, member_no)'
|
||
);
|
||
PREPARE member_no_index_stmt FROM @member_no_index_sql;
|
||
EXECUTE member_no_index_stmt;
|
||
DEALLOCATE PREPARE member_no_index_stmt;
|
||
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name, sort_no, status, deleted)
|
||
VALUES
|
||
(5200023, 10001, 'MERCHANT', 'BUTTON', 5200020, 'merchant:member:edit', '编辑成员', 30, 'ACTIVE', 0),
|
||
(5200024, 10001, 'MERCHANT', 'BUTTON', 5200020, 'merchant:member:status', '启停成员', 40, 'ACTIVE', 0),
|
||
(5200025, 10001, 'MERCHANT', 'BUTTON', 5200020, 'merchant:member:reset-password', '重置成员密码', 50, 'ACTIVE', 0),
|
||
(5300023, 0, 'MERCHANT', 'BUTTON', 5300020, 'merchant:member:edit', '编辑成员', 30, 'ACTIVE', 0),
|
||
(5300024, 0, 'MERCHANT', 'BUTTON', 5300020, 'merchant:member:status', '启停成员', 40, 'ACTIVE', 0),
|
||
(5300025, 0, 'MERCHANT', 'BUTTON', 5300020, 'merchant:member:reset-password', '重置成员密码', 50, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE permission_name = VALUES(permission_name), status = VALUES(status), deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000101, id, 10001 FROM iam_permission
|
||
WHERE tenant_id = 10001 AND permission_code IN
|
||
('merchant:member:edit', 'merchant:member:status', 'merchant:member:reset-password')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000100, id, 0 FROM iam_permission
|
||
WHERE tenant_id = 0 AND permission_code IN
|
||
('merchant:member:edit', 'merchant:member:status', 'merchant:member:reset-password')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\rbac-role-management-v2.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
-- 角色管理闭环权限迁移,可重复执行。
|
||
USE baiyue_iam;
|
||
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
|
||
permission_name, sort_no, status, deleted)
|
||
VALUES
|
||
(5100011, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:create', '新增企业客户', 10, 'ACTIVE', 0),
|
||
(5101023, 0, 'PLATFORM', 'BUTTON', 5101020, 'platform:role:edit', '编辑角色', 30, 'ACTIVE', 0),
|
||
(5101024, 0, 'PLATFORM', 'BUTTON', 5101020, 'platform:role:status', '启停角色', 40, 'ACTIVE', 0),
|
||
(5101025, 0, 'PLATFORM', 'BUTTON', 5101020, 'platform:role:delete', '删除角色', 50, 'ACTIVE', 0),
|
||
(5101040, 0, 'PLATFORM', 'MENU', 5101000, 'platform:member:list', '平台成员', 40, 'ACTIVE', 0),
|
||
(5101041, 0, 'PLATFORM', 'BUTTON', 5101040, 'platform:member:grant-role', '分配成员角色', 10, 'ACTIVE', 0),
|
||
(5101012, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:edit', '编辑用户', 20, 'ACTIVE', 0),
|
||
(5101013, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:status', '启停用户', 30, 'ACTIVE', 0),
|
||
(5101014, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:reset-password', '重置密码', 40, 'ACTIVE', 0),
|
||
(5101015, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:grant-role', '分配内部用户角色', 50, 'ACTIVE', 0),
|
||
(5200033, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:edit', '编辑角色', 30, 'ACTIVE', 0),
|
||
(5200034, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:status', '启停角色', 40, 'ACTIVE', 0),
|
||
(5200035, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:delete', '删除角色', 50, 'ACTIVE', 0),
|
||
(5300033, 0, 'MERCHANT', 'BUTTON', 5300030, 'merchant:role:edit', '编辑角色', 30, 'ACTIVE', 0),
|
||
(5300034, 0, 'MERCHANT', 'BUTTON', 5300030, 'merchant:role:status', '启停角色', 40, 'ACTIVE', 0),
|
||
(5300035, 0, 'MERCHANT', 'BUTTON', 5300030, 'merchant:role:delete', '删除角色', 50, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), sort_no = VALUES(sort_no),
|
||
status = 'ACTIVE', deleted = 0;
|
||
|
||
-- 示例管理员获得新增权限;其他角色仍由管理员按需授权。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0 FROM iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND permission_code IN ('platform:role:edit', 'platform:role:status', 'platform:role:delete',
|
||
'platform:member:list', 'platform:member:grant-role', 'platform:customer:create',
|
||
'platform:user:edit', 'platform:user:status', 'platform:user:reset-password',
|
||
'platform:user:grant-role')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000101, id, 10001 FROM iam_permission
|
||
WHERE tenant_id = 10001 AND app_code = 'MERCHANT'
|
||
AND permission_code IN ('merchant:role:edit', 'merchant:role:status', 'merchant:role:delete')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000100, id, 0 FROM iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'MERCHANT'
|
||
AND permission_code IN ('merchant:role:edit', 'merchant:role:status', 'merchant:role:delete')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase1-platform-master-data-and-permission-management.sql
|
||
-- ============================================================================
|
||
-- 权限资源维护与平台主数据权限迁移。
|
||
-- 在 phase1-admin-rbac.sql、rbac-role-management-v2.sql 之后执行,可重复执行。
|
||
USE baiyue_iam;
|
||
|
||
-- 兼容不支持 ADD COLUMN IF NOT EXISTS 的 MySQL 版本,通过元数据判断后执行。
|
||
SET @iam_permission_built_in_sql = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'iam_permission' AND column_name = 'built_in'),
|
||
'SELECT 1',
|
||
"ALTER TABLE iam_permission ADD COLUMN built_in TINYINT NOT NULL DEFAULT 0 COMMENT '是否为系统预置权限;预置资源不可通过管理接口修改' AFTER status"
|
||
);
|
||
PREPARE iam_permission_built_in_stmt FROM @iam_permission_built_in_sql;
|
||
EXECUTE iam_permission_built_in_stmt;
|
||
DEALLOCATE PREPARE iam_permission_built_in_stmt;
|
||
|
||
-- 一期固定编号范围内的资源均为种子资源,运行期生成的雪花编号不作预置标记。
|
||
UPDATE iam_permission
|
||
SET built_in = 1
|
||
WHERE id BETWEEN 5000000 AND 5399999 AND built_in = 0;
|
||
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name,
|
||
route_path, sort_no, status, built_in, deleted)
|
||
VALUES
|
||
(5103040, 0, 'PLATFORM', 'MENU', 5103000, 'platform:download:list', '下载中心',
|
||
'/downloads', 40, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path), sort_no = VALUES(sort_no),
|
||
status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name,
|
||
sort_no, status, built_in, deleted)
|
||
VALUES
|
||
(5100014, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:view', '查看企业客户档案', 15, 'ACTIVE', 1, 0),
|
||
(5100012, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:edit', '编辑企业客户', 20, 'ACTIVE', 1, 0),
|
||
(5100013, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:status', '启停企业客户', 30, 'ACTIVE', 1, 0),
|
||
(5100015, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:export', '导出企业客户', 40, 'ACTIVE', 1, 0),
|
||
(5100016, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:reset-password', '重置客户管理员密码', 50, 'ACTIVE', 1, 0),
|
||
(5100017, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:delete', '归档企业客户', 60, 'ACTIVE', 1, 0),
|
||
(5100018, 0, 'PLATFORM', 'BUTTON', 5100010, 'platform:customer:impersonate', '跳转客户登录', 70, 'ACTIVE', 1, 0),
|
||
(5101015, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:grant-role', '分配内部用户角色', 50, 'ACTIVE', 1, 0),
|
||
(5101032, 0, 'PLATFORM', 'BUTTON', 5101030, 'platform:permission:edit', '编辑权限资源', 20, 'ACTIVE', 1, 0),
|
||
(5101033, 0, 'PLATFORM', 'BUTTON', 5101030, 'platform:permission:status', '启停权限资源', 30, 'ACTIVE', 1, 0),
|
||
(5101034, 0, 'PLATFORM', 'BUTTON', 5101030, 'platform:permission:delete', '删除权限资源', 40, 'ACTIVE', 1, 0),
|
||
(5103021, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:create', '新增快递公司', 10, 'ACTIVE', 1, 0),
|
||
(5103022, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:edit', '编辑快递公司', 20, 'ACTIVE', 1, 0),
|
||
(5103023, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:status', '启停快递公司', 30, 'ACTIVE', 1, 0),
|
||
(5103024, 0, 'PLATFORM', 'BUTTON', 5103020, 'platform:express:import', '批量导入快递公司', 40, 'ACTIVE', 1, 0),
|
||
(5103031, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:create', '新增往来单位', 10, 'ACTIVE', 1, 0),
|
||
(5103032, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:edit', '编辑往来单位', 20, 'ACTIVE', 1, 0),
|
||
(5103033, 0, 'PLATFORM', 'BUTTON', 5103030, 'platform:counterparty:status', '启停往来单位', 30, 'ACTIVE', 1, 0),
|
||
(5103041, 0, 'PLATFORM', 'BUTTON', 5103040, 'platform:download:all', '查看全部下载任务', 10, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), 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, role.tenant_id
|
||
FROM iam_role role
|
||
JOIN iam_permission permission ON permission.tenant_id = 0 AND permission.app_code = 'PLATFORM'
|
||
AND permission.permission_code IN (
|
||
'platform:customer:edit', 'platform:customer:status', 'platform:customer:reset-password',
|
||
'platform:customer:delete', 'platform:customer:impersonate', 'platform:user:grant-role',
|
||
'platform:permission:edit', 'platform:permission:status', 'platform:permission:delete',
|
||
'platform:express:list', 'platform:express:create', 'platform:express:edit', 'platform:express:status', 'platform:express:import',
|
||
'platform:counterparty:list', 'platform:counterparty:create', 'platform:counterparty:edit', 'platform:counterparty:status',
|
||
'platform:customer:export', 'platform:download:list', 'platform:download:all'
|
||
)
|
||
WHERE role.tenant_id = 0 AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR');
|
||
|
||
-- 已有客户运营角色继续具备客户导出与本人下载任务查询能力,但不授予查看全部任务。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT DISTINCT existing_grant.role_id, added_permission.id, existing_grant.tenant_id
|
||
FROM iam_role_permission existing_grant
|
||
JOIN iam_permission existing_permission ON existing_permission.id = existing_grant.permission_id
|
||
JOIN iam_permission added_permission
|
||
ON added_permission.tenant_id = 0 AND added_permission.app_code = 'PLATFORM'
|
||
AND added_permission.permission_code IN ('platform:customer:export', 'platform:download:list')
|
||
WHERE existing_permission.tenant_id = 0
|
||
AND existing_permission.app_code = 'PLATFORM'
|
||
AND existing_permission.permission_code IN ('platform:customer:list', 'platform:customer:review')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- 详情查看从列表和审核权限中拆分;升级时保留既有运营角色的查询能力。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT DISTINCT existing_grant.role_id, view_permission.id, existing_grant.tenant_id
|
||
FROM iam_role_permission existing_grant
|
||
JOIN iam_permission existing_permission
|
||
ON existing_permission.id = existing_grant.permission_id
|
||
JOIN iam_permission view_permission
|
||
ON view_permission.tenant_id = 0
|
||
AND view_permission.app_code = 'PLATFORM'
|
||
AND view_permission.permission_code = 'platform:customer:view'
|
||
WHERE existing_permission.tenant_id = 0
|
||
AND existing_permission.app_code = 'PLATFORM'
|
||
AND existing_permission.permission_code IN ('platform:customer:list', 'platform:customer:review')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase1-customer-profile.sql
|
||
-- ============================================================================
|
||
-- 一期平台客户档案字段增量迁移。可在同一数据库重复执行。
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'contact_wechat'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN contact_wechat VARCHAR(64) NULL COMMENT ''联系微信'' AFTER contact_email');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'sales_owner_name'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN sales_owner_name VARCHAR(64) NULL COMMENT ''销售负责人姓名'' AFTER contact_wechat');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'payment_term_days'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN payment_term_days SMALLINT UNSIGNED NULL COMMENT ''账期天数'' AFTER sales_owner_name');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'enterprise_type'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN enterprise_type VARCHAR(64) NULL COMMENT ''企业类型'' AFTER payment_term_days');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'established_date'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN established_date DATE NULL COMMENT ''成立日期'' AFTER enterprise_type');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'registered_capital'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN registered_capital DECIMAL(18,2) NULL COMMENT ''注册资本'' AFTER established_date');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'registered_capital_currency'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN registered_capital_currency CHAR(3) NULL COMMENT ''注册资本币种 ISO 4217'' AFTER registered_capital');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'registered_address'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN registered_address VARCHAR(500) NULL COMMENT ''注册地址文本'' AFTER registered_capital_currency');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'iam_tenant' AND column_name = 'internal_remark'),
|
||
'SELECT 1', 'ALTER TABLE iam_tenant ADD COLUMN internal_remark VARCHAR(1000) NULL COMMENT ''平台内部备注'' AFTER registered_address');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
-- ============================================================================
|
||
-- 来源:order\src\main\resources\db\phase1-order-extension.sql
|
||
-- ============================================================================
|
||
-- Phase-one first-leg order fields. Safe to run repeatedly on MySQL 8.
|
||
USE baiyue_order;
|
||
SET NAMES utf8mb4;
|
||
|
||
DROP PROCEDURE IF EXISTS phase1_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase1_add_column(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_definition VARCHAR(1000)
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE()
|
||
AND TABLE_NAME = p_table_name
|
||
AND COLUMN_NAME = p_column_name
|
||
) THEN
|
||
SET @phase1_sql = CONCAT('ALTER TABLE `', p_table_name, '` ADD COLUMN `',
|
||
p_column_name, '` ', p_definition);
|
||
PREPARE phase1_stmt FROM @phase1_sql;
|
||
EXECUTE phase1_stmt;
|
||
DEALLOCATE PREPARE phase1_stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase1_add_column('ord_order', 'pickup_method', "VARCHAR(24) NULL COMMENT 'Pickup method'");
|
||
CALL phase1_add_column('ord_order', 'receiving_warehouse_id', "BIGINT UNSIGNED NULL COMMENT 'Domestic receiving warehouse ID'");
|
||
CALL phase1_add_column('ord_order', 'receiving_warehouse_name', "VARCHAR(128) NULL COMMENT 'Domestic receiving warehouse snapshot'");
|
||
CALL phase1_add_column('ord_order', 'receiving_warehouse_address', "VARCHAR(512) NULL COMMENT '国内收货仓完整地址快照'");
|
||
CALL phase1_add_column('ord_order', 'receiving_time', "VARCHAR(128) NULL COMMENT '国内收货仓收货时间快照'");
|
||
CALL phase1_add_column('ord_order', 'channel_code', "VARCHAR(64) NULL COMMENT 'Shipping channel code'");
|
||
CALL phase1_add_column('ord_order', 'channel_name', "VARCHAR(128) NULL COMMENT 'Shipping channel snapshot'");
|
||
CALL phase1_add_column('ord_order', 'tax_mode', "VARCHAR(24) NULL COMMENT 'BUY_DECLARATION or TAX_REFUND'");
|
||
CALL phase1_add_column('ord_order', 'container_mode', "VARCHAR(24) NULL COMMENT 'FULL_CONTAINER or LCL'");
|
||
CALL phase1_add_column('ord_order', 'customs_inspection', "TINYINT(1) NULL COMMENT 'Whether customs inspection is required'");
|
||
CALL phase1_add_column('ord_order', 'destination_type', "VARCHAR(24) NULL COMMENT 'FBA, OVERSEAS or PRIVATE'");
|
||
CALL phase1_add_column('ord_order', 'amazon_warehouse_code', "VARCHAR(32) NULL COMMENT 'Amazon warehouse code'");
|
||
CALL phase1_add_column('ord_order', 'shipment_id', "VARCHAR(64) NULL COMMENT 'Amazon Shipment ID'");
|
||
CALL phase1_add_column('ord_order', 'reference_id', "VARCHAR(64) NULL COMMENT 'Amazon Reference ID'");
|
||
CALL phase1_add_column('ord_order', 'express_company', "VARCHAR(64) NULL COMMENT '履约快递公司名称快照'");
|
||
CALL phase1_add_column('ord_order', 'express_tracking_no', "VARCHAR(128) NULL COMMENT '履约快递单号'");
|
||
|
||
CALL phase1_add_column('ord_order_cargo', 'sku_no', "VARCHAR(64) NULL COMMENT 'SKU number'");
|
||
CALL phase1_add_column('ord_order_cargo', 'upc_no', "VARCHAR(64) NULL COMMENT 'UPC number'");
|
||
CALL phase1_add_column('ord_order_cargo', 'cargo_name_en', "VARCHAR(128) NULL COMMENT 'English cargo name'");
|
||
CALL phase1_add_column('ord_order_cargo', 'customs_hs_code', "VARCHAR(20) NULL COMMENT 'Destination customs HS code'");
|
||
CALL phase1_add_column('ord_order_cargo', 'units_per_package', "INT UNSIGNED NULL COMMENT 'Units per carton'");
|
||
CALL phase1_add_column('ord_order_cargo', 'total_quantity', "INT UNSIGNED NULL COMMENT 'Total units'");
|
||
CALL phase1_add_column('ord_order_cargo', 'net_weight_kg', "DECIMAL(18,3) NULL COMMENT 'Net weight in kilograms'");
|
||
CALL phase1_add_column('ord_order_cargo', 'unit_declared_value', "DECIMAL(18,2) NULL COMMENT 'Declared value per unit'");
|
||
CALL phase1_add_column('ord_order_cargo', 'material', "VARCHAR(128) NULL COMMENT 'Material'");
|
||
CALL phase1_add_column('ord_order_cargo', 'product_usage', "VARCHAR(128) NULL COMMENT 'Usage'");
|
||
CALL phase1_add_column('ord_order_cargo', 'model', "VARCHAR(64) NULL COMMENT 'Model'");
|
||
CALL phase1_add_column('ord_order_cargo', 'brand', "VARCHAR(64) NULL COMMENT 'Brand'");
|
||
CALL phase1_add_column('ord_order_cargo', 'product_length_cm', "DECIMAL(12,2) NULL COMMENT 'Product length in centimeters'");
|
||
CALL phase1_add_column('ord_order_cargo', 'product_width_cm', "DECIMAL(12,2) NULL COMMENT 'Product width in centimeters'");
|
||
CALL phase1_add_column('ord_order_cargo', 'product_height_cm', "DECIMAL(12,2) NULL COMMENT 'Product height in centimeters'");
|
||
CALL phase1_add_column('ord_order_cargo', 'shipment_id', "VARCHAR(64) NULL COMMENT 'Cargo Amazon Shipment ID'");
|
||
CALL phase1_add_column('ord_order_cargo', 'reference_id', "VARCHAR(64) NULL COMMENT 'Cargo Amazon Reference ID'");
|
||
CALL phase1_add_column('ord_order_cargo', 'product_image_file_id', "BIGINT UNSIGNED NULL COMMENT 'Product image logical file ID'");
|
||
|
||
UPDATE ord_order_cargo cargo
|
||
JOIN ord_order orders ON orders.id = cargo.order_id AND orders.tenant_id = cargo.tenant_id
|
||
SET cargo.shipment_id = COALESCE(NULLIF(cargo.shipment_id, ''), orders.shipment_id),
|
||
cargo.reference_id = COALESCE(NULLIF(cargo.reference_id, ''), orders.reference_id)
|
||
WHERE orders.destination_type = 'FBA'
|
||
AND ((cargo.shipment_id IS NULL OR cargo.shipment_id = '')
|
||
OR (cargo.reference_id IS NULL OR cargo.reference_id = ''));
|
||
|
||
DROP PROCEDURE phase1_add_column;
|
||
|
||
-- 已有订单库的幂等记录迁移,和订单写入处于同一个本地事务。
|
||
CREATE TABLE IF NOT EXISTS ord_idempotency_record (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '幂等记录 ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '租户 ID',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人 ID',
|
||
operation VARCHAR(32) NOT NULL COMMENT '操作类型',
|
||
resource_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '资源 ID',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '客户端幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '请求内容摘要',
|
||
status VARCHAR(16) NOT NULL COMMENT '处理状态',
|
||
result_order_id BIGINT UNSIGNED NULL COMMENT '重放结果订单 ID',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_order_idempotency_scope (tenant_id, operator_id, operation, idempotency_key),
|
||
KEY idx_order_idempotency_created (created_at)
|
||
) ENGINE=InnoDB COMMENT='订单写接口持久化幂等记录';
|
||
|
||
-- ============================================================================
|
||
-- 来源:order\src\main\resources\db\phase1-masterdata-extension.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_order;
|
||
-- MySQL 8 可重复执行的订单快照及履约字段迁移。
|
||
SET @schema_name = DATABASE();
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@schema_name AND TABLE_NAME='ord_order' AND COLUMN_NAME='receiving_warehouse_address')=0,
|
||
'ALTER TABLE ord_order ADD COLUMN receiving_warehouse_address VARCHAR(600) NULL COMMENT ''收货仓地址历史快照'' AFTER receiving_warehouse_name','SELECT 1');
|
||
PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@schema_name AND TABLE_NAME='ord_order' AND COLUMN_NAME='receiving_time')=0,
|
||
'ALTER TABLE ord_order ADD COLUMN receiving_time VARCHAR(100) NULL COMMENT ''收货时间历史快照'' AFTER receiving_warehouse_address','SELECT 1');
|
||
PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@schema_name AND TABLE_NAME='ord_order' AND COLUMN_NAME='express_company')=0,
|
||
'ALTER TABLE ord_order ADD COLUMN express_company VARCHAR(64) NULL COMMENT ''履约快递公司'' AFTER reference_id','SELECT 1');
|
||
PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@schema_name AND TABLE_NAME='ord_order' AND COLUMN_NAME='express_tracking_no')=0,
|
||
'ALTER TABLE ord_order ADD COLUMN express_tracking_no VARCHAR(128) NULL COMMENT ''履约快递单号'' AFTER express_company','SELECT 1');
|
||
PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
|
||
|
||
-- ============================================================================
|
||
-- 来源:warehouse\src\main\resources\db\phase1-warehouse-schema-migration.sql
|
||
-- ============================================================================
|
||
-- 既有 baiyue_warehouse 的一期增量迁移(只增不删,可重复执行)。
|
||
-- 执行顺序:warehouse.sql、warehouse-partner.sql、warehouse-express-company.sql、
|
||
-- 本文件、warehouse-seed.sql。应用不会在启动时自动执行这些脚本,必须由发布流程执行。
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_warehouse;
|
||
|
||
-- MySQL 5.7/8.0 各小版本对条件补列语法的支持不一致。
|
||
-- 统一先查 information_schema,再通过动态 SQL 补列,确保脚本可重复执行。
|
||
DROP PROCEDURE IF EXISTS add_column_if_missing;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE add_column_if_missing(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_column_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1
|
||
FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @add_column_sql = CONCAT(
|
||
'ALTER TABLE `', REPLACE(p_table_name, '`', '``'),
|
||
'` ADD COLUMN `', REPLACE(p_column_name, '`', '``'),
|
||
'` ', p_column_definition
|
||
);
|
||
PREPARE add_column_statement FROM @add_column_sql;
|
||
EXECUTE add_column_statement;
|
||
DEALLOCATE PREPARE add_column_statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
-- 早期 wh_warehouse 缺少的业务与审计字段。
|
||
CALL add_column_if_missing('wh_warehouse', 'warehouse_type', 'VARCHAR(32) NOT NULL DEFAULT ''DOMESTIC'' COMMENT ''仓库类型'' AFTER warehouse_name');
|
||
CALL add_column_if_missing('wh_warehouse', 'country_code', 'CHAR(2) NOT NULL DEFAULT ''CN'' COMMENT ''所在国家二字码'' AFTER warehouse_type');
|
||
CALL add_column_if_missing('wh_warehouse', 'timezone', 'VARCHAR(64) NOT NULL DEFAULT ''Asia/Shanghai'' COMMENT ''IANA时区'' AFTER country_code');
|
||
CALL add_column_if_missing('wh_warehouse', 'contact_name', 'VARCHAR(64) NULL AFTER timezone');
|
||
CALL add_column_if_missing('wh_warehouse', 'contact_phone', 'VARCHAR(32) NULL AFTER contact_name');
|
||
CALL add_column_if_missing('wh_warehouse', 'contact_email', 'VARCHAR(128) NULL AFTER contact_phone');
|
||
CALL add_column_if_missing('wh_warehouse', 'status', 'VARCHAR(20) NOT NULL DEFAULT ''ACTIVE'' AFTER contact_email');
|
||
CALL add_column_if_missing('wh_warehouse', 'remark', 'VARCHAR(500) NULL AFTER status');
|
||
CALL add_column_if_missing('wh_warehouse', 'created_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_warehouse', 'created_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_warehouse', 'updated_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_warehouse', 'updated_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_warehouse', 'deleted', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||
CALL add_column_if_missing('wh_warehouse', 'version', 'INT NOT NULL DEFAULT 0');
|
||
|
||
CALL add_column_if_missing('wh_warehouse_address', 'tenant_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER id');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'warehouse_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER tenant_id');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'address_type', 'VARCHAR(20) NOT NULL DEFAULT ''OPERATING'' AFTER warehouse_id');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'country_code', 'CHAR(2) NOT NULL DEFAULT ''CN'' AFTER address_type');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'state_province', 'VARCHAR(100) NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'city', 'VARCHAR(100) NOT NULL DEFAULT '''' AFTER state_province');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'district', 'VARCHAR(100) NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'postal_code', 'VARCHAR(20) NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'address_line1', 'VARCHAR(255) NOT NULL DEFAULT '''' AFTER postal_code');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'address_line2', 'VARCHAR(255) NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'latitude', 'DECIMAL(10,7) NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'longitude', 'DECIMAL(10,7) NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'created_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'created_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'updated_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'updated_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'deleted', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||
CALL add_column_if_missing('wh_warehouse_address', 'version', 'INT NOT NULL DEFAULT 0');
|
||
|
||
CALL add_column_if_missing('wh_service_capability', 'tenant_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER id');
|
||
CALL add_column_if_missing('wh_service_capability', 'warehouse_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER tenant_id');
|
||
CALL add_column_if_missing('wh_service_capability', 'capability_code', 'VARCHAR(50) NOT NULL DEFAULT '''' AFTER warehouse_id');
|
||
CALL add_column_if_missing('wh_service_capability', 'capability_name', 'VARCHAR(100) NOT NULL DEFAULT '''' AFTER capability_code');
|
||
CALL add_column_if_missing('wh_service_capability', 'enabled', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||
CALL add_column_if_missing('wh_service_capability', 'cutoff_time', 'TIME NULL');
|
||
CALL add_column_if_missing('wh_service_capability', 'description', 'VARCHAR(500) NULL');
|
||
CALL add_column_if_missing('wh_service_capability', 'created_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_service_capability', 'created_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_service_capability', 'updated_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_service_capability', 'updated_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_service_capability', 'deleted', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||
CALL add_column_if_missing('wh_service_capability', 'version', 'INT NOT NULL DEFAULT 0');
|
||
|
||
CALL add_column_if_missing('wh_partner', 'tenant_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER id');
|
||
CALL add_column_if_missing('wh_partner', 'partner_code', 'VARCHAR(50) NOT NULL DEFAULT '''' AFTER tenant_id');
|
||
CALL add_column_if_missing('wh_partner', 'partner_name', 'VARCHAR(128) NOT NULL DEFAULT '''' AFTER partner_code');
|
||
CALL add_column_if_missing('wh_partner', 'partner_type', 'VARCHAR(20) NOT NULL DEFAULT ''CUSTOMER'' AFTER partner_name');
|
||
CALL add_column_if_missing('wh_partner', 'contact_name', 'VARCHAR(64) NOT NULL DEFAULT '''' AFTER partner_type');
|
||
CALL add_column_if_missing('wh_partner', 'contact_phone', 'VARCHAR(32) NOT NULL DEFAULT '''' AFTER contact_name');
|
||
CALL add_column_if_missing('wh_partner', 'contact_email', 'VARCHAR(128) NULL');
|
||
CALL add_column_if_missing('wh_partner', 'country_code', 'CHAR(2) NOT NULL DEFAULT ''CN'' AFTER contact_email');
|
||
CALL add_column_if_missing('wh_partner', 'province', 'VARCHAR(64) NULL');
|
||
CALL add_column_if_missing('wh_partner', 'city', 'VARCHAR(64) NOT NULL DEFAULT '''' AFTER province');
|
||
CALL add_column_if_missing('wh_partner', 'district', 'VARCHAR(64) NULL');
|
||
CALL add_column_if_missing('wh_partner', 'address_line', 'VARCHAR(255) NOT NULL DEFAULT '''' AFTER district');
|
||
CALL add_column_if_missing('wh_partner', 'postal_code', 'VARCHAR(20) NULL');
|
||
CALL add_column_if_missing('wh_partner', 'status', 'VARCHAR(20) NOT NULL DEFAULT ''ACTIVE'' AFTER postal_code');
|
||
CALL add_column_if_missing('wh_partner', 'created_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_partner', 'created_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_partner', 'updated_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_partner', 'updated_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_partner', 'deleted', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||
CALL add_column_if_missing('wh_partner', 'version', 'INT NOT NULL DEFAULT 0');
|
||
|
||
-- 该表在旧库中不存在时先由 warehouse-express-company.sql 建表;此段补齐早期部署缺列。
|
||
CALL add_column_if_missing('wh_express_company', 'tenant_id', 'BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER id');
|
||
CALL add_column_if_missing('wh_express_company', 'company_code', 'VARCHAR(50) NOT NULL DEFAULT '''' AFTER tenant_id');
|
||
CALL add_column_if_missing('wh_express_company', 'company_name', 'VARCHAR(128) NOT NULL DEFAULT '''' AFTER company_code');
|
||
CALL add_column_if_missing('wh_express_company', 'service_phone', 'VARCHAR(32) NULL');
|
||
CALL add_column_if_missing('wh_express_company', 'website', 'VARCHAR(255) NULL');
|
||
CALL add_column_if_missing('wh_express_company', 'tracking_url_template', 'VARCHAR(500) NULL');
|
||
CALL add_column_if_missing('wh_express_company', 'status', 'VARCHAR(20) NOT NULL DEFAULT ''ACTIVE'' AFTER tracking_url_template');
|
||
CALL add_column_if_missing('wh_express_company', 'remark', 'VARCHAR(500) NULL');
|
||
CALL add_column_if_missing('wh_express_company', 'created_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_express_company', 'created_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_express_company', 'updated_by', 'BIGINT UNSIGNED NULL');
|
||
CALL add_column_if_missing('wh_express_company', 'updated_at', 'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)');
|
||
CALL add_column_if_missing('wh_express_company', 'deleted', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||
CALL add_column_if_missing('wh_express_company', 'version', 'INT NOT NULL DEFAULT 0');
|
||
|
||
-- 平台公共仓储主数据和平台往来单位统一归平台作用域 0;订单自身仍按客户 tenant_id 隔离。
|
||
UPDATE wh_warehouse SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_warehouse_address a
|
||
JOIN wh_warehouse w ON w.id = a.warehouse_id
|
||
SET a.tenant_id = w.tenant_id
|
||
WHERE a.tenant_id <> w.tenant_id;
|
||
UPDATE wh_service_capability c
|
||
JOIN wh_warehouse w ON w.id = c.warehouse_id
|
||
SET c.tenant_id = w.tenant_id
|
||
WHERE c.tenant_id <> w.tenant_id;
|
||
UPDATE wh_warehouse_address SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_service_capability SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_express_company SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_partner SET tenant_id = 0, data_scope_id = 0
|
||
WHERE tenant_id <> 0 OR data_scope_id <> 0;
|
||
UPDATE wh_partner_type SET tenant_id = 0, data_scope_id = 0
|
||
WHERE tenant_id <> 0 OR data_scope_id <> 0;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_platform_public_resource_scope;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_platform_public_resource_scope()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM wh_warehouse WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_warehouse_address WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_service_capability WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_express_company WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_partner WHERE tenant_id <> 0 OR data_scope_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_partner_type WHERE tenant_id <> 0 OR data_scope_id <> 0) > 0 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = 'Warehouse 迁移失败:平台公共仓储或往来单位仍有非0作用域';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM wh_warehouse_address a
|
||
JOIN wh_warehouse w ON w.id = a.warehouse_id
|
||
WHERE a.tenant_id <> w.tenant_id
|
||
) OR EXISTS (
|
||
SELECT 1 FROM wh_service_capability c
|
||
JOIN wh_warehouse w ON w.id = c.warehouse_id
|
||
WHERE c.tenant_id <> w.tenant_id
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = 'Warehouse 迁移失败:仓库子表与父仓库平台归属不一致';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_platform_public_resource_scope();
|
||
DROP PROCEDURE IF EXISTS assert_platform_public_resource_scope;
|
||
|
||
DROP PROCEDURE IF EXISTS add_column_if_missing;
|
||
|
||
-- MySQL 8 的 ALTER ADD INDEX 不在所有小版本都支持 IF NOT EXISTS,使用元数据判断以保证重复执行安全。
|
||
SET @schema_name = DATABASE();
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_warehouse' AND index_name = 'idx_wh_tenant_status') = 0,
|
||
'ALTER TABLE wh_warehouse ADD KEY idx_wh_tenant_status (tenant_id, status, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_warehouse' AND index_name = 'idx_wh_tenant_type_status') = 0,
|
||
'ALTER TABLE wh_warehouse ADD KEY idx_wh_tenant_type_status (tenant_id, warehouse_type, status, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_warehouse_address' AND index_name = 'idx_address_warehouse') = 0,
|
||
'ALTER TABLE wh_warehouse_address ADD KEY idx_address_warehouse (tenant_id, warehouse_id, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_service_capability' AND index_name = 'idx_capability_query') = 0,
|
||
'ALTER TABLE wh_service_capability ADD KEY idx_capability_query (tenant_id, warehouse_id, enabled, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_partner' AND index_name = 'idx_wh_partner_tenant_status') = 0,
|
||
'ALTER TABLE wh_partner ADD KEY idx_wh_partner_tenant_status (tenant_id, status, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_express_company' AND index_name = 'idx_wh_express_tenant_status') = 0,
|
||
'ALTER TABLE wh_express_company ADD KEY idx_wh_express_tenant_status (tenant_id, status, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_partner' AND index_name = 'uk_wh_partner_tenant_code') = 0,
|
||
'ALTER TABLE wh_partner ADD UNIQUE KEY uk_wh_partner_tenant_code (tenant_id, partner_code, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_express_company' AND index_name = 'uk_wh_express_tenant_code') = 0,
|
||
'ALTER TABLE wh_express_company ADD UNIQUE KEY uk_wh_express_tenant_code (tenant_id, company_code, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_express_company' AND index_name = 'uk_wh_express_tenant_name') = 0,
|
||
'ALTER TABLE wh_express_company ADD UNIQUE KEY uk_wh_express_tenant_name (tenant_id, company_name, deleted)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase1-firstleg-task-list-index.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
-- 既有履约任务表补充分页查询索引;重复执行时不重复创建。
|
||
SET @firstleg_task_list_index_sql = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_fulfillment_task'
|
||
AND index_name = 'idx_task_tenant_created'),
|
||
'SELECT 1',
|
||
'CREATE INDEX idx_task_tenant_created ON fl_fulfillment_task (tenant_id, created_at)'
|
||
);
|
||
PREPARE firstleg_task_list_index_stmt FROM @firstleg_task_list_index_sql;
|
||
EXECUTE firstleg_task_list_index_stmt;
|
||
DEALLOCATE PREPARE firstleg_task_list_index_stmt;
|
||
|
||
-- ============================================================================
|
||
-- 来源:warehouse\src\main\resources\db\phase2-pallet-location-schema.sql
|
||
-- ============================================================================
|
||
-- 二期托盘、库位、预占与占用表结构。
|
||
-- 本脚本只新增表、字段、索引和约束,不包含业务数据及流程实现。
|
||
-- 可在 warehouse.sql 和一期增量脚本之后执行;所有建表均可重复执行。
|
||
SET NAMES utf8mb4;
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_warehouse
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_warehouse;
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_pallet (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '托盘ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共托盘固定使用 tenant_id=0',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
|
||
pallet_code VARCHAR(50) NOT NULL COMMENT '平台仓库内唯一托盘编码',
|
||
pallet_type VARCHAR(32) NOT NULL COMMENT '托盘类型',
|
||
pallet_name VARCHAR(100) NULL COMMENT '托盘名称',
|
||
capacity_cbm DECIMAL(18,6) NOT NULL COMMENT '托盘可用容积CBM',
|
||
weight_kg DECIMAL(18,3) NOT NULL DEFAULT 0 COMMENT '托盘重量kg',
|
||
color VARCHAR(32) NOT NULL DEFAULT '' COMMENT '托盘颜色',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'AVAILABLE' COMMENT '内部状态:AVAILABLE、RESERVED或OCCUPIED',
|
||
remark VARCHAR(500) NULL COMMENT '备注',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_pallet_tenant_code (tenant_id, pallet_code),
|
||
KEY idx_pallet_available (tenant_id, warehouse_id, status, deleted),
|
||
KEY idx_pallet_type (tenant_id, warehouse_id, pallet_type, status, deleted),
|
||
CONSTRAINT fk_pallet_warehouse FOREIGN KEY (warehouse_id) REFERENCES wh_warehouse (id),
|
||
CONSTRAINT ck_pallet_capacity CHECK (capacity_cbm > 0)
|
||
) ENGINE=InnoDB COMMENT='二期托盘基础资料和物理托盘实例';
|
||
|
||
-- 存量库补齐托盘重量、颜色字段(可重复执行)。
|
||
SET @pallet_weight_sql = IF((SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'wh_pallet' AND column_name = 'weight_kg') = 0,
|
||
'ALTER TABLE wh_pallet ADD COLUMN weight_kg DECIMAL(18,3) NOT NULL DEFAULT 0 COMMENT ''托盘重量kg'' AFTER capacity_cbm', 'SELECT 1');
|
||
PREPARE pallet_weight_stmt FROM @pallet_weight_sql; EXECUTE pallet_weight_stmt; DEALLOCATE PREPARE pallet_weight_stmt;
|
||
SET @pallet_color_sql = IF((SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'wh_pallet' AND column_name = 'color') = 0,
|
||
'ALTER TABLE wh_pallet ADD COLUMN color VARCHAR(32) NOT NULL DEFAULT '''' COMMENT ''托盘颜色'' AFTER weight_kg', 'SELECT 1');
|
||
PREPARE pallet_color_stmt FROM @pallet_color_sql; EXECUTE pallet_color_stmt; DEALLOCATE PREPARE pallet_color_stmt;
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_location (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '库位ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共库位固定使用 tenant_id=0',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
|
||
zone_code VARCHAR(50) NULL COMMENT '库区编码',
|
||
zone_name VARCHAR(100) NULL COMMENT '库区名称',
|
||
location_code VARCHAR(50) NOT NULL COMMENT '平台仓库内唯一库位编码',
|
||
location_name VARCHAR(100) NULL COMMENT '库位名称',
|
||
location_type VARCHAR(32) NOT NULL DEFAULT 'PALLET' COMMENT '库位类型,二期为PALLET',
|
||
max_weight_kg DECIMAL(18,3) NULL COMMENT '最大承重展示值,二期不作容量拦截',
|
||
shared_overflow TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否为系统共享溢出位A0',
|
||
status VARCHAR(20) NOT NULL DEFAULT 'AVAILABLE' COMMENT 'AVAILABLE、RESERVED、OCCUPIED或DISABLED',
|
||
remark VARCHAR(500) NULL COMMENT '备注',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_location_tenant_warehouse_code (tenant_id, warehouse_id, location_code),
|
||
KEY idx_location_available (tenant_id, warehouse_id, status, deleted),
|
||
KEY idx_location_zone (tenant_id, warehouse_id, zone_code, status, deleted),
|
||
CONSTRAINT fk_location_warehouse FOREIGN KEY (warehouse_id) REFERENCES wh_warehouse (id),
|
||
CONSTRAINT ck_location_max_weight CHECK (max_weight_kg IS NULL OR max_weight_kg > 0)
|
||
) ENGINE=InnoDB COMMENT='二期库位基础资料,默认一托盘对应一个库位且不作容量校验';
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_location_reservation (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '库位预占ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共库位预占固定使用 tenant_id=0',
|
||
reservation_no VARCHAR(40) NOT NULL COMMENT '平台内唯一预占业务号',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '调用方幂等键',
|
||
source_service VARCHAR(40) NOT NULL DEFAULT 'FIRSTLEG' COMMENT '来源服务',
|
||
business_id BIGINT UNSIGNED NOT NULL COMMENT '来源服务入库单ID',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID',
|
||
pallet_id BIGINT UNSIGNED NOT NULL COMMENT '托盘ID',
|
||
pallet_code VARCHAR(50) NOT NULL COMMENT '托盘编码快照',
|
||
location_id BIGINT UNSIGNED NOT NULL COMMENT '库位ID',
|
||
location_code VARCHAR(50) NOT NULL COMMENT '库位编码快照',
|
||
shared_overflow TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否使用共享溢出位A0',
|
||
reservation_status VARCHAR(20) NOT NULL DEFAULT 'RESERVED' COMMENT 'RESERVED、CONFIRMED、RELEASED或EXPIRED',
|
||
expires_at DATETIME(3) NOT NULL COMMENT '预占过期时间',
|
||
confirmed_at DATETIME(3) NULL COMMENT '确认占用时间',
|
||
released_at DATETIME(3) NULL COMMENT '释放时间',
|
||
release_reason VARCHAR(500) NULL COMMENT '释放或过期原因',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人或系统主体ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
active_reservation TINYINT GENERATED ALWAYS AS (CASE WHEN reservation_status = 'RESERVED' AND shared_overflow = 0 THEN 1 ELSE NULL END) STORED COMMENT '普通库位活动预占唯一标记',
|
||
active_pallet_reservation TINYINT GENERATED ALWAYS AS (CASE WHEN reservation_status = 'RESERVED' THEN 1 ELSE NULL END) STORED COMMENT '托盘活动预占唯一标记(包含A0)',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_location_reservation_no (tenant_id, reservation_no),
|
||
UNIQUE KEY uk_location_reservation_idempotency (tenant_id, source_service, idempotency_key),
|
||
UNIQUE KEY uk_location_active_reservation (tenant_id, location_id, active_reservation),
|
||
UNIQUE KEY uk_pallet_active_reservation (tenant_id, pallet_id, active_pallet_reservation),
|
||
KEY idx_location_reservation_business (tenant_id, source_service, business_id, reservation_status),
|
||
KEY idx_location_reservation_expire (reservation_status, expires_at),
|
||
KEY idx_location_reservation_target (tenant_id, warehouse_id, location_id, reservation_status),
|
||
CONSTRAINT fk_reservation_warehouse FOREIGN KEY (warehouse_id) REFERENCES wh_warehouse (id),
|
||
CONSTRAINT fk_reservation_pallet FOREIGN KEY (pallet_id) REFERENCES wh_pallet (id),
|
||
CONSTRAINT fk_reservation_location FOREIGN KEY (location_id) REFERENCES wh_location (id)
|
||
) ENGINE=InnoDB COMMENT='跨服务入库Saga的托盘和库位临时预占';
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_location_occupancy (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '库位占用ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共库位占用固定使用 tenant_id=0',
|
||
occupancy_no VARCHAR(40) NOT NULL COMMENT '平台内唯一占用业务号',
|
||
reservation_id BIGINT UNSIGNED NOT NULL COMMENT '来源预占ID',
|
||
source_service VARCHAR(40) NOT NULL DEFAULT 'FIRSTLEG' COMMENT '来源服务',
|
||
business_id BIGINT UNSIGNED NOT NULL COMMENT '来源服务入库单ID',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID',
|
||
pallet_id BIGINT UNSIGNED NOT NULL COMMENT '托盘ID',
|
||
pallet_code VARCHAR(50) NOT NULL COMMENT '托盘编码快照',
|
||
location_id BIGINT UNSIGNED NOT NULL COMMENT '库位ID',
|
||
location_code VARCHAR(50) NOT NULL COMMENT '库位编码快照',
|
||
shared_overflow TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否使用共享溢出位A0',
|
||
allocated_volume_cbm DECIMAL(18,6) NOT NULL COMMENT '本托盘分配体积CBM',
|
||
allocated_package_count INT UNSIGNED NULL COMMENT '本托盘分配箱数',
|
||
occupancy_status VARCHAR(20) NOT NULL DEFAULT 'OCCUPIED' COMMENT 'OCCUPIED或RELEASED',
|
||
occupied_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '占用开始时间',
|
||
released_at DATETIME(3) NULL COMMENT '释放时间',
|
||
release_reason VARCHAR(500) NULL COMMENT '释放原因',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人或系统主体ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
active_location_occupancy TINYINT GENERATED ALWAYS AS (CASE WHEN occupancy_status = 'OCCUPIED' AND shared_overflow = 0 THEN 1 ELSE NULL END) STORED COMMENT '普通库位活动占用唯一标记',
|
||
active_pallet_occupancy TINYINT GENERATED ALWAYS AS (CASE WHEN occupancy_status = 'OCCUPIED' THEN 1 ELSE NULL END) STORED COMMENT '托盘活动占用唯一标记(包含A0)',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_location_occupancy_no (tenant_id, occupancy_no),
|
||
UNIQUE KEY uk_occupancy_reservation (tenant_id, reservation_id),
|
||
UNIQUE KEY uk_location_active_occupancy (tenant_id, location_id, active_location_occupancy),
|
||
UNIQUE KEY uk_pallet_active_occupancy (tenant_id, pallet_id, active_pallet_occupancy),
|
||
KEY idx_occupancy_business (tenant_id, source_service, business_id, occupancy_status),
|
||
KEY idx_occupancy_warehouse (tenant_id, warehouse_id, occupancy_status, occupied_at),
|
||
CONSTRAINT fk_occupancy_reservation FOREIGN KEY (reservation_id) REFERENCES wh_location_reservation (id),
|
||
CONSTRAINT fk_occupancy_warehouse FOREIGN KEY (warehouse_id) REFERENCES wh_warehouse (id),
|
||
CONSTRAINT fk_occupancy_pallet FOREIGN KEY (pallet_id) REFERENCES wh_pallet (id),
|
||
CONSTRAINT fk_occupancy_location FOREIGN KEY (location_id) REFERENCES wh_location (id),
|
||
CONSTRAINT ck_occupancy_volume CHECK (allocated_volume_cbm > 0)
|
||
) ENGINE=InnoDB COMMENT='已确认的一托一库位占用事实记录';
|
||
|
||
-- 头程入库单属于其他数据库,不建立跨库外键。
|
||
|
||
-- ============================================================================
|
||
-- 来源:warehouse\src\main\resources\db\phase2-location-overflow-migration.sql
|
||
-- ============================================================================
|
||
-- 二期库位共享溢出位 A0 增量迁移。
|
||
-- 执行顺序:phase2-pallet-location-schema.sql、本文件。
|
||
-- 普通库位仍保持一托一位;同一租户仓库只有一个 A0,且 A0 可承载多个活动资源。
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_warehouse;
|
||
|
||
DROP PROCEDURE IF EXISTS add_location_overflow_column_if_missing;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE add_location_overflow_column_if_missing(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_column_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @column_sql = CONCAT(
|
||
'ALTER TABLE `', REPLACE(p_table_name, '`', '``'),
|
||
'` ADD COLUMN `', REPLACE(p_column_name, '`', '``'), '` ', p_column_definition
|
||
);
|
||
PREPARE column_statement FROM @column_sql;
|
||
EXECUTE column_statement;
|
||
DEALLOCATE PREPARE column_statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL add_location_overflow_column_if_missing(
|
||
'wh_location', 'shared_overflow',
|
||
'TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否为系统共享溢出位 A0'' AFTER max_weight_kg'
|
||
);
|
||
CALL add_location_overflow_column_if_missing(
|
||
'wh_location_reservation', 'shared_overflow',
|
||
'TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否使用共享溢出位 A0'' AFTER location_code'
|
||
);
|
||
CALL add_location_overflow_column_if_missing(
|
||
'wh_location_occupancy', 'shared_overflow',
|
||
'TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否使用共享溢出位 A0'' AFTER location_code'
|
||
);
|
||
|
||
-- 兼容迁移前人工或旧流程已创建的 A0,并将资源快照回填一致。
|
||
UPDATE wh_location
|
||
SET shared_overflow = 1,
|
||
status = 'AVAILABLE'
|
||
WHERE location_code = 'A0' AND deleted = 0;
|
||
|
||
UPDATE wh_location_reservation r
|
||
JOIN wh_location l
|
||
ON l.tenant_id = r.tenant_id AND l.id = r.location_id
|
||
SET r.shared_overflow = l.shared_overflow;
|
||
|
||
UPDATE wh_location_occupancy o
|
||
JOIN wh_location l
|
||
ON l.tenant_id = o.tenant_id AND l.id = o.location_id
|
||
SET o.shared_overflow = l.shared_overflow;
|
||
|
||
DROP PROCEDURE IF EXISTS add_location_overflow_column_if_missing;
|
||
|
||
-- 仅旧生成列需要移除并重建唯一索引;已经是最终定义时必须跳过,避免重复部署产生约束空窗。
|
||
SET @schema_name = DATABASE();
|
||
SET @reservation_needs_upgrade = IFNULL((
|
||
SELECT IF(LOCATE('shared_overflow', generation_expression) = 0, 1, 0)
|
||
FROM information_schema.columns
|
||
WHERE table_schema = @schema_name AND table_name = 'wh_location_reservation'
|
||
AND column_name = 'active_reservation'
|
||
), 1);
|
||
SET @sql = IF(@reservation_needs_upgrade = 1 AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_reservation' AND index_name = 'uk_location_active_reservation') > 0,
|
||
'ALTER TABLE wh_location_reservation DROP INDEX uk_location_active_reservation', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF(@reservation_needs_upgrade = 1 AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_reservation' AND index_name = 'uk_pallet_active_reservation') > 0,
|
||
'ALTER TABLE wh_location_reservation DROP INDEX uk_pallet_active_reservation', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
SET @sql = IF(@reservation_needs_upgrade = 1,
|
||
'ALTER TABLE wh_location_reservation MODIFY COLUMN active_reservation TINYINT GENERATED ALWAYS AS (CASE WHEN reservation_status = ''RESERVED'' AND shared_overflow = 0 THEN 1 ELSE NULL END) STORED COMMENT ''普通库位活动预占唯一标记,A0 返回 NULL''',
|
||
'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_reservation' AND index_name = 'uk_location_active_reservation') = 0,
|
||
'ALTER TABLE wh_location_reservation ADD UNIQUE KEY uk_location_active_reservation (tenant_id, location_id, active_reservation)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
SET @occupancy_needs_upgrade = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = @schema_name AND table_name = 'wh_location_occupancy'
|
||
AND column_name = 'active_location_occupancy'
|
||
AND LOCATE('shared_overflow', generation_expression) > 0)
|
||
AND EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = @schema_name AND table_name = 'wh_location_occupancy'
|
||
AND column_name = 'active_pallet_occupancy'),
|
||
0, 1);
|
||
SET @sql = IF(@occupancy_needs_upgrade = 1 AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_occupancy' AND index_name = 'uk_location_active_occupancy') > 0,
|
||
'ALTER TABLE wh_location_occupancy DROP INDEX uk_location_active_occupancy', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF(@occupancy_needs_upgrade = 1 AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_occupancy' AND index_name = 'uk_pallet_active_occupancy') > 0,
|
||
'ALTER TABLE wh_location_occupancy DROP INDEX uk_pallet_active_occupancy', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
DROP PROCEDURE IF EXISTS add_location_overflow_column_if_missing;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE add_location_overflow_column_if_missing(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_column_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @column_sql = CONCAT(
|
||
'ALTER TABLE `', REPLACE(p_table_name, '`', '``'),
|
||
'` ADD COLUMN `', REPLACE(p_column_name, '`', '``'), '` ', p_column_definition
|
||
);
|
||
PREPARE column_statement FROM @column_sql;
|
||
EXECUTE column_statement;
|
||
DEALLOCATE PREPARE column_statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL add_location_overflow_column_if_missing(
|
||
'wh_location_reservation', 'active_pallet_reservation',
|
||
'TINYINT GENERATED ALWAYS AS (CASE WHEN reservation_status = ''RESERVED'' THEN 1 ELSE NULL END) STORED COMMENT ''托盘活动预占唯一标记(包含 A0)'''
|
||
);
|
||
CALL add_location_overflow_column_if_missing(
|
||
'wh_location_occupancy', 'active_location_occupancy',
|
||
'TINYINT GENERATED ALWAYS AS (CASE WHEN occupancy_status = ''OCCUPIED'' AND shared_overflow = 0 THEN 1 ELSE NULL END) STORED COMMENT ''普通库位活动占用唯一标记'''
|
||
);
|
||
CALL add_location_overflow_column_if_missing(
|
||
'wh_location_occupancy', 'active_pallet_occupancy',
|
||
'TINYINT GENERATED ALWAYS AS (CASE WHEN occupancy_status = ''OCCUPIED'' THEN 1 ELSE NULL END) STORED COMMENT ''托盘活动占用唯一标记(包含 A0)'''
|
||
);
|
||
DROP PROCEDURE IF EXISTS add_location_overflow_column_if_missing;
|
||
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_reservation' AND index_name = 'uk_pallet_active_reservation') = 0,
|
||
'ALTER TABLE wh_location_reservation ADD UNIQUE KEY uk_pallet_active_reservation (tenant_id, pallet_id, active_pallet_reservation)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_occupancy' AND index_name = 'uk_location_active_occupancy') = 0,
|
||
'ALTER TABLE wh_location_occupancy ADD UNIQUE KEY uk_location_active_occupancy (tenant_id, location_id, active_location_occupancy)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_location_occupancy' AND index_name = 'uk_pallet_active_occupancy') = 0,
|
||
'ALTER TABLE wh_location_occupancy ADD UNIQUE KEY uk_pallet_active_occupancy (tenant_id, pallet_id, active_pallet_occupancy)', 'SELECT 1');
|
||
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
-- 托盘、库位、预占和占用均为平台公共仓储资源,存量测试数据统一迁移到平台作用域 0。
|
||
UPDATE wh_pallet SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_location SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_location_reservation SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
UPDATE wh_location_occupancy SET tenant_id = 0 WHERE tenant_id <> 0;
|
||
|
||
-- 托盘编码在平台公共作用域全局唯一;包含逻辑删除记录,编码不复用。
|
||
DROP PROCEDURE IF EXISTS assert_pallet_code_globally_unique;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_pallet_code_globally_unique()
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM wh_pallet GROUP BY tenant_id, pallet_code HAVING COUNT(*) > 1) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '托盘编码迁移失败:不同仓库存在重复编码,请先清理';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_pallet_code_globally_unique();
|
||
DROP PROCEDURE IF EXISTS assert_pallet_code_globally_unique;
|
||
SET @pallet_code_index_sql = IF((SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'wh_pallet' AND index_name = 'uk_pallet_tenant_warehouse_code') > 0,
|
||
'ALTER TABLE wh_pallet DROP INDEX uk_pallet_tenant_warehouse_code', 'SELECT 1');
|
||
PREPARE pallet_code_index_stmt FROM @pallet_code_index_sql; EXECUTE pallet_code_index_stmt; DEALLOCATE PREPARE pallet_code_index_stmt;
|
||
SET @pallet_code_index_sql = IF((SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'wh_pallet' AND index_name = 'uk_pallet_tenant_code') = 0,
|
||
'ALTER TABLE wh_pallet ADD UNIQUE KEY uk_pallet_tenant_code (tenant_id, pallet_code)', 'SELECT 1');
|
||
PREPARE pallet_code_index_stmt FROM @pallet_code_index_sql; EXECUTE pallet_code_index_stmt; DEALLOCATE PREPARE pallet_code_index_stmt;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase2_public_resource_scope;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase2_public_resource_scope()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM wh_pallet WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_location WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_location_reservation WHERE tenant_id <> 0) > 0
|
||
OR (SELECT COUNT(*) FROM wh_location_occupancy WHERE tenant_id <> 0) > 0 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = 'Warehouse 二期迁移失败:托盘、库位、预占或占用仍有 tenant_id<>0';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM wh_pallet p JOIN wh_warehouse w ON w.id = p.warehouse_id
|
||
WHERE p.tenant_id <> w.tenant_id
|
||
) OR EXISTS (
|
||
SELECT 1 FROM wh_location l JOIN wh_warehouse w ON w.id = l.warehouse_id
|
||
WHERE l.tenant_id <> w.tenant_id
|
||
) OR EXISTS (
|
||
SELECT 1 FROM wh_location_reservation r
|
||
JOIN wh_warehouse w ON w.id = r.warehouse_id
|
||
JOIN wh_pallet p ON p.id = r.pallet_id
|
||
JOIN wh_location l ON l.id = r.location_id
|
||
WHERE r.tenant_id <> w.tenant_id OR r.tenant_id <> p.tenant_id OR r.tenant_id <> l.tenant_id
|
||
) OR EXISTS (
|
||
SELECT 1 FROM wh_location_occupancy o
|
||
JOIN wh_warehouse w ON w.id = o.warehouse_id
|
||
JOIN wh_pallet p ON p.id = o.pallet_id
|
||
JOIN wh_location l ON l.id = o.location_id
|
||
JOIN wh_location_reservation r ON r.id = o.reservation_id
|
||
WHERE o.tenant_id <> w.tenant_id OR o.tenant_id <> p.tenant_id
|
||
OR o.tenant_id <> l.tenant_id OR o.tenant_id <> r.tenant_id
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = 'Warehouse 二期迁移失败:公共仓储资源父子归属不一致';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase2_public_resource_scope();
|
||
DROP PROCEDURE IF EXISTS assert_phase2_public_resource_scope;
|
||
|
||
-- ============================================================================
|
||
-- 来源:order\src\main\resources\db\phase2-order-carrier-migration.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_order;
|
||
|
||
SET @carrier_id_exists := (
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'ord_order' AND column_name = 'carrier_id'
|
||
);
|
||
SET @carrier_id_sql := IF(@carrier_id_exists = 0,
|
||
'ALTER TABLE ord_order ADD COLUMN carrier_id BIGINT UNSIGNED NULL COMMENT ''船司逻辑ID快照'' AFTER channel_name',
|
||
'SELECT 1');
|
||
PREPARE carrier_id_statement FROM @carrier_id_sql;
|
||
EXECUTE carrier_id_statement;
|
||
DEALLOCATE PREPARE carrier_id_statement;
|
||
|
||
SET @carrier_name_exists := (
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'ord_order' AND column_name = 'carrier_name'
|
||
);
|
||
SET @carrier_name_sql := IF(@carrier_name_exists = 0,
|
||
'ALTER TABLE ord_order ADD COLUMN carrier_name VARCHAR(128) NULL COMMENT ''船司名称快照'' AFTER carrier_id',
|
||
'SELECT 1');
|
||
PREPARE carrier_name_statement FROM @carrier_name_sql;
|
||
EXECUTE carrier_name_statement;
|
||
DEALLOCATE PREPARE carrier_name_statement;
|
||
|
||
-- 客户提交时由 Order 固定入仓码;审核通过后 Firstleg 沿用该码创建正式入库单。
|
||
SET @inbound_code_exists := (
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'ord_order' AND column_name = 'inbound_code'
|
||
);
|
||
SET @inbound_code_sql := IF(@inbound_code_exists = 0,
|
||
'ALTER TABLE ord_order ADD COLUMN inbound_code VARCHAR(40) NULL COMMENT ''客户提交后生成的入仓码,草稿为空'' AFTER order_no',
|
||
'SELECT 1');
|
||
PREPARE inbound_code_statement FROM @inbound_code_sql;
|
||
EXECUTE inbound_code_statement;
|
||
DEALLOCATE PREPARE inbound_code_statement;
|
||
|
||
SET @inbound_code_index_exists := (
|
||
SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'ord_order'
|
||
AND index_name = 'uk_order_tenant_inbound_code'
|
||
);
|
||
SET @inbound_code_index_sql := IF(@inbound_code_index_exists = 0,
|
||
'ALTER TABLE ord_order ADD UNIQUE KEY uk_order_tenant_inbound_code (tenant_id, inbound_code)',
|
||
'SELECT 1');
|
||
PREPARE inbound_code_index_statement FROM @inbound_code_index_sql;
|
||
EXECUTE inbound_code_index_statement;
|
||
DEALLOCATE PREPARE inbound_code_index_statement;
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-inbound-schema.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_firstleg
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_firstleg;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
task_id BIGINT UNSIGNED NULL COMMENT '头程履约任务ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID,不建立跨库外键',
|
||
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '租户内唯一入仓码',
|
||
process_status VARCHAR(24) NOT NULL DEFAULT 'WAIT_MEASURE' COMMENT '入库流程状态',
|
||
order_status_snapshot VARCHAR(24) NOT NULL COMMENT '订单状态快照',
|
||
measure_review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '测量数据审核状态',
|
||
fee_status VARCHAR(24) NOT NULL DEFAULT 'UNPRICED' COMMENT '费用业务状态',
|
||
fee_review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '费用审核状态',
|
||
notification_status VARCHAR(24) NOT NULL DEFAULT 'NOT_CREATED' COMMENT '客户通知状态',
|
||
expected_arrival_at DATETIME(3) NULL COMMENT '预计到达时间',
|
||
customer_id BIGINT UNSIGNED NULL COMMENT '客户逻辑ID',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||
company_id BIGINT UNSIGNED NULL COMMENT '运营公司逻辑ID',
|
||
company_name VARCHAR(128) NULL COMMENT '运营公司名称快照',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '实际作业仓库ID',
|
||
warehouse_name VARCHAR(128) NOT NULL COMMENT '实际作业仓库名称快照',
|
||
delivery_warehouse_id BIGINT UNSIGNED NULL COMMENT '交货仓逻辑ID',
|
||
delivery_warehouse_name VARCHAR(128) NULL COMMENT '交货仓名称快照',
|
||
business_type VARCHAR(32) NOT NULL COMMENT '业务类型',
|
||
channel_code VARCHAR(64) NULL COMMENT '渠道编码',
|
||
channel_name VARCHAR(128) NULL COMMENT '渠道名称快照',
|
||
carrier_id BIGINT UNSIGNED NULL COMMENT '计费船司逻辑ID',
|
||
carrier_name VARCHAR(128) NULL COMMENT '计费船司名称快照',
|
||
tax_mode VARCHAR(24) NULL COMMENT '买单或退税模式',
|
||
pricing_tax_mode VARCHAR(16) NULL COMMENT '报价包税维度:TAX_INCLUDED/TAX_EXCLUDED',
|
||
container_mode VARCHAR(24) NULL COMMENT '整柜或散货模式',
|
||
customs_inspection TINYINT(1) NULL COMMENT '是否商检',
|
||
destination_type VARCHAR(24) NULL COMMENT '目的仓类型',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓逻辑ID',
|
||
destination_code VARCHAR(32) NULL COMMENT '目的仓编码,例如FBA仓编码',
|
||
destination_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
hold_flag TINYINT(1) NOT NULL DEFAULT 0 COMMENT '当前是否扣货',
|
||
current_hold_reason VARCHAR(500) NULL COMMENT '当前扣货理由快照',
|
||
total_package_count INT UNSIGNED NULL COMMENT '确认后的总箱数',
|
||
actual_gross_weight_kg DECIMAL(18,3) NULL COMMENT '确认后的实际总毛重kg',
|
||
chargeable_weight_kg DECIMAL(18,3) NULL COMMENT '当前费用快照的计费重量kg',
|
||
actual_volume_cbm DECIMAL(18,4) NULL COMMENT '确认后的实际总体积m3',
|
||
chargeable_volume_cbm DECIMAL(18,4) NULL COMMENT '当前费用快照的计费体积m3',
|
||
pallet_count INT UNSIGNED NULL COMMENT '已分配总托盘数快照',
|
||
volume_weight_kg DECIMAL(18,3) NULL COMMENT '总材积重kg',
|
||
actual_net_weight_kg DECIMAL(18,3) NULL COMMENT '总净重kg',
|
||
measure_completed_at DATETIME(3) NULL COMMENT '测量完成时间',
|
||
inbounded_at DATETIME(3) NULL COMMENT '入库完成时间',
|
||
express_company_code VARCHAR(64) NULL COMMENT '快递公司编码快照',
|
||
express_company_name VARCHAR(128) NULL COMMENT '快递公司名称快照',
|
||
express_tracking_no VARCHAR(128) NULL COMMENT '快递单号快照',
|
||
public_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_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_tenant_code (tenant_id, inbound_code),
|
||
UNIQUE KEY uk_inbound_tenant_order (tenant_id, order_id),
|
||
KEY idx_inbound_process_time (tenant_id, process_status, created_at, id),
|
||
KEY idx_inbound_measure_time (tenant_id, measure_review_status, created_at, id),
|
||
KEY idx_inbound_fee_time (tenant_id, fee_review_status, created_at, id),
|
||
KEY idx_inbound_warehouse_status (tenant_id, warehouse_id, process_status, created_at),
|
||
KEY idx_inbound_customer_time (tenant_id, customer_id, created_at),
|
||
KEY idx_inbound_company_time (tenant_id, company_id, created_at),
|
||
KEY idx_inbound_arrival (tenant_id, expected_arrival_at),
|
||
KEY idx_inbound_destination (tenant_id, destination_code, created_at),
|
||
KEY idx_inbound_channel (tenant_id, channel_code, created_at),
|
||
CONSTRAINT fk_inbound_task FOREIGN KEY (task_id) REFERENCES fl_fulfillment_task (id)
|
||
) ENGINE=InnoDB COMMENT='二期头程入库单聚合根和列表查询快照';
|
||
|
||
UPDATE baiyue_order.ord_order o
|
||
LEFT JOIN baiyue_firstleg.fl_inbound_order i
|
||
ON i.tenant_id = o.tenant_id AND i.order_id = o.id AND i.deleted = 0
|
||
SET o.inbound_code = COALESCE(i.inbound_code, CONCAT('RK-', o.id))
|
||
WHERE o.deleted = 0
|
||
AND o.status <> 'DRAFT'
|
||
AND (o.submitted_at IS NOT NULL
|
||
OR o.status IN ('SUBMITTED', 'APPROVED', 'ACCEPTED', 'PROCESSING', 'COMPLETED'))
|
||
AND o.inbound_code IS NULL;
|
||
|
||
-- 从未提交的草稿或由草稿直接取消的订单不应拥有入仓码。
|
||
UPDATE baiyue_order.ord_order
|
||
SET inbound_code = NULL
|
||
WHERE deleted = 0
|
||
AND inbound_code IS NOT NULL
|
||
AND (status = 'DRAFT'
|
||
OR (status IN ('REJECTED', 'CANCELLED') AND submitted_at IS NULL));
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_pickup_snapshot (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '提货信息快照ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
pickup_method VARCHAR(24) NULL COMMENT '提货方式',
|
||
pickup_at DATETIME(3) NULL COMMENT '提货时间',
|
||
contact_name VARCHAR(64) NULL COMMENT '提货联系人',
|
||
contact_phone VARCHAR(32) NULL COMMENT '提货联系人电话',
|
||
contact_email VARCHAR(128) NULL COMMENT '提货联系人邮箱',
|
||
country_code CHAR(2) NULL COMMENT '国家二字码',
|
||
province VARCHAR(64) NULL COMMENT '省或州',
|
||
city VARCHAR(64) NULL COMMENT '城市',
|
||
district VARCHAR(64) NULL COMMENT '区县',
|
||
address_line VARCHAR(512) NULL COMMENT '提货详细地址',
|
||
postal_code VARCHAR(20) NULL COMMENT '提货邮编',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_pickup_inbound (tenant_id, inbound_id),
|
||
CONSTRAINT fk_pickup_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单提货信息不可变快照';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_destination_snapshot (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '目的仓地址快照ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
destination_type VARCHAR(24) NULL COMMENT '目的仓类型',
|
||
destination_code VARCHAR(32) NULL COMMENT '目的仓编码',
|
||
destination_name VARCHAR(128) NULL COMMENT '目的仓名称',
|
||
country_code CHAR(2) NULL COMMENT '国家二字码',
|
||
state_province VARCHAR(100) NULL COMMENT '省或州',
|
||
city VARCHAR(100) NULL COMMENT '城市',
|
||
address_line VARCHAR(512) NULL COMMENT '详细地址',
|
||
postal_code VARCHAR(20) NULL COMMENT '邮编',
|
||
contact_name VARCHAR(64) NULL COMMENT '联系人',
|
||
contact_phone VARCHAR(32) NULL COMMENT '联系人电话',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_destination_inbound (tenant_id, inbound_id),
|
||
CONSTRAINT fk_destination_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单目的仓地址和联系人快照';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_transport_snapshot (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '运输信息快照ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
loading_status VARCHAR(24) NULL COMMENT '配载状态快照',
|
||
container_no VARCHAR(64) NULL COMMENT '集装箱号',
|
||
cabinet_no VARCHAR(64) NULL COMMENT '柜号或内部柜单号',
|
||
vessel_name VARCHAR(128) NULL COMMENT '船名',
|
||
voyage_no VARCHAR(64) NULL COMMENT '航次',
|
||
sailing_at DATETIME(3) NULL COMMENT '开船时间',
|
||
port_arrival_at DATETIME(3) NULL COMMENT '到港时间',
|
||
source_updated_at DATETIME(3) NULL COMMENT '上游数据更新时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_transport_inbound (tenant_id, inbound_id),
|
||
KEY idx_transport_container (tenant_id, container_no),
|
||
KEY idx_transport_cabinet (tenant_id, cabinet_no),
|
||
KEY idx_transport_loading (tenant_id, loading_status, sailing_at),
|
||
CONSTRAINT fk_transport_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单关联运输信息只读快照';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_measurement (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '测量批次ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
source_type VARCHAR(24) NOT NULL COMMENT '数据来源:THIRD_PARTY或MANUAL',
|
||
source_code VARCHAR(64) NULL COMMENT '第三方应用或来源编码',
|
||
external_event_no VARCHAR(128) NULL COMMENT '第三方事件号',
|
||
external_request_hash CHAR(64) NULL COMMENT '第三方data原文和图片内容联合请求哈希',
|
||
device_code VARCHAR(64) NULL COMMENT '测量设备编码',
|
||
warehouse_code VARCHAR(50) NULL COMMENT '第三方回传仓库编码',
|
||
measured_at DATETIME(3) NULL COMMENT '设备测量时间',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'RECEIVED' COMMENT '测量批次状态',
|
||
raw_payload_json JSON NULL COMMENT '第三方原始报文',
|
||
original_document_no VARCHAR(64) NULL COMMENT '原单据号码快照',
|
||
original_gross_weight_kg DECIMAL(18,3) NULL COMMENT '原单据毛重kg',
|
||
original_volume_cbm DECIMAL(18,4) NULL COMMENT '原单据体积或材积m3',
|
||
actual_goods_name VARCHAR(255) NULL COMMENT '实际品名',
|
||
total_package_count INT UNSIGNED NULL COMMENT '本批次总箱数',
|
||
total_volume_raw_cbm DECIMAL(18,6) NULL COMMENT '本批次原始总体积m3',
|
||
total_volume_cbm DECIMAL(18,4) NULL COMMENT '本批次业务总体积m3',
|
||
volume_weight_kg DECIMAL(18,3) NULL COMMENT '本批次总材积重kg',
|
||
created_by BIGINT UNSIGNED NULL COMMENT '创建人或系统主体ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_measure_external_event (tenant_id, source_code, external_event_no),
|
||
KEY idx_measure_inbound_time (tenant_id, inbound_id, created_at),
|
||
CONSTRAINT fk_measure_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库长宽高测量批次和第三方原始数据';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_measurement_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '测量明细ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
measurement_id BIGINT UNSIGNED NOT NULL COMMENT '测量批次ID',
|
||
inbound_detail_id BIGINT UNSIGNED NULL COMMENT '绑定的订单货物明细ID,平台手工明细可为空',
|
||
external_detail_no VARCHAR(128) NULL COMMENT '第三方测量明细唯一号,平台手工明细可为空',
|
||
box_code VARCHAR(128) NULL COMMENT '柏悦唛头箱号;设备单箱测量时同一客户内唯一',
|
||
scanned_shipment_id VARCHAR(64) NULL COMMENT '设备从Amazon FBA箱标实扫的Shipment ID',
|
||
item_no VARCHAR(32) NOT NULL COMMENT '批次内明细号',
|
||
box_specification VARCHAR(64) NOT NULL COMMENT '箱规标识',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '相同箱规箱数',
|
||
length_cm DECIMAL(12,3) NOT NULL COMMENT '原始长度cm',
|
||
width_cm DECIMAL(12,3) NOT NULL COMMENT '原始宽度cm',
|
||
height_cm DECIMAL(12,3) NOT NULL COMMENT '原始高度cm',
|
||
girth_cm DECIMAL(12,3) NOT NULL COMMENT '周长cm,规则为长+2*(宽+高)',
|
||
unit_weight_kg DECIMAL(18,3) NULL COMMENT '单箱重量kg,第三方仅回传尺寸时可为空',
|
||
unit_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '单箱业务体积m3',
|
||
volume_raw_cbm DECIMAL(18,6) NOT NULL COMMENT '原始体积m3',
|
||
volume_cbm DECIMAL(18,4) NOT NULL COMMENT '业务体积m3',
|
||
total_weight_kg DECIMAL(18,3) NULL COMMENT '本箱规总重量kg',
|
||
total_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '本箱规总体积m3',
|
||
volume_weight_kg DECIMAL(18,3) NOT NULL COMMENT '本箱规材积重kg',
|
||
calculation_rule_version VARCHAR(64) NOT NULL COMMENT '体积计算和舍入规则版本',
|
||
source_image_file_id BIGINT UNSIGNED NULL COMMENT '测量设备图片文件ID',
|
||
machine_no VARCHAR(64) NULL COMMENT '本次测量设备编号',
|
||
device_measured_at DATETIME(3) NULL COMMENT '本次设备测量时间',
|
||
over_limit_warning VARCHAR(500) NULL COMMENT '超线提醒快照',
|
||
outer_length_cm DECIMAL(12,3) NULL COMMENT '外箱规长度cm',
|
||
outer_width_cm DECIMAL(12,3) NULL COMMENT '外箱规宽度cm',
|
||
outer_height_cm DECIMAL(12,3) NULL COMMENT '外箱规高度cm',
|
||
remark VARCHAR(500) NULL COMMENT '测量明细备注',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最后更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_measure_item_no (tenant_id, measurement_id, item_no),
|
||
UNIQUE KEY uk_measure_external_detail (tenant_id, inbound_detail_id, external_detail_no),
|
||
UNIQUE KEY uk_measure_box_code (tenant_id, box_code),
|
||
CONSTRAINT fk_measure_item_batch FOREIGN KEY (measurement_id) REFERENCES fl_inbound_measurement (id)
|
||
) ENGINE=InnoDB COMMENT='入库长宽高和箱数测量明细';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_mojia_measurement_receipt (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '墨家联调收件记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
subject_id BIGINT UNSIGNED NOT NULL COMMENT '设备应用操作主体ID',
|
||
box_code VARCHAR(128) NOT NULL COMMENT '设备扫描的原始箱码',
|
||
scanned_shipment_id VARCHAR(64) NULL COMMENT '设备从Amazon FBA箱标实扫的Shipment ID',
|
||
length_cm DECIMAL(12,3) NOT NULL COMMENT '设备实测长度cm',
|
||
width_cm DECIMAL(12,3) NOT NULL COMMENT '设备实测宽度cm',
|
||
height_cm DECIMAL(12,3) NOT NULL COMMENT '设备实测高度cm',
|
||
weight_kg DECIMAL(18,3) NULL COMMENT '设备实测重量kg',
|
||
machine_no VARCHAR(64) NOT NULL COMMENT '设备编号',
|
||
measured_at DATETIME(3) NOT NULL COMMENT '设备测量时间或服务接收时间',
|
||
image_file_id BIGINT UNSIGNED NOT NULL COMMENT 'File服务中的墨家联调图片ID',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'UNMATCHED' COMMENT '收件状态',
|
||
match_failure_code VARCHAR(16) NOT NULL COMMENT '正式业务匹配失败码',
|
||
match_failure_reason VARCHAR(500) NOT NULL COMMENT '脱敏后的匹配失败原因',
|
||
request_id VARCHAR(128) NULL COMMENT '请求链路ID',
|
||
trace_id VARCHAR(128) NULL COMMENT '调用链ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_mojia_receipt_status_time (tenant_id, status, created_at),
|
||
KEY idx_mojia_receipt_box_time (tenant_id, box_code, created_at)
|
||
) ENGINE=InnoDB COMMENT='墨家模拟或未匹配入库单的原始测量收件箱';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_carton_label (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '实际箱唛头映射ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '当前客户隔离ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID快照',
|
||
inbound_detail_id BIGINT UNSIGNED NOT NULL COMMENT '订单货物明细ID,不建立跨库外键',
|
||
cargo_item_no INT UNSIGNED NOT NULL COMMENT '订单货物明细序号快照',
|
||
cargo_name VARCHAR(255) NULL COMMENT '货物名称快照',
|
||
detail_package_count INT UNSIGNED NOT NULL COMMENT '该订单货物明细箱数快照',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
box_code VARCHAR(80) NOT NULL COMMENT '固定二维码内容:入仓码#总箱数-箱下标',
|
||
total_carton_count INT UNSIGNED NOT NULL COMMENT '首次分配时整单总箱数',
|
||
carton_index INT UNSIGNED NOT NULL COMMENT '整单从1开始的稳定箱下标',
|
||
box_specification VARCHAR(255) NOT NULL COMMENT '订单箱规或货物明细描述快照',
|
||
length_cm DECIMAL(12,3) NULL COMMENT '下单箱规长度cm快照',
|
||
width_cm DECIMAL(12,3) NULL COMMENT '下单箱规宽度cm快照',
|
||
height_cm DECIMAL(12,3) NULL COMMENT '下单箱规高度cm快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '首次分配时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_carton_label_box_code (tenant_id, box_code),
|
||
UNIQUE KEY uk_carton_label_index (tenant_id, inbound_id, carton_index),
|
||
KEY idx_carton_label_detail (tenant_id, inbound_id, inbound_detail_id),
|
||
CONSTRAINT fk_carton_label_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='首次打印前冻结的实际箱号与订单货物明细映射';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_weight_result (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量结果ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
file_id BIGINT UNSIGNED NOT NULL COMMENT '称重图片文件ID',
|
||
ocr_provider VARCHAR(64) NULL COMMENT 'OCR服务商编码',
|
||
ocr_raw_text VARCHAR(1000) NULL COMMENT 'OCR原始文本',
|
||
ocr_weight_value DECIMAL(18,3) NULL COMMENT 'OCR识别原始重量值',
|
||
ocr_weight_unit VARCHAR(8) NULL COMMENT 'OCR识别原始单位',
|
||
confidence DECIMAL(6,5) NULL COMMENT '识别置信度',
|
||
adjusted_weight_value DECIMAL(18,3) NULL COMMENT '人工修正后的原单位重量',
|
||
adjusted_weight_unit VARCHAR(8) NULL COMMENT '人工修正后的单位',
|
||
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认并统一换算后的重量kg',
|
||
recognition_status VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT '识别和确认状态',
|
||
adjustment_reason VARCHAR(500) NULL COMMENT '人工修正原因',
|
||
adjusted_by BIGINT UNSIGNED NULL COMMENT '修正人ID',
|
||
adjusted_at DATETIME(3) NULL COMMENT '修正时间',
|
||
confirmed_by BIGINT UNSIGNED NULL COMMENT '确认人ID',
|
||
confirmed_at DATETIME(3) NULL COMMENT '确认时间',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_weight_inbound_file (tenant_id, inbound_id, file_id),
|
||
KEY idx_weight_inbound_status (tenant_id, inbound_id, recognition_status),
|
||
CONSTRAINT fk_weight_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='称重图片OCR原值、人工修正值和确认重量';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量修订记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
weight_result_id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量结果ID',
|
||
revision_no INT UNSIGNED NOT NULL COMMENT '结果版本号,与乐观锁版本一致',
|
||
action VARCHAR(24) NOT NULL COMMENT '动作:CORRECT或CONFIRM',
|
||
before_weight_value DECIMAL(18,3) NULL COMMENT '变更前原单位重量',
|
||
before_weight_unit VARCHAR(8) NULL COMMENT '变更前单位',
|
||
after_weight_value DECIMAL(18,3) NULL COMMENT '变更后原单位重量',
|
||
after_weight_unit VARCHAR(8) NULL COMMENT '变更后单位',
|
||
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的重量kg',
|
||
reason VARCHAR(500) NULL COMMENT '人工修正原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
operated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_weight_revision_no (tenant_id, weight_result_id, revision_no),
|
||
KEY idx_weight_revision_time (tenant_id, inbound_id, operated_at),
|
||
CONSTRAINT fk_weight_revision_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_weight_revision_result FOREIGN KEY (weight_result_id) REFERENCES fl_inbound_weight_result (id)
|
||
) ENGINE=InnoDB COMMENT='OCR重量人工修正和确认的不可变版本历史';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_measure_review_record (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '测量审核记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
measurement_id BIGINT UNSIGNED NULL COMMENT '被审核的测量批次ID',
|
||
review_result VARCHAR(24) NOT NULL COMMENT '审核结果',
|
||
review_reason VARCHAR(1000) NULL COMMENT '审核或驳回原因',
|
||
exception_release TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否异常放行',
|
||
release_reason VARCHAR(1000) NULL COMMENT '异常放行原因',
|
||
allow_rejected_config TINYINT(1) NOT NULL DEFAULT 0 COMMENT '操作时Nacos异常放行开关值',
|
||
reviewer_id BIGINT UNSIGNED NOT NULL COMMENT '审核人ID',
|
||
reviewer_name VARCHAR(64) NULL COMMENT '审核人名称快照',
|
||
reviewed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '审核时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_review_inbound_time (tenant_id, inbound_id, reviewed_at),
|
||
KEY idx_review_exception_release (tenant_id, inbound_id, exception_release, reviewed_at),
|
||
CONSTRAINT fk_review_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_review_measurement FOREIGN KEY (measurement_id) REFERENCES fl_inbound_measurement (id)
|
||
) ENGINE=InnoDB COMMENT='测量审核、驳回和异常放行不可变记录';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_customer_charge_rule (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '客户计费规则ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
rule_code VARCHAR(50) NOT NULL COMMENT '租户内规则编码',
|
||
rule_version INT UNSIGNED NOT NULL COMMENT '规则版本号',
|
||
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户逻辑ID',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||
business_type VARCHAR(32) NOT NULL COMMENT '业务类型',
|
||
charge_mode VARCHAR(16) NOT NULL COMMENT '计费口径:KG或CBM',
|
||
tax_mode VARCHAR(24) NULL COMMENT '包税或不包税类型',
|
||
conversion_factor DECIMAL(18,6) NOT NULL COMMENT '重量体积换算系数',
|
||
rounding_scale TINYINT UNSIGNED NOT NULL DEFAULT 3 COMMENT '结果保留小数位数',
|
||
rounding_mode VARCHAR(24) NOT NULL DEFAULT 'CEILING' COMMENT '舍入方式',
|
||
currency CHAR(3) NOT NULL COMMENT '默认币种',
|
||
effective_from DATETIME(3) NOT NULL COMMENT '生效开始时间',
|
||
effective_to DATETIME(3) NULL COMMENT '生效结束时间',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '规则状态',
|
||
remark VARCHAR(500) NULL COMMENT '规则备注',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_charge_rule_version (tenant_id, rule_code, rule_version),
|
||
KEY idx_charge_rule_match (tenant_id, customer_id, business_type, status, effective_from, effective_to)
|
||
) ENGINE=InnoDB COMMENT='客户头程计费规则版本';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_carrier_fba_rate (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '船司FBA价格ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台价格作用域ID,船司价格固定为0',
|
||
price_book_version_id BIGINT UNSIGNED NULL COMMENT '整表价格版本ID;历史兼容价格可为空',
|
||
rate_code VARCHAR(50) NOT NULL COMMENT '平台公共价格编码',
|
||
rate_version INT UNSIGNED NOT NULL COMMENT '价格版本号',
|
||
origin_warehouse_id BIGINT UNSIGNED NULL COMMENT '确认前可为空;确认后绑定平台公共起运仓逻辑ID',
|
||
origin_warehouse_code VARCHAR(64) NULL COMMENT '起运仓稳定编码',
|
||
origin_warehouse_name VARCHAR(128) NOT NULL COMMENT '起运仓名称快照',
|
||
business_type VARCHAR(32) NOT NULL COMMENT '业务或计费类型',
|
||
channel_code VARCHAR(64) NULL COMMENT '渠道编码',
|
||
price_model VARCHAR(32) NOT NULL DEFAULT 'FBA_WAREHOUSE_RATE' COMMENT '价格模型',
|
||
carrier_id BIGINT UNSIGNED NULL COMMENT '船司逻辑ID',
|
||
carrier_name VARCHAR(128) NULL COMMENT '历史承运商名称;渠道价格可为空',
|
||
destination_type VARCHAR(16) NULL COMMENT '目的地类型:FBA或OVERSEAS;REGION_WEIGHT_TIER区域阶梯行为空',
|
||
destination_code VARCHAR(32) NULL COMMENT 'FBA目的仓编码;区域阶梯模型为空',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '平台海外仓稳定ID;不存环境专属ID',
|
||
destination_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
region_code VARCHAR(16) NULL COMMENT '区域阶梯模型区域编码',
|
||
pricing_tax_mode VARCHAR(16) NULL COMMENT '报价包税维度;独立于订单tax_mode',
|
||
weight_from_kg DECIMAL(18,6) NULL COMMENT '区域阶梯起始计费重量KG',
|
||
charge_unit VARCHAR(16) NOT NULL COMMENT '计费单位:KG或CBM',
|
||
unit_price DECIMAL(18,6) NOT NULL COMMENT '基础单价',
|
||
currency CHAR(3) NOT NULL COMMENT '币种',
|
||
additional_fee DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '固定附加费',
|
||
source_sheet_name VARCHAR(128) NULL COMMENT '原始Excel Sheet',
|
||
source_row_number INT UNSIGNED NULL COMMENT '原始Excel行号',
|
||
source_destination_text VARCHAR(1000) NULL COMMENT '原始组合仓库或区域文本',
|
||
effective_from DATETIME(3) NOT NULL COMMENT '生效开始时间',
|
||
effective_to DATETIME(3) NULL COMMENT '生效结束时间',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'ACTIVE' COMMENT '价格状态:PENDING_CONFIRMATION、ACTIVE或SUPERSEDED',
|
||
remark VARCHAR(500) NULL COMMENT '价格备注',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_fba_rate_version (tenant_id, rate_code, rate_version),
|
||
KEY idx_fba_rate_match (tenant_id, origin_warehouse_id, business_type, channel_code, carrier_id, destination_type, destination_code, destination_warehouse_id, charge_unit, status, effective_from, effective_to),
|
||
KEY idx_fba_rate_book (price_book_version_id, channel_code, origin_warehouse_id, price_model, status),
|
||
KEY idx_fba_rate_destination (price_book_version_id, channel_code, origin_warehouse_id, destination_type, destination_warehouse_id, destination_code),
|
||
KEY idx_fba_rate_region (price_book_version_id, channel_code, origin_warehouse_id, region_code, weight_from_kg)
|
||
) ENGINE=InnoDB COMMENT='船司FBA目的仓价格版本';
|
||
|
||
ALTER TABLE fl_carrier_fba_rate
|
||
MODIFY COLUMN status VARCHAR(24) NOT NULL DEFAULT 'ACTIVE'
|
||
COMMENT '价格状态:PENDING_CONFIRMATION、ACTIVE或SUPERSEDED';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_fee (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '入库费用ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
fee_no VARCHAR(40) NOT NULL COMMENT '租户内唯一费用单号',
|
||
fee_version INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '费用快照版本',
|
||
charge_rule_id BIGINT UNSIGNED NULL COMMENT '历史客户计费规则ID;渠道价格可为空',
|
||
charge_rule_version INT UNSIGNED NULL COMMENT '历史客户规则版本快照',
|
||
carrier_rate_id BIGINT UNSIGNED NOT NULL COMMENT '船司FBA价格ID',
|
||
carrier_rate_version INT UNSIGNED NOT NULL COMMENT '船司价格版本快照',
|
||
price_book_version_id BIGINT UNSIGNED NULL COMMENT '整表价格版本ID快照',
|
||
rule_code VARCHAR(50) NULL COMMENT '历史客户规则编码快照',
|
||
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID快照',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||
business_type VARCHAR(32) NOT NULL COMMENT '业务类型快照',
|
||
tax_mode VARCHAR(24) NULL COMMENT '订单买单或退税模式快照',
|
||
pricing_tax_mode VARCHAR(16) NULL COMMENT '报价包税维度快照',
|
||
rounding_scale TINYINT UNSIGNED NULL COMMENT '历史客户规则计费量保留位数快照',
|
||
rounding_mode VARCHAR(24) NULL COMMENT '历史客户规则舍入方式快照',
|
||
rule_effective_from DATETIME(3) NULL COMMENT '历史客户规则生效开始快照',
|
||
rule_effective_to DATETIME(3) NULL COMMENT '客户规则生效结束快照',
|
||
rate_code VARCHAR(50) NOT NULL COMMENT '船司价格编码快照',
|
||
origin_warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '起运仓ID快照',
|
||
origin_warehouse_name VARCHAR(128) NOT NULL COMMENT '起运仓名称快照',
|
||
channel_code VARCHAR(64) NOT NULL COMMENT '渠道编码快照',
|
||
carrier_id BIGINT UNSIGNED NULL COMMENT '历史承运商ID;渠道价格可为空',
|
||
carrier_name VARCHAR(128) NULL COMMENT '历史承运商名称;渠道价格可为空',
|
||
destination_type VARCHAR(16) NULL COMMENT '目的地类型快照:FBA或OVERSEAS',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '海外仓或FBA目的仓ID快照',
|
||
destination_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
destination_code VARCHAR(32) NULL COMMENT 'FBA仓编码快照;海外仓可为空',
|
||
rate_effective_from DATETIME(3) NOT NULL COMMENT '价格生效开始快照',
|
||
rate_effective_to DATETIME(3) NULL COMMENT '价格生效结束快照',
|
||
charge_mode VARCHAR(16) NULL COMMENT '计费口径:KG或CBM',
|
||
conversion_factor DECIMAL(18,6) NULL COMMENT '历史客户规则使用的换算系数快照',
|
||
actual_weight_kg DECIMAL(18,3) NOT NULL COMMENT '试算时实际重量kg',
|
||
actual_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '试算时实际体积m3',
|
||
converted_quantity DECIMAL(18,4) NOT NULL COMMENT '换算后的重量或体积',
|
||
chargeable_quantity DECIMAL(18,4) NOT NULL COMMENT '最终计费量',
|
||
charge_unit VARCHAR(16) NOT NULL COMMENT '计费单位',
|
||
currency CHAR(3) NOT NULL COMMENT '币种',
|
||
subtotal_amount DECIMAL(18,2) NOT NULL COMMENT '基础费用金额',
|
||
additional_amount DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '附加费金额',
|
||
total_amount DECIMAL(18,2) NOT NULL COMMENT '总金额',
|
||
fee_status VARCHAR(24) NOT NULL DEFAULT 'CALCULATED' COMMENT '费用状态',
|
||
review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '费用审核状态',
|
||
exception_release TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否基于测量异常放行生成',
|
||
calculated_by BIGINT UNSIGNED NOT NULL COMMENT '试算人ID',
|
||
calculated_at DATETIME(3) NOT NULL COMMENT '试算时间',
|
||
confirmed_by BIGINT UNSIGNED NULL COMMENT '确认人ID',
|
||
confirmed_at DATETIME(3) NULL COMMENT '确认时间',
|
||
sent_by BIGINT UNSIGNED NULL COMMENT '发送人ID',
|
||
sent_at DATETIME(3) NULL COMMENT '发送客户时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_fee_no (tenant_id, fee_no),
|
||
UNIQUE KEY uk_inbound_fee_version (tenant_id, inbound_id, fee_version),
|
||
KEY idx_inbound_fee_status (tenant_id, inbound_id, fee_status, review_status),
|
||
CONSTRAINT fk_fee_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_fee_charge_rule FOREIGN KEY (charge_rule_id) REFERENCES fl_customer_charge_rule (id),
|
||
CONSTRAINT fk_fee_carrier_rate FOREIGN KEY (carrier_rate_id) REFERENCES fl_carrier_fba_rate (id)
|
||
) ENGINE=InnoDB COMMENT='入库头程费用计算和确认快照';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_fee_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '费用明细ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
fee_id BIGINT UNSIGNED NOT NULL COMMENT '费用头ID',
|
||
item_no INT UNSIGNED NOT NULL COMMENT '费用单内序号',
|
||
fee_code VARCHAR(50) NOT NULL COMMENT '费用项目编码',
|
||
fee_name VARCHAR(128) NOT NULL COMMENT '费用项目名称快照',
|
||
quantity DECIMAL(18,4) NOT NULL COMMENT '计费数量',
|
||
unit VARCHAR(16) NOT NULL COMMENT '计费单位',
|
||
unit_price DECIMAL(18,6) NOT NULL COMMENT '单价',
|
||
amount DECIMAL(18,2) NOT NULL COMMENT '金额',
|
||
currency CHAR(3) NOT NULL COMMENT '币种',
|
||
automatic_flag TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否自动计算',
|
||
remark VARCHAR(500) NULL COMMENT '费用备注',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_fee_item_no (tenant_id, fee_id, item_no),
|
||
CONSTRAINT fk_fee_item_header FOREIGN KEY (fee_id) REFERENCES fl_inbound_fee (id)
|
||
) ENGINE=InnoDB COMMENT='入库头程费用项目明细';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_pallet_allocation (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '头程托盘分配引用ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
allocation_no INT UNSIGNED NOT NULL COMMENT '入库单内分配序号',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '仓库服务仓库ID',
|
||
warehouse_resource_tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓储资源固定使用 0;tenant_id 仍是客户隔离键',
|
||
pallet_id BIGINT UNSIGNED NOT NULL COMMENT '仓库服务托盘ID',
|
||
pallet_code VARCHAR(50) NOT NULL COMMENT '托盘编码快照',
|
||
pallet_capacity_cbm DECIMAL(18,4) NOT NULL COMMENT '托盘可用容积快照',
|
||
allocated_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '本托盘分配体积',
|
||
allocated_package_count INT UNSIGNED NULL COMMENT '本托盘分配箱数',
|
||
location_id BIGINT UNSIGNED NOT NULL COMMENT '仓库服务库位ID',
|
||
location_code VARCHAR(50) NOT NULL COMMENT '库位编码快照',
|
||
reservation_no VARCHAR(64) NOT NULL COMMENT '仓库库位预占单号',
|
||
allocation_status VARCHAR(24) NOT NULL DEFAULT 'RESERVED' COMMENT '分配状态',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_pallet_allocation_no (tenant_id, inbound_id, allocation_no),
|
||
UNIQUE KEY uk_pallet_allocation_pallet (tenant_id, inbound_id, pallet_id),
|
||
KEY idx_allocation_location (tenant_id, warehouse_id, location_id, allocation_status),
|
||
CONSTRAINT fk_allocation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='头程侧托盘、库位和仓库预占结果引用';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_file (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '入库单文件关系ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
file_id BIGINT UNSIGNED NOT NULL COMMENT '文件服务文件ID',
|
||
file_type VARCHAR(24) NOT NULL COMMENT '文件类型:RELATED、MARK、PC_ORDER、MEASURE或WEIGHT',
|
||
file_name_snapshot VARCHAR(255) NOT NULL COMMENT '文件名快照',
|
||
visibility VARCHAR(16) NOT NULL DEFAULT 'INTERNAL' COMMENT '可见范围',
|
||
file_version INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '业务文件版本',
|
||
uploaded_by BIGINT UNSIGNED NOT NULL COMMENT '上传人ID',
|
||
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '上传时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_file_relation (tenant_id, inbound_id, file_id, file_type),
|
||
KEY idx_inbound_file_type (tenant_id, inbound_id, file_type, deleted),
|
||
CONSTRAINT fk_file_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单与文件中心对象的分类关系';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_hold_record (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '扣货记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
hold_action VARCHAR(16) NOT NULL COMMENT '动作:HOLD或RELEASE',
|
||
reason VARCHAR(1000) NOT NULL COMMENT '扣货或解除原因',
|
||
evidence_file_id BIGINT UNSIGNED NULL COMMENT '证据文件ID',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
operated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_hold_inbound_time (tenant_id, inbound_id, operated_at),
|
||
CONSTRAINT fk_hold_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单扣货和解除扣货不可变历史';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_internal_note (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '客服内部备注ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
content VARCHAR(2000) NOT 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 '创建时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_internal_note_time (tenant_id, inbound_id, created_at),
|
||
CONSTRAINT fk_internal_note_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单客服内部备注历史';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_status_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '入库状态历史ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
from_status VARCHAR(24) NULL COMMENT '变更前状态',
|
||
to_status VARCHAR(24) NOT NULL COMMENT '变更后状态',
|
||
action VARCHAR(32) NOT NULL COMMENT '触发动作',
|
||
reason VARCHAR(1000) NULL COMMENT '变更原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
occurred_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '发生时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_inbound_history_time (tenant_id, inbound_id, occurred_at),
|
||
CONSTRAINT fk_history_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='入库单流程状态不可变历史';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_outbox (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '领域事件全局ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '事件所属租户ID',
|
||
aggregate_type VARCHAR(32) NOT NULL DEFAULT 'INBOUND' COMMENT '聚合类型',
|
||
aggregate_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '事件类型',
|
||
payload_json JSON NOT NULL COMMENT '事件负载快照',
|
||
publish_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '发布状态',
|
||
retry_count INT NOT NULL DEFAULT 0 COMMENT '重试次数',
|
||
next_retry_at DATETIME(3) NULL COMMENT '下次重试时间',
|
||
published_at DATETIME(3) NULL COMMENT '发布时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_inbound_outbox_publish (publish_status, next_retry_at, created_at)
|
||
) ENGINE=InnoDB COMMENT='入库事务Outbox事件表';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_idempotency (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '入库写操作幂等记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '租户ID',
|
||
operation VARCHAR(40) NOT NULL COMMENT '操作类型',
|
||
operator_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '操作者或外部应用ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '入库单ID,创建前可为0',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '请求内容摘要',
|
||
status VARCHAR(16) NOT NULL COMMENT '处理状态',
|
||
result_json JSON NULL COMMENT '成功结果摘要',
|
||
result_resource_id BIGINT UNSIGNED NULL COMMENT '成功结果资源ID',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_idempotency (tenant_id, operation, operator_id, idempotency_key),
|
||
KEY idx_inbound_idempotency_created (created_at)
|
||
) ENGINE=InnoDB COMMENT='第三方测量、审核、费用发送和入库写操作幂等记录';
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-inbound-measurement-maintenance.sql
|
||
-- ============================================================================
|
||
-- 二期入库测量与 OCR 维护增量(只增不删,可重复执行)。
|
||
-- 执行顺序:phase2-inbound-schema.sql 后执行本文件;已有二期库只需执行本文件。
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
DROP PROCEDURE IF EXISTS phase2_measure_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase2_measure_add_column(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_column_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @phase2_measure_sql = CONCAT(
|
||
'ALTER TABLE `', REPLACE(p_table_name, '`', '``'),
|
||
'` ADD COLUMN `', REPLACE(p_column_name, '`', '``'), '` ', p_column_definition
|
||
);
|
||
PREPARE phase2_measure_statement FROM @phase2_measure_sql;
|
||
EXECUTE phase2_measure_statement;
|
||
DEALLOCATE PREPARE phase2_measure_statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase2_measure_add_column('fl_inbound_measurement', 'original_document_no',
|
||
'VARCHAR(64) NULL COMMENT ''原单据号码快照'' AFTER raw_payload_json');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement', 'original_gross_weight_kg',
|
||
'DECIMAL(18,3) NULL COMMENT ''原单据毛重kg'' AFTER original_document_no');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement', 'original_volume_cbm',
|
||
'DECIMAL(18,4) NULL COMMENT ''原单据体积或材积m3'' AFTER original_gross_weight_kg');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement', 'actual_goods_name',
|
||
'VARCHAR(255) NULL COMMENT ''实际品名'' AFTER original_volume_cbm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement', 'volume_weight_kg',
|
||
'DECIMAL(18,3) NULL COMMENT ''本批次总材积重kg'' AFTER total_volume_cbm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement', 'version',
|
||
'INT NOT NULL DEFAULT 0 COMMENT ''乐观锁版本号'' AFTER updated_at');
|
||
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'box_specification',
|
||
'VARCHAR(64) NOT NULL DEFAULT '''' COMMENT ''箱规标识'' AFTER item_no');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'box_code',
|
||
'VARCHAR(128) NULL COMMENT ''柏悦唛头箱号;设备单箱测量时同一客户内唯一'' AFTER external_detail_no');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'girth_cm',
|
||
'DECIMAL(12,3) NOT NULL DEFAULT 0 COMMENT ''周长cm,规则为长+2*(宽+高)'' AFTER height_cm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'unit_weight_kg',
|
||
'DECIMAL(18,3) NULL COMMENT ''单箱重量kg'' AFTER girth_cm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'unit_volume_cbm',
|
||
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''单箱业务体积m3'' AFTER unit_weight_kg');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'scanned_shipment_id',
|
||
'VARCHAR(64) NULL COMMENT ''设备从Amazon FBA箱标实扫的Shipment ID'' AFTER box_code');
|
||
CALL phase2_measure_add_column('fl_mojia_measurement_receipt', 'scanned_shipment_id',
|
||
'VARCHAR(64) NULL COMMENT ''设备从Amazon FBA箱标实扫的Shipment ID'' AFTER box_code');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'total_weight_kg',
|
||
'DECIMAL(18,3) NULL COMMENT ''本箱规总重量kg'' AFTER volume_cbm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'total_volume_cbm',
|
||
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''本箱规总体积m3'' AFTER total_weight_kg');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'volume_weight_kg',
|
||
'DECIMAL(18,3) NOT NULL DEFAULT 0 COMMENT ''本箱规材积重kg'' AFTER total_volume_cbm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'over_limit_warning',
|
||
'VARCHAR(500) NULL COMMENT ''超线提醒快照'' AFTER source_image_file_id');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'machine_no',
|
||
'VARCHAR(64) NULL COMMENT ''本次测量设备编号'' AFTER source_image_file_id');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'device_measured_at',
|
||
'DATETIME(3) NULL COMMENT ''本次设备测量时间'' AFTER machine_no');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'outer_length_cm',
|
||
'DECIMAL(12,3) NULL COMMENT ''外箱规长度cm'' AFTER over_limit_warning');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'outer_width_cm',
|
||
'DECIMAL(12,3) NULL COMMENT ''外箱规宽度cm'' AFTER outer_length_cm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'outer_height_cm',
|
||
'DECIMAL(12,3) NULL COMMENT ''外箱规高度cm'' AFTER outer_width_cm');
|
||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'updated_at',
|
||
'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT ''最后更新时间'' AFTER created_at');
|
||
|
||
SET @has_measure_box_code_index = (
|
||
SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_measurement_item'
|
||
AND index_name = 'uk_measure_box_code'
|
||
);
|
||
SET @phase2_measure_sql = IF(
|
||
@has_measure_box_code_index = 0,
|
||
'ALTER TABLE fl_inbound_measurement_item ADD UNIQUE KEY uk_measure_box_code (tenant_id, box_code)',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE phase2_measure_statement FROM @phase2_measure_sql;
|
||
EXECUTE phase2_measure_statement;
|
||
DEALLOCATE PREPARE phase2_measure_statement;
|
||
|
||
ALTER TABLE fl_inbound_measurement_item
|
||
MODIFY COLUMN calculation_rule_version VARCHAR(64) NOT NULL COMMENT '体积计算和舍入规则版本';
|
||
|
||
DROP PROCEDURE IF EXISTS phase2_measure_add_column;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量修订记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
weight_result_id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量结果ID',
|
||
revision_no INT UNSIGNED NOT NULL COMMENT '结果版本号,与乐观锁版本一致',
|
||
action VARCHAR(24) NOT NULL COMMENT '动作:CORRECT或CONFIRM',
|
||
before_weight_value DECIMAL(18,3) NULL COMMENT '变更前原单位重量',
|
||
before_weight_unit VARCHAR(8) NULL COMMENT '变更前单位',
|
||
after_weight_value DECIMAL(18,3) NULL COMMENT '变更后原单位重量',
|
||
after_weight_unit VARCHAR(8) NULL COMMENT '变更后单位',
|
||
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的重量kg',
|
||
reason VARCHAR(500) NULL COMMENT '人工修正原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
operated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_weight_revision_no (tenant_id, weight_result_id, revision_no),
|
||
KEY idx_weight_revision_time (tenant_id, inbound_id, operated_at),
|
||
CONSTRAINT fk_weight_revision_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_weight_revision_result FOREIGN KEY (weight_result_id) REFERENCES fl_inbound_weight_result (id)
|
||
) ENGINE=InnoDB COMMENT='OCR重量人工修正和确认的不可变版本历史';
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-inbound-weight-audit-migration.sql
|
||
-- ============================================================================
|
||
-- 二期称重结果审计字段增量迁移(可重复执行)。
|
||
-- InboundWeightResultEntity 继承 BaseEntity,持久化需要 created_by、updated_by 两个审计字段。
|
||
SET @has_created_by := (
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_weight_result' AND column_name = 'created_by'
|
||
);
|
||
SET @created_by_sql := IF(@has_created_by = 0,
|
||
'ALTER TABLE fl_inbound_weight_result ADD COLUMN created_by BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''创建人ID'' AFTER confirmed_at',
|
||
'SELECT 1');
|
||
PREPARE created_by_stmt FROM @created_by_sql;
|
||
EXECUTE created_by_stmt;
|
||
DEALLOCATE PREPARE created_by_stmt;
|
||
|
||
SET @has_updated_by := (
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_weight_result' AND column_name = 'updated_by'
|
||
);
|
||
SET @updated_by_sql := IF(@has_updated_by = 0,
|
||
'ALTER TABLE fl_inbound_weight_result ADD COLUMN updated_by BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''最后修改人ID'' AFTER created_at',
|
||
'SELECT 1');
|
||
PREPARE updated_by_stmt FROM @updated_by_sql;
|
||
EXECUTE updated_by_stmt;
|
||
DEALLOCATE PREPARE updated_by_stmt;
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-inbound-workflow-migration.sql
|
||
-- ============================================================================
|
||
-- 业务块4增量迁移:既有二期库补齐异常放行关口查询索引。
|
||
-- 可重复执行;新库仍先执行 phase2-inbound-schema.sql。
|
||
USE baiyue_firstleg;
|
||
SET NAMES utf8mb4;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_measure_review_record'
|
||
AND index_name = 'idx_review_exception_release'),
|
||
'SELECT 1',
|
||
'ALTER TABLE fl_measure_review_record ADD INDEX idx_review_exception_release (tenant_id, inbound_id, exception_release, reviewed_at)'
|
||
);
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-measurement-openapi.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
-- 既有二期数据库增量:先确保 phase2-inbound-schema.sql 中的测量和幂等表已存在。
|
||
CREATE TABLE IF NOT EXISTS fl_third_party_call_log (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '第三方调用日志ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '第三方未知/未解析主体哨兵值;不是平台客户记录或平台作用域,不得用于业务数据授权',
|
||
app_key VARCHAR(128) NULL COMMENT '第三方应用标识',
|
||
signature_subject VARCHAR(128) NULL COMMENT '签名主体快照',
|
||
request_method VARCHAR(8) NOT NULL COMMENT 'HTTP方法',
|
||
request_path VARCHAR(255) NOT NULL COMMENT '签名使用的规范路径',
|
||
request_timestamp VARCHAR(32) NULL COMMENT '请求头Unix毫秒时间戳原值',
|
||
nonce VARCHAR(128) NULL COMMENT '请求Nonce',
|
||
idempotency_key VARCHAR(128) NULL COMMENT 'HTTP幂等键,查询接口为空',
|
||
idempotency_anchor TINYINT UNSIGNED NULL COMMENT '签名通过后首个请求为1,重复调用为空',
|
||
signature_value VARCHAR(128) NULL COMMENT '第三方签名原值',
|
||
request_hash CHAR(64) NOT NULL COMMENT '原始请求体SHA-256小写十六进制',
|
||
raw_payload LONGTEXT NOT NULL COMMENT '未经重新序列化的UTF-8原始请求体',
|
||
processing_status VARCHAR(16) NOT NULL COMMENT 'PROCESSING、ACCEPTED或REJECTED',
|
||
result_code VARCHAR(32) NULL COMMENT '统一响应业务码',
|
||
result_message VARCHAR(1000) NULL COMMENT '处理结果或失败原因',
|
||
result_json JSON NULL COMMENT '首次成功结果快照',
|
||
requested_at DATETIME(3) NOT NULL COMMENT '服务端接收时间',
|
||
processed_at DATETIME(3) NULL COMMENT '处理完成时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_third_call_app_time (tenant_id, app_key, requested_at),
|
||
KEY idx_third_call_idempotency (tenant_id, app_key, idempotency_key),
|
||
KEY idx_third_call_nonce (tenant_id, app_key, nonce),
|
||
UNIQUE KEY uk_third_call_idempotency_owner (tenant_id, app_key, idempotency_key, idempotency_anchor)
|
||
) ENGINE=InnoDB COMMENT='第三方测量开放接口原始请求、签名主体和处理结果留档';
|
||
|
||
-- 既有表也要同步未知主体 0 的语义,避免历史注释继续误导为真实租户。
|
||
SET @third_party_tenant_comment = '第三方未知/未解析主体哨兵值;不是平台客户记录或平台作用域,不得用于业务数据授权';
|
||
SET @ddl = IF(
|
||
(SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_third_party_call_log'
|
||
AND column_name = 'tenant_id' AND column_comment = @third_party_tenant_comment) = 1,
|
||
'SELECT 1',
|
||
'ALTER TABLE fl_third_party_call_log MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''第三方未知/未解析主体哨兵值;不是平台客户记录或平台作用域,不得用于业务数据授权'''
|
||
);
|
||
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-third-party-single-measurement-migration.sql
|
||
-- ============================================================================
|
||
-- 二期第三方单条箱规 + 单张图片绑定增量;可重复执行。
|
||
USE baiyue_firstleg;
|
||
|
||
DROP PROCEDURE IF EXISTS phase2_add_measurement_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase2_add_measurement_column(
|
||
IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_definition TEXT)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = p_table AND COLUMN_NAME = p_column
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
|
||
PREPARE statement FROM @ddl;
|
||
EXECUTE statement;
|
||
DEALLOCATE PREPARE statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase2_add_measurement_column(
|
||
'fl_inbound_measurement', 'external_request_hash',
|
||
'CHAR(64) NULL COMMENT ''第三方data原文和图片内容联合请求哈希'' AFTER external_event_no');
|
||
CALL phase2_add_measurement_column(
|
||
'fl_inbound_measurement_item', 'inbound_detail_id',
|
||
'BIGINT UNSIGNED NULL COMMENT ''绑定的订单货物明细ID,平台手工明细可为空'' AFTER measurement_id');
|
||
CALL phase2_add_measurement_column(
|
||
'fl_inbound_measurement_item', 'external_detail_no',
|
||
'VARCHAR(128) NULL COMMENT ''第三方测量明细唯一号,平台手工明细可为空'' AFTER inbound_detail_id');
|
||
|
||
DROP PROCEDURE IF EXISTS phase2_add_measurement_column;
|
||
|
||
SET @has_index = (
|
||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||
WHERE TABLE_SCHEMA = DATABASE()
|
||
AND TABLE_NAME = 'fl_inbound_measurement_item'
|
||
AND INDEX_NAME = 'uk_measure_external_detail'
|
||
);
|
||
SET @ddl = IF(
|
||
@has_index = 0,
|
||
'ALTER TABLE fl_inbound_measurement_item ADD UNIQUE KEY uk_measure_external_detail (tenant_id, inbound_detail_id, external_detail_no)',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE statement FROM @ddl;
|
||
EXECUTE statement;
|
||
DEALLOCATE PREPARE statement;
|
||
|
||
-- ============================================================================
|
||
-- 来源:firstleg\src\main\resources\db\phase2-fba-pricing-migration.sql
|
||
-- ============================================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
-- 二期 FBA 计费增量迁移。通过 information_schema 判断,允许重复部署。
|
||
DROP PROCEDURE IF EXISTS phase2_fba_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase2_fba_add_column(IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_definition TEXT)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = p_table AND COLUMN_NAME = p_column
|
||
) THEN
|
||
SET @sql = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
|
||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase2_fba_add_column('fl_inbound_order', 'carrier_id', "BIGINT UNSIGNED NULL COMMENT '计费船司逻辑ID' AFTER channel_name");
|
||
CALL phase2_fba_add_column('fl_inbound_order', 'carrier_name', "VARCHAR(128) NULL COMMENT '计费船司名称快照' AFTER carrier_id");
|
||
CALL phase2_fba_add_column('fl_carrier_fba_rate', 'business_type', "VARCHAR(32) NOT NULL DEFAULT 'FBA' COMMENT '业务或计费类型' AFTER origin_warehouse_name");
|
||
CALL phase2_fba_add_column('fl_customer_charge_rule', 'version', "INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER deleted");
|
||
CALL phase2_fba_add_column('fl_carrier_fba_rate', 'version', "INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER deleted");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_code', "VARCHAR(50) NOT NULL DEFAULT '' COMMENT '客户规则编码快照' AFTER carrier_rate_version");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'customer_id', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '客户ID快照' AFTER rule_code");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'customer_name', "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '客户名称快照' AFTER customer_id");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'business_type', "VARCHAR(32) NOT NULL DEFAULT '' COMMENT '业务类型快照' AFTER customer_name");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'tax_mode', "VARCHAR(24) NULL COMMENT '包税模式快照' AFTER business_type");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rounding_scale', "TINYINT UNSIGNED NOT NULL DEFAULT 3 COMMENT '计费量保留位数快照' AFTER tax_mode");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rounding_mode', "VARCHAR(24) NOT NULL DEFAULT 'CEILING' COMMENT '舍入方式快照' AFTER rounding_scale");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_effective_from', "DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '客户规则生效开始快照' AFTER rounding_mode");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_effective_to', "DATETIME(3) NULL COMMENT '客户规则生效结束快照' AFTER rule_effective_from");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rate_code', "VARCHAR(50) NOT NULL DEFAULT '' COMMENT '船司价格编码快照' AFTER rule_effective_to");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'origin_warehouse_id', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '起运仓ID快照' AFTER rate_code");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'origin_warehouse_name', "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '起运仓名称快照' AFTER origin_warehouse_id");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'channel_code', "VARCHAR(64) NOT NULL DEFAULT '' COMMENT '渠道编码快照' AFTER origin_warehouse_name");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'carrier_id', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '船司ID快照' AFTER channel_code");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'carrier_name', "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '船司名称快照' AFTER carrier_id");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'destination_code', "VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'FBA仓编码快照' AFTER carrier_name");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rate_effective_from', "DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '价格生效开始快照' AFTER destination_code");
|
||
CALL phase2_fba_add_column('fl_inbound_fee', 'rate_effective_to', "DATETIME(3) NULL COMMENT '价格生效结束快照' AFTER rate_effective_from");
|
||
CALL phase2_fba_add_column('fl_inbound_idempotency', 'result_resource_id', "BIGINT UNSIGNED NULL COMMENT '成功结果资源ID' AFTER result_json");
|
||
CALL phase2_fba_add_column('fl_inbound_pallet_allocation', 'warehouse_resource_tenant_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '平台公共仓储资源固定使用 0;tenant_id 仍是客户隔离键' AFTER warehouse_id");
|
||
|
||
-- 仓储资源是平台公共主数据;存量分配快照统一迁移到平台作用域 0,再收口非空约束。
|
||
UPDATE fl_inbound_pallet_allocation
|
||
SET warehouse_resource_tenant_id = 0
|
||
WHERE warehouse_resource_tenant_id IS NULL OR warehouse_resource_tenant_id <> 0;
|
||
SET @resource_tenant_needs_modify = (SELECT COUNT(*) FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_inbound_pallet_allocation'
|
||
AND COLUMN_NAME = 'warehouse_resource_tenant_id'
|
||
AND (IS_NULLABLE = 'YES' OR COLUMN_COMMENT <> '平台公共仓储资源固定使用 0;tenant_id 仍是客户隔离键'));
|
||
SET @sql = IF(@resource_tenant_needs_modify > 0,
|
||
"ALTER TABLE fl_inbound_pallet_allocation MODIFY COLUMN warehouse_resource_tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓储资源固定使用 0;tenant_id 仍是客户隔离键'",
|
||
'SELECT 1');
|
||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
DROP PROCEDURE IF EXISTS phase2_fba_add_column;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_price_channel_catalog (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '渠道目录ID',
|
||
channel_code VARCHAR(64) NOT NULL COMMENT '稳定渠道编码',
|
||
display_name VARCHAR(128) NOT NULL COMMENT '平台渠道名称',
|
||
price_model VARCHAR(32) NOT NULL COMMENT '默认价格模型',
|
||
source_sheet_name VARCHAR(128) NOT NULL COMMENT '来源Sheet名称;海派为同一Sheet',
|
||
aliases_json JSON NOT NULL COMMENT '上传名称别名数组',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '目录状态',
|
||
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_price_channel_code (channel_code),
|
||
KEY idx_price_channel_status (status, price_model)
|
||
) ENGINE=InnoDB COMMENT='平台公共船期价格固定渠道目录';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_price_origin_alias (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '起运仓别名ID',
|
||
warehouse_code VARCHAR(64) NOT NULL COMMENT '平台仓库稳定编码,不存环境专属ID',
|
||
alias_name VARCHAR(128) NOT NULL COMMENT 'Excel起运仓名称别名',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||
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_price_origin_alias (warehouse_code, alias_name),
|
||
KEY idx_price_origin_alias_name (alias_name, status)
|
||
) ENGINE=InnoDB COMMENT='价格表起运仓名称别名映射';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_price_warehouse_mapping (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '价格仓映射ID',
|
||
source_type VARCHAR(24) NOT NULL COMMENT '来源维度:ORIGIN_CODE或OVERSEAS_NAME',
|
||
source_value VARCHAR(128) NOT NULL COMMENT '解析后的来源仓编码或海外仓名称',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '固定平台公共仓库ID',
|
||
warehouse_code VARCHAR(64) NOT NULL COMMENT '平台仓库稳定编码快照',
|
||
warehouse_type VARCHAR(16) NOT NULL COMMENT 'DOMESTIC或OVERSEAS',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||
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_price_warehouse_mapping_source (source_type, source_value),
|
||
KEY idx_price_warehouse_mapping_target (warehouse_id, status)
|
||
) ENGINE=InnoDB COMMENT='价格表来源仓与固定平台公共仓映射';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_price_book_version (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '整表价格版本ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '平台公共价格作用域固定为0',
|
||
version_no INT UNSIGNED NOT NULL COMMENT '整表版本号',
|
||
original_file_name VARCHAR(255) NOT NULL COMMENT '原始Excel文件名',
|
||
source_file_id BIGINT UNSIGNED NULL COMMENT 'File服务原始文件ID',
|
||
source_sha256 CHAR(64) NULL COMMENT '原始文件SHA-256',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'PENDING_CONFIRMATION' COMMENT '待确认上传记录;ACTIVE/SUPERSEDED/FAILED',
|
||
blocking_error_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阻断错误数',
|
||
warning_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '警告数',
|
||
parse_issues_json JSON NULL COMMENT '上传解析问题及待确认重新校验结果',
|
||
channel_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '解析渠道数',
|
||
rate_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '解析价格数',
|
||
created_by BIGINT UNSIGNED NOT NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
published_by BIGINT UNSIGNED NULL,
|
||
published_at DATETIME(3) NULL COMMENT '确认并生效时间',
|
||
superseded_at DATETIME(3) NULL COMMENT '被下一版本替换时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0,
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_price_book_version (tenant_id, version_no),
|
||
KEY idx_price_book_status (tenant_id, status, published_at),
|
||
KEY idx_price_book_source_hash (tenant_id, source_sha256)
|
||
) ENGINE=InnoDB COMMENT='平台公共船期价格整表版本';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_price_book_channel (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '版本渠道快照ID',
|
||
price_book_version_id BIGINT UNSIGNED NOT NULL COMMENT '整表价格版本ID',
|
||
channel_code VARCHAR(64) NOT NULL COMMENT '固定渠道编码',
|
||
channel_name VARCHAR(128) NOT NULL COMMENT '渠道名称快照',
|
||
price_model VARCHAR(32) NOT NULL COMMENT '价格模型快照',
|
||
source_sheet_name VARCHAR(128) NOT NULL COMMENT '原始Excel Sheet',
|
||
estimated_departure_date DATE NULL COMMENT '标准预计开船日期',
|
||
estimated_departure_text VARCHAR(128) NULL COMMENT '原始预计开船日期文本',
|
||
latest_warehouse_entry VARCHAR(128) NULL COMMENT '最晚进仓时间',
|
||
vessel_name VARCHAR(128) NULL COMMENT '船名',
|
||
voyage_no VARCHAR(64) NULL COMMENT '航次',
|
||
reference_transit VARCHAR(128) NULL COMMENT '参考时效',
|
||
surcharge_description VARCHAR(2000) NULL COMMENT '附加费说明,仅展示',
|
||
remark VARCHAR(2000) NULL COMMENT '渠道备注',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_price_book_channel (price_book_version_id, channel_code),
|
||
KEY idx_price_book_channel_code (channel_code, price_book_version_id)
|
||
|
||
) ENGINE=InnoDB COMMENT='价格版本渠道及船期快照';
|
||
|
||
INSERT INTO fl_price_channel_catalog
|
||
(id, channel_code, display_name, price_model, source_sheet_name, aliases_json, status)
|
||
VALUES
|
||
(1, 'MATSON_CLX_TRUCK', '美森正班海卡', 'FBA_WAREHOUSE_RATE', '美森正班海卡', JSON_ARRAY('美森正班海卡'), 'ACTIVE'),
|
||
(2, 'MATSON_MAX_TRUCK', '美森加班海卡', 'FBA_WAREHOUSE_RATE', '美森加班海卡', JSON_ARRAY('美森加班海卡'), 'ACTIVE'),
|
||
(3, 'EXX_TRUCK', 'EXX海卡', 'FBA_WAREHOUSE_RATE', 'EXX海卡', JSON_ARRAY('EXX海卡'), 'ACTIVE'),
|
||
(4, 'HEDE_TRUCK', '合德海卡', 'FBA_WAREHOUSE_RATE', '合德海卡', JSON_ARRAY('合德海卡'), 'ACTIVE'),
|
||
(5, 'EXX_RAIL_NY', '纽约EXX海铁', 'FBA_WAREHOUSE_RATE', '纽约EXX海铁', JSON_ARRAY('纽约EXX海铁'), 'ACTIVE'),
|
||
(6, 'OA_LA_TRUCK', '洛杉矶普船海卡', 'FBA_WAREHOUSE_RATE', '洛杉矶普船海卡', JSON_ARRAY('洛杉矶普船海卡'), 'ACTIVE'),
|
||
(7, 'OA_LA_FULL_TRUCK', '洛杉矶普船全卡', 'FBA_WAREHOUSE_RATE', '洛杉矶普船全卡', JSON_ARRAY('洛杉矶普船全卡'), 'ACTIVE'),
|
||
(8, 'OA_CHICAGO_RAIL', '芝加哥普船海铁', 'FBA_WAREHOUSE_RATE', '芝加哥普船海铁', JSON_ARRAY('芝加哥普船海铁'), 'ACTIVE'),
|
||
(9, 'OA_HOUSTON_TRUCK', '休斯顿普船海卡', 'FBA_WAREHOUSE_RATE', '休斯顿普船海卡', JSON_ARRAY('休斯顿普船海卡'), 'ACTIVE'),
|
||
(10, 'OA_NEW_YORK_TRUCK', '纽约普船海卡', 'FBA_WAREHOUSE_RATE', '纽约普船海卡', JSON_ARRAY('纽约普船海卡'), 'ACTIVE'),
|
||
(11, 'OA_SAVANNAH_TRUCK', '萨凡纳普船海卡', 'FBA_WAREHOUSE_RATE', '萨凡纳普船海卡', JSON_ARRAY('萨凡纳普船海卡'), 'ACTIVE'),
|
||
(12, 'MATSON_CLX_PARCEL', '美森正班海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('美森正班海派'), 'ACTIVE'),
|
||
(13, 'MATSON_MAX_PARCEL', '美森加班海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('美森加班海派'), 'ACTIVE'),
|
||
(14, 'HEDE_PARCEL', '合德海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('合德海派'), 'ACTIVE'),
|
||
(15, 'OA_PARCEL', '普船海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('普船海派'), 'ACTIVE')
|
||
ON DUPLICATE KEY UPDATE
|
||
display_name = VALUES(display_name), price_model = VALUES(price_model),
|
||
source_sheet_name = VALUES(source_sheet_name), aliases_json = VALUES(aliases_json), status = VALUES(status);
|
||
|
||
INSERT INTO fl_price_origin_alias (id, warehouse_code, alias_name, status)
|
||
VALUES
|
||
(1, 'CN-YIWU-01', '华东仓(义乌)', 'ACTIVE'),
|
||
(2, 'CN-YIWU-01', '义乌仓', 'ACTIVE'),
|
||
(3, 'CN-YIWU-01', '义乌', 'ACTIVE'),
|
||
(4, 'CN-DONGGUAN-01', '华南仓(深圳)', 'ACTIVE'),
|
||
(5, 'CN-DONGGUAN-01', '东莞仓', 'ACTIVE'),
|
||
(6, 'CN-DONGGUAN-01', '深圳仓', 'ACTIVE'),
|
||
(7, 'CN-DONGGUAN-01', '华南仓(深圳/东莞)', 'ACTIVE')
|
||
ON DUPLICATE KEY UPDATE status = VALUES(status);
|
||
|
||
INSERT INTO fl_price_warehouse_mapping
|
||
(id, source_type, source_value, warehouse_id, warehouse_code, warehouse_type, status)
|
||
VALUES
|
||
(1, 'ORIGIN_CODE', 'CN-YIWU-01', 193000000000200001, 'CN-YIWU-01', 'DOMESTIC', 'ACTIVE'),
|
||
(2, 'ORIGIN_CODE', 'CN-DONGGUAN-01', 193000000000200002, 'CN-DONGGUAN-01', 'DOMESTIC', 'ACTIVE'),
|
||
(3, 'OVERSEAS_NAME', '洛杉矶仓', 193000000000200003, 'US-LAX-01', 'OVERSEAS', 'ACTIVE'),
|
||
(4, 'OVERSEAS_NAME', '纽约仓', 193000000000200004, 'US-NYC-01', 'OVERSEAS', 'ACTIVE')
|
||
ON DUPLICATE KEY UPDATE
|
||
warehouse_id = VALUES(warehouse_id), warehouse_code = VALUES(warehouse_code),
|
||
warehouse_type = VALUES(warehouse_type), status = VALUES(status);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase2-inbound-query-permissions.sql
|
||
-- ============================================================================
|
||
-- 二期入库单列表和详情只读权限,可在一期 RBAC 脚本后重复执行。
|
||
USE baiyue_iam;
|
||
|
||
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
|
||
(5102030, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:inbound:list',
|
||
'入库单', '/inbounds', 'GET', 30, 'ACTIVE', 1, 0),
|
||
(5102031, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:view',
|
||
'查看入库单详情', NULL, 'GET', 10, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id),
|
||
route_path = VALUES(route_path), http_method = VALUES(http_method),
|
||
status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT role.id, permission.id, role.tenant_id
|
||
FROM iam_role role
|
||
JOIN iam_permission permission
|
||
ON permission.tenant_id = 0
|
||
AND permission.permission_code IN (
|
||
'platform:firstleg:inbound:list', 'platform:firstleg:inbound:view')
|
||
WHERE role.tenant_id = 0
|
||
AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR');
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase2-inbound-measurement-permissions.sql
|
||
-- ============================================================================
|
||
-- 二期入库测量平台菜单和按钮权限,可重复执行。
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_iam;
|
||
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name,
|
||
route_path, http_method, sort_no, status, deleted)
|
||
VALUES
|
||
(5102041, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:measure:view', '查看入库测量', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5102042, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:measure:edit', '保存测量草稿', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5102043, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:measure:submit', '确认并提交测量', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5102044, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:weight:edit', '维护OCR重量', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5102045, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:weight:confirm', '确认OCR重量', NULL, NULL, 40, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = NULL, sort_no = VALUES(sort_no), status = 'ACTIVE', deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0 FROM iam_permission
|
||
WHERE id IN (5102041, 5102042, 5102043, 5102044, 5102045)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000003, id, 0 FROM iam_permission
|
||
WHERE id IN (5102041, 5102042, 5102043, 5102044, 5102045)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase2-inbound-permissions.sql
|
||
-- ============================================================================
|
||
-- 二期入库审核与异常放行按钮权限,可重复部署。
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
DROP PROCEDURE IF EXISTS repair_location_permission_ids;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE repair_location_permission_ids()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE id IN (5103060, 5103061, 5103062, 5103063, 5103064)
|
||
AND permission_code <> CASE id
|
||
WHEN 5103060 THEN 'platform:location:list'
|
||
WHEN 5103061 THEN 'platform:location:create'
|
||
WHEN 5103062 THEN 'platform:location:edit'
|
||
WHEN 5103063 THEN 'platform:location:status'
|
||
WHEN 5103064 THEN 'platform:location:delete'
|
||
END
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '库位权限迁移失败:5103060-5103064 已被其他权限占用';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE (id = 5103040 AND NOT (
|
||
(permission_code = 'platform:download:list' AND parent_id = 5103000)
|
||
OR (permission_code = 'platform:location:list' AND parent_id = 5103000)))
|
||
OR (id = 5103041 AND NOT (
|
||
(permission_code = 'platform:download:all' AND parent_id = 5103040)
|
||
OR (permission_code = 'platform:location:create' AND parent_id = 5103040)))
|
||
OR (permission_code = 'platform:download:list' AND id <> 5103040)
|
||
OR (permission_code = 'platform:download:all' AND id <> 5103041)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '库位权限迁移失败:下载中心稳定 ID 被未知权限占用';
|
||
END IF;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_location_role_grant;
|
||
CREATE TEMPORARY TABLE tmp_location_role_grant (
|
||
role_id BIGINT UNSIGNED NOT NULL, tenant_id BIGINT UNSIGNED NOT NULL,
|
||
permission_code VARCHAR(128) NOT NULL,
|
||
PRIMARY KEY (role_id, tenant_id, permission_code)
|
||
) ENGINE=MEMORY;
|
||
INSERT IGNORE INTO tmp_location_role_grant (role_id, tenant_id, permission_code)
|
||
SELECT rp.role_id, rp.tenant_id, permission.permission_code
|
||
FROM iam_role_permission rp JOIN iam_permission permission ON permission.id = rp.permission_id
|
||
WHERE permission.permission_code IN ('platform:location:list', 'platform:location:create',
|
||
'platform:location:edit', 'platform:location:status', 'platform:location:delete');
|
||
DELETE rp FROM iam_role_permission rp
|
||
JOIN iam_permission permission ON permission.id = rp.permission_id
|
||
WHERE permission.permission_code IN ('platform:location:list', 'platform:location:create',
|
||
'platform:location:edit', 'platform:location:status', 'platform:location:delete');
|
||
DELETE FROM iam_permission WHERE permission_code IN ('platform:location:create',
|
||
'platform:location:edit', 'platform:location:status', 'platform:location:delete');
|
||
DELETE FROM iam_permission WHERE permission_code = 'platform:location:list';
|
||
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
|
||
(5103040, 0, 'PLATFORM', 'MENU', 5103000, 'platform:download:list', '下载中心', '/downloads', NULL, 40, 'ACTIVE', 1, 0),
|
||
(5103041, 0, 'PLATFORM', 'BUTTON', 5103040, 'platform:download:all', '查看全部下载任务', NULL, NULL, 10, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = VALUES(http_method), sort_no = VALUES(sort_no), status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL repair_location_permission_ids();
|
||
DROP PROCEDURE repair_location_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, deleted)
|
||
VALUES
|
||
(5102051, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:measurement-review',
|
||
'测量审核', NULL, NULL, 10, 'ACTIVE', 0),
|
||
(5102052, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:measurement-review-history',
|
||
'查看测量审核历史', NULL, NULL, 20, 'ACTIVE', 0),
|
||
(5102053, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:exception-release',
|
||
'测量异常放行', NULL, NULL, 30, 'ACTIVE', 0),
|
||
(5102054, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:send-fee',
|
||
'发送费用并入库', NULL, NULL, 40, 'ACTIVE', 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id),
|
||
status = 'ACTIVE', deleted = 0;
|
||
|
||
-- 仅给平台超级管理员补齐默认权限;业务角色由 IAM 页面按职责授权。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, p.id, 0
|
||
FROM iam_permission p
|
||
WHERE p.id BETWEEN 5102051 AND 5102054
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- 客服(订单运营)负责测量审核;费用发送由审核通过后的服务端编排自动执行。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000003, p.id, 0
|
||
FROM iam_permission p
|
||
WHERE p.id IN (5102051, 5102052)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase2-fba-pricing-permissions.sql
|
||
-- ============================================================================
|
||
-- 二期 FBA 计费权限;在入库查询、测量和审核权限脚本后执行,可重复部署。
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
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
|
||
(5102061, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:rule:view', '查看客户计费规则', NULL, NULL, 10, 'ACTIVE', 1, 0),
|
||
(5102062, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:rule:edit', '维护客户计费规则', NULL, NULL, 20, 'ACTIVE', 1, 0),
|
||
(5102063, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:rate:view', '查看承运商价格', NULL, NULL, 30, 'ACTIVE', 1, 0),
|
||
(5102064, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:rate:edit', '维护承运商价格', NULL, NULL, 40, 'ACTIVE', 1, 0),
|
||
(5102065, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:calculate', '计算入库费用', NULL, NULL, 50, 'ACTIVE', 1, 0),
|
||
(5102066, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:confirm', '内部确认入库费用', NULL, NULL, 60, 'ACTIVE', 1, 0),
|
||
(5102067, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:view', '查看入库费用', NULL, NULL, 70, 'ACTIVE', 1, 0),
|
||
(5102068, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:fba-pricing:adjust', '调整已发布费用', NULL, NULL, 80, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id),
|
||
status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0 FROM iam_permission
|
||
WHERE id BETWEEN 5102061 AND 5102068
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000003, id, 0 FROM iam_permission
|
||
WHERE id IN (5102061, 5102063, 5102065, 5102066, 5102067, 5102068)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- 客户尺寸计算配置权限;与 FBA 计费规则相互独立。
|
||
DROP PROCEDURE IF EXISTS unified_guard_dimension_config_permissions;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_guard_dimension_config_permissions()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE (id = 5102111 AND COALESCE(permission_code, '') <> 'firstleg:dimension-config:view')
|
||
OR (id = 5102112 AND COALESCE(permission_code, '') <> 'firstleg:dimension-config:edit')
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户尺寸计算配置权限固定ID已被其他权限码占用';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE (permission_code = 'firstleg:dimension-config:view' AND id <> 5102111)
|
||
OR (permission_code = 'firstleg:dimension-config:edit' AND id <> 5102112)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户尺寸计算配置权限码已绑定其他固定ID';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_guard_dimension_config_permissions();
|
||
DROP PROCEDURE IF EXISTS unified_guard_dimension_config_permissions;
|
||
|
||
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
|
||
(5102111, 0, 'PLATFORM', 'BUTTON', 5100010, 'firstleg:dimension-config:view',
|
||
'查看客户尺寸计算配置', NULL, NULL, 90, 'ACTIVE', 1, 0),
|
||
(5102112, 0, 'PLATFORM', 'BUTTON', 5100010, 'firstleg:dimension-config:edit',
|
||
'维护客户尺寸计算配置', NULL, NULL, 100, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id),
|
||
status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT r.id, p.id, 0
|
||
FROM iam_role r
|
||
JOIN iam_permission p
|
||
ON p.tenant_id = 0 AND p.app_code = 'PLATFORM'
|
||
AND p.permission_code IN ('firstleg:dimension-config:view', 'firstleg:dimension-config:edit')
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'PLATFORM_ADMIN'
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT r.id, p.id, 0
|
||
FROM iam_role r
|
||
JOIN iam_permission p
|
||
ON p.tenant_id = 0 AND p.app_code = 'PLATFORM'
|
||
AND p.permission_code = 'firstleg:dimension-config:view'
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'ORDER_OPERATOR'
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase2-location-permissions.sql
|
||
-- ============================================================================
|
||
-- 二期库位管理菜单和按钮权限。
|
||
-- 在 phase1-admin-rbac.sql、phase1-platform-master-data-and-permission-management.sql 之后执行,可重复执行。
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
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
|
||
(5103060, 0, 'PLATFORM', 'MENU', 5103000, 'platform:location:list', '库位管理',
|
||
'/master-data/locations', NULL, 40, 'ACTIVE', 1, 0),
|
||
(5103061, 0, 'PLATFORM', 'BUTTON', 5103060, 'platform:location:create', '新增库位',
|
||
NULL, NULL, 10, 'ACTIVE', 1, 0),
|
||
(5103062, 0, 'PLATFORM', 'BUTTON', 5103060, 'platform:location:edit', '编辑库位',
|
||
NULL, NULL, 20, 'ACTIVE', 1, 0),
|
||
(5103063, 0, 'PLATFORM', 'BUTTON', 5103060, 'platform:location:status', '启停库位',
|
||
NULL, NULL, 30, 'ACTIVE', 1, 0),
|
||
(5103064, 0, 'PLATFORM', 'BUTTON', 5103060, 'platform:location:delete', '删除库位',
|
||
NULL, NULL, 40, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
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 saved.role_id, permission.id, saved.tenant_id
|
||
FROM tmp_location_role_grant saved
|
||
JOIN iam_permission permission ON permission.permission_code = saved.permission_code
|
||
AND permission.tenant_id = 0 AND permission.app_code = 'PLATFORM';
|
||
|
||
-- 二期新增库位权限默认只授予平台系统管理员。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0
|
||
FROM iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND permission_code IN (
|
||
'platform:location:list', 'platform:location:create', 'platform:location:edit',
|
||
'platform:location:status', 'platform:location:delete'
|
||
)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_location_role_grant;
|
||
|
||
-- ============================================================================
|
||
-- 来源:iam\src\main\resources\db\phase2-pallet-permissions.sql
|
||
-- ============================================================================
|
||
-- 二期托盘管理菜单和按钮权限。
|
||
-- 在 phase1-admin-rbac.sql、phase1-platform-master-data-and-permission-management.sql 之后执行,可重复执行。
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
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
|
||
(5103050, 0, 'PLATFORM', 'MENU', 5103000, 'platform:pallet:list', '托盘管理',
|
||
'/master-data/pallets', NULL, 50, 'ACTIVE', 1, 0),
|
||
(5103051, 0, 'PLATFORM', 'BUTTON', 5103050, 'platform:pallet:create', '新增托盘',
|
||
NULL, NULL, 10, 'ACTIVE', 1, 0),
|
||
(5103052, 0, 'PLATFORM', 'BUTTON', 5103050, 'platform:pallet:edit', '编辑托盘',
|
||
NULL, NULL, 20, 'ACTIVE', 1, 0),
|
||
(5103054, 0, 'PLATFORM', 'BUTTON', 5103050, 'platform:pallet:delete', '删除托盘',
|
||
NULL, NULL, 30, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
sort_no = VALUES(sort_no), status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
-- 二期新增托盘权限默认只授予平台系统管理员。
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0
|
||
FROM iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND permission_code IN (
|
||
'platform:pallet:list', 'platform:pallet:create', 'platform:pallet:edit', 'platform:pallet:delete'
|
||
)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- 兼容已经试运行过旧脚本的环境:移除人工启停权限。
|
||
DELETE rp FROM iam_role_permission rp
|
||
JOIN iam_permission p ON p.id = rp.permission_id
|
||
WHERE p.tenant_id = 0 AND p.app_code = 'PLATFORM' AND p.permission_code = 'platform:pallet:status';
|
||
UPDATE iam_permission
|
||
SET status = 'INACTIVE', deleted = 1
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM' AND permission_code = 'platform:pallet:status';
|
||
|
||
-- ============================================================================
|
||
-- 存量 File 导出中心安全升级
|
||
-- ============================================================================
|
||
USE baiyue_file;
|
||
DROP PROCEDURE IF EXISTS unified_file_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_file_add_column(
|
||
IN p_column_name VARCHAR(64), IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'file_export_task'
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @unified_ddl = CONCAT(
|
||
'ALTER TABLE file_export_task ADD COLUMN `',
|
||
REPLACE(p_column_name, '`', '``'), '` ', p_definition
|
||
);
|
||
PREPARE unified_stmt FROM @unified_ddl;
|
||
EXECUTE unified_stmt;
|
||
DEALLOCATE PREPARE unified_stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL unified_file_add_column('dispatch_version',
|
||
'INT NOT NULL DEFAULT 1 COMMENT ''派发版本,仅执行重试时递增'' AFTER failure_message');
|
||
CALL unified_file_add_column('attempt_count',
|
||
'INT NOT NULL DEFAULT 0 COMMENT ''已执行尝试次数'' AFTER dispatch_version');
|
||
CALL unified_file_add_column('max_attempts',
|
||
'INT NOT NULL DEFAULT 3 COMMENT ''自动重试上限'' AFTER attempt_count');
|
||
CALL unified_file_add_column('next_retry_at',
|
||
'DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT ''允许执行的最早时间'' AFTER max_attempts');
|
||
CALL unified_file_add_column('lease_owner',
|
||
'VARCHAR(128) NULL COMMENT ''当前执行实例标识'' AFTER next_retry_at');
|
||
CALL unified_file_add_column('lease_until',
|
||
'DATETIME(3) NULL COMMENT ''执行租约截止时间'' AFTER lease_owner');
|
||
CALL unified_file_add_column('last_heartbeat_at',
|
||
'DATETIME(3) NULL COMMENT ''最后心跳时间'' AFTER lease_until');
|
||
CALL unified_file_add_column('execution_deadline_at',
|
||
'DATETIME(3) NULL COMMENT ''本次执行截止时间'' AFTER last_heartbeat_at');
|
||
DROP PROCEDURE IF EXISTS unified_file_add_column;
|
||
|
||
SET @unified_ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'file_export_task'
|
||
AND index_name = 'idx_file_export_dispatch'),
|
||
'SELECT 1',
|
||
'ALTER TABLE file_export_task ADD INDEX idx_file_export_dispatch (status, next_retry_at, id)'
|
||
);
|
||
PREPARE unified_stmt FROM @unified_ddl; EXECUTE unified_stmt; DEALLOCATE PREPARE unified_stmt;
|
||
|
||
SET @unified_ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'file_export_task'
|
||
AND index_name = 'idx_file_export_lease'),
|
||
'SELECT 1',
|
||
'ALTER TABLE file_export_task ADD INDEX idx_file_export_lease (status, lease_until)'
|
||
);
|
||
PREPARE unified_stmt FROM @unified_ddl; EXECUTE unified_stmt; DEALLOCATE PREPARE unified_stmt;
|
||
|
||
-- ============================================================================
|
||
-- 一期订单快照字段规格统一
|
||
-- ============================================================================
|
||
USE baiyue_order;
|
||
SET @unified_ddl = IF(
|
||
COALESCE((SELECT character_maximum_length FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'ord_order'
|
||
AND column_name = 'receiving_warehouse_address'), 0) < 600,
|
||
'ALTER TABLE ord_order MODIFY COLUMN receiving_warehouse_address VARCHAR(600) NULL COMMENT ''收货仓地址历史快照''',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE unified_stmt FROM @unified_ddl; EXECUTE unified_stmt; DEALLOCATE PREPARE unified_stmt;
|
||
|
||
SET @unified_ddl = IF(
|
||
COALESCE((SELECT character_maximum_length FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'ord_order'
|
||
AND column_name = 'receiving_time'), 0) < 128,
|
||
'ALTER TABLE ord_order MODIFY COLUMN receiving_time VARCHAR(128) NULL COMMENT ''收货时间历史快照''',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE unified_stmt FROM @unified_ddl; EXECUTE unified_stmt; DEALLOCATE PREPARE unified_stmt;
|
||
|
||
-- ============================================================================
|
||
-- 重新同步一期商户管理员全局模板,包含一期后续追加的菜单和按钮
|
||
-- ============================================================================
|
||
USE baiyue_iam;
|
||
INSERT INTO iam_permission (
|
||
id, tenant_id, app_code, permission_type, parent_id, permission_code,
|
||
permission_name, route_path, http_method, sort_no, status, deleted
|
||
)
|
||
SELECT id + 100000, 0, app_code, permission_type,
|
||
CASE WHEN parent_id = 0 THEN 0 ELSE parent_id + 100000 END,
|
||
permission_code, permission_name, route_path, NULL, sort_no, 'ACTIVE', 0
|
||
FROM iam_permission
|
||
WHERE tenant_id = 10001 AND app_code = 'MERCHANT'
|
||
AND permission_type IN ('MENU', 'BUTTON')
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
parent_id = VALUES(parent_id), sort_no = VALUES(sort_no), status = 'ACTIVE', deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT owner_role.id, permission.id, 0
|
||
FROM iam_role owner_role
|
||
JOIN iam_permission permission
|
||
ON permission.app_code = 'MERCHANT' AND permission.tenant_id = 0
|
||
AND permission.permission_type IN ('MENU', 'BUTTON')
|
||
AND permission.status = 'ACTIVE' AND permission.deleted = 0
|
||
WHERE owner_role.tenant_id = 0 AND owner_role.app_code = 'MERCHANT'
|
||
AND owner_role.role_code = 'MERCHANT_OWNER'
|
||
AND owner_role.status = 'ACTIVE' AND owner_role.deleted = 0
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- ============================================================================
|
||
-- 统一执行结果强校验:任一关键结构缺失立即终止,不输出虚假 PASS
|
||
-- ============================================================================
|
||
DROP PROCEDURE IF EXISTS unified_assert_table;
|
||
DROP PROCEDURE IF EXISTS unified_assert_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_assert_table(IN p_schema VARCHAR(64), IN p_table VARCHAR(64))
|
||
BEGIN
|
||
DECLARE v_count INT DEFAULT 0;
|
||
DECLARE v_message VARCHAR(255);
|
||
SELECT COUNT(*) INTO v_count FROM information_schema.tables
|
||
WHERE table_schema = p_schema AND table_name = p_table;
|
||
IF v_count <> 1 THEN
|
||
SET v_message = CONCAT('统一迁移失败,缺少表:', p_schema, '.', p_table);
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = v_message;
|
||
END IF;
|
||
END$$
|
||
|
||
CREATE PROCEDURE unified_assert_column(
|
||
IN p_schema VARCHAR(64), IN p_table VARCHAR(64), IN p_column VARCHAR(64)
|
||
)
|
||
BEGIN
|
||
DECLARE v_count INT DEFAULT 0;
|
||
DECLARE v_message VARCHAR(255);
|
||
SELECT COUNT(*) INTO v_count FROM information_schema.columns
|
||
WHERE table_schema = p_schema AND table_name = p_table AND column_name = p_column;
|
||
IF v_count <> 1 THEN
|
||
SET v_message = CONCAT('统一迁移失败,缺少字段:', p_schema, '.', p_table, '.', p_column);
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = v_message;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL unified_assert_table('baiyue_auth', 'auth_account');
|
||
CALL unified_assert_table('baiyue_iam', 'iam_permission');
|
||
CALL unified_assert_table('baiyue_order', 'ord_order');
|
||
CALL unified_assert_table('baiyue_warehouse', 'wh_warehouse');
|
||
CALL unified_assert_table('baiyue_warehouse', 'wh_pallet');
|
||
CALL unified_assert_table('baiyue_warehouse', 'wh_location');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_fulfillment_task');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_inbound_order');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_third_party_call_log');
|
||
CALL unified_assert_table('baiyue_file', 'file_export_task_guard');
|
||
CALL unified_assert_table('baiyue_file', 'file_export_outbox');
|
||
CALL unified_assert_table('baiyue_audit', 'sys_audit_log');
|
||
CALL unified_assert_table('baiyue_event_bridge', 'bridge_inbox');
|
||
CALL unified_assert_column('baiyue_order', 'ord_order', 'carrier_id');
|
||
CALL unified_assert_column('baiyue_order', 'ord_order', 'carrier_name');
|
||
CALL unified_assert_column('baiyue_order', 'ord_order', 'pricing_tax_mode');
|
||
CALL unified_assert_column('baiyue_order', 'ord_order', 'inbound_code');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_inbound_pallet_allocation', 'warehouse_resource_tenant_id');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_inbound_order', 'pricing_tax_mode');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_inbound_measurement_item', 'external_detail_no');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_price_channel_catalog');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_price_origin_alias');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_price_warehouse_mapping');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_price_book_version');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_price_book_version', 'parse_issues_json');
|
||
CALL unified_assert_table('baiyue_firstleg', 'fl_price_book_channel');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_carrier_fba_rate', 'price_book_version_id');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_carrier_fba_rate', 'pricing_tax_mode');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_carrier_fba_rate', 'source_sheet_name');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_carrier_fba_rate', 'destination_type');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_carrier_fba_rate', 'destination_warehouse_id');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_carrier_fba_rate', 'destination_name');
|
||
CALL unified_assert_column('baiyue_firstleg', 'fl_inbound_fee', 'price_book_version_id');
|
||
CALL unified_assert_column('baiyue_file', 'file_export_task', 'execution_deadline_at');
|
||
|
||
DROP PROCEDURE IF EXISTS unified_assert_table;
|
||
DROP PROCEDURE IF EXISTS unified_assert_column;
|
||
|
||
SET @phase2_permission_count = (
|
||
SELECT COUNT(DISTINCT permission_code)
|
||
FROM iam_permission
|
||
WHERE tenant_id = 0 AND deleted = 0
|
||
AND permission_code IN (
|
||
'platform:firstleg:inbound:list', 'platform:firstleg:inbound:view',
|
||
'platform:firstleg:inbound:measure:view', 'platform:firstleg:inbound:measure:edit',
|
||
'platform:firstleg:inbound:measure:submit', 'platform:firstleg:inbound:weight:edit',
|
||
'platform:firstleg:inbound:weight:confirm', 'firstleg:inbound:measurement-review',
|
||
'firstleg:inbound:measurement-review-history', 'firstleg:inbound:exception-release',
|
||
'firstleg:inbound:send-fee', 'firstleg:fba-pricing:rule:view',
|
||
'firstleg:fba-pricing:rule:edit', 'firstleg:fba-pricing:rate:view',
|
||
'firstleg:fba-pricing:rate:edit', 'firstleg:fba-pricing:calculate',
|
||
'firstleg:fba-pricing:confirm', 'firstleg:fba-pricing:view',
|
||
'firstleg:fba-pricing:adjust', 'firstleg:dimension-config:view',
|
||
'firstleg:dimension-config:edit', 'platform:location:list',
|
||
'platform:location:create', 'platform:location:edit', 'platform:location:status',
|
||
'platform:location:delete', 'platform:pallet:list', 'platform:pallet:create',
|
||
'platform:pallet:edit', 'platform:pallet:delete'
|
||
)
|
||
);
|
||
|
||
DROP PROCEDURE IF EXISTS unified_assert_permission_count;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_assert_permission_count()
|
||
BEGIN
|
||
IF @phase2_permission_count <> 30 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:二期 IAM 权限资源未达到 30 项';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_assert_permission_count();
|
||
DROP PROCEDURE IF EXISTS unified_assert_permission_count;
|
||
|
||
-- ============================================================================
|
||
-- 2026-08-05:海外仓阶梯费用基础资料 CRUD 增量
|
||
-- ============================================================================
|
||
USE baiyue_warehouse;
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_rule (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '海外仓阶梯费用规则ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户账号ID,沿用tenant_id物理字段',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '客户公司名称快照',
|
||
currency CHAR(3) NOT NULL COMMENT 'ISO 4217币种三字码',
|
||
initialization_key VARCHAR(191) NULL COMMENT '初始化幂等键',
|
||
created_by BIGINT UNSIGNED NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
|
||
ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
active_customer_id BIGINT UNSIGNED
|
||
GENERATED ALWAYS AS (CASE WHEN deleted = 0 THEN tenant_id ELSE NULL END) STORED
|
||
COMMENT '未删除规则的客户唯一键',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_overseas_fee_active_customer (active_customer_id),
|
||
KEY idx_overseas_fee_page (deleted, updated_at, id),
|
||
KEY idx_overseas_fee_customer_name (customer_name, deleted)
|
||
) ENGINE=InnoDB COMMENT='客户维度海外仓阶梯仓储费规则';
|
||
|
||
CREATE TABLE IF NOT EXISTS wh_overseas_storage_fee_tier (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '海外仓阶梯费用明细ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户账号ID,沿用tenant_id物理字段',
|
||
rule_id BIGINT UNSIGNED NOT NULL COMMENT '阶梯费用规则ID',
|
||
start_day SMALLINT UNSIGNED NOT NULL COMMENT '起始天,含边界',
|
||
end_day SMALLINT UNSIGNED NULL COMMENT '结束天,含边界;NULL表示以上',
|
||
fee_type VARCHAR(32) NOT NULL DEFAULT 'STORAGE' COMMENT '费用类型',
|
||
billing_unit VARCHAR(16) NOT NULL DEFAULT 'CBM' COMMENT '计费单位',
|
||
unit_price DECIMAL(18,6) NOT NULL COMMENT '仓储费标准,可为0',
|
||
sort_no SMALLINT UNSIGNED NOT NULL COMMENT '阶梯排序号',
|
||
created_by BIGINT UNSIGNED NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
|
||
ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
active_start_day SMALLINT UNSIGNED
|
||
GENERATED ALWAYS AS (CASE WHEN deleted = 0 THEN start_day ELSE NULL END) STORED
|
||
COMMENT '未删除阶梯的起始天唯一键',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_overseas_fee_tier_start (rule_id, fee_type, active_start_day),
|
||
KEY idx_overseas_fee_tier_rule (tenant_id, rule_id, deleted, sort_no, id),
|
||
CONSTRAINT chk_overseas_fee_tier_days CHECK (start_day >= 1 AND (end_day IS NULL OR end_day >= start_day)),
|
||
CONSTRAINT chk_overseas_fee_tier_price CHECK (unit_price >= 0)
|
||
) ENGINE=InnoDB COMMENT='海外仓阶梯仓储费明细';
|
||
|
||
-- 兼容已存在的旧版海外仓费用表:补齐新字段及免费档/无上限档约束。
|
||
DROP PROCEDURE IF EXISTS migrate_overseas_storage_fee_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE migrate_overseas_storage_fee_schema()
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_rule'
|
||
AND column_name = 'initialization_key'
|
||
) THEN
|
||
SET @overseas_fee_ddl =
|
||
'ALTER TABLE wh_overseas_storage_fee_rule ADD COLUMN initialization_key VARCHAR(191) NULL COMMENT ''初始化幂等键''';
|
||
PREPARE overseas_fee_statement FROM @overseas_fee_ddl;
|
||
EXECUTE overseas_fee_statement;
|
||
DEALLOCATE PREPARE overseas_fee_statement;
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND column_name = 'fee_type'
|
||
) THEN
|
||
SET @overseas_fee_ddl =
|
||
'ALTER TABLE wh_overseas_storage_fee_tier ADD COLUMN fee_type VARCHAR(32) NOT NULL DEFAULT ''STORAGE'' COMMENT ''费用类型''';
|
||
PREPARE overseas_fee_statement FROM @overseas_fee_ddl;
|
||
EXECUTE overseas_fee_statement;
|
||
DEALLOCATE PREPARE overseas_fee_statement;
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND column_name = 'billing_unit'
|
||
) THEN
|
||
SET @overseas_fee_ddl =
|
||
'ALTER TABLE wh_overseas_storage_fee_tier ADD COLUMN billing_unit VARCHAR(16) NOT NULL DEFAULT ''CBM'' COMMENT ''计费单位''';
|
||
PREPARE overseas_fee_statement FROM @overseas_fee_ddl;
|
||
EXECUTE overseas_fee_statement;
|
||
DEALLOCATE PREPARE overseas_fee_statement;
|
||
END IF;
|
||
|
||
ALTER TABLE wh_overseas_storage_fee_tier
|
||
MODIFY COLUMN end_day SMALLINT UNSIGNED NULL COMMENT '结束天,含边界;NULL表示以上',
|
||
MODIFY COLUMN unit_price DECIMAL(18,6) NOT NULL COMMENT '仓储费标准,可为0';
|
||
|
||
IF EXISTS (
|
||
SELECT 1 FROM wh_overseas_storage_fee_tier
|
||
WHERE start_day < 1
|
||
OR (end_day IS NOT NULL AND end_day < start_day)
|
||
OR unit_price < 0
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = 'Warehouse 迁移失败:海外仓费用阶梯存在非法天数或负价格';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_overseas_storage_fee_tier
|
||
WHERE active_start_day IS NOT NULL
|
||
GROUP BY rule_id, fee_type, active_start_day
|
||
HAVING COUNT(*) > 1
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = 'Warehouse 迁移失败:海外仓费用阶梯目标唯一键存在重复数据';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.table_constraints
|
||
WHERE constraint_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND constraint_name = 'chk_overseas_fee_tier_days'
|
||
AND constraint_type = 'CHECK'
|
||
) THEN
|
||
ALTER TABLE wh_overseas_storage_fee_tier DROP CHECK chk_overseas_fee_tier_days;
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.table_constraints
|
||
WHERE constraint_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND constraint_name = 'chk_overseas_fee_tier_price'
|
||
AND constraint_type = 'CHECK'
|
||
) THEN
|
||
ALTER TABLE wh_overseas_storage_fee_tier DROP CHECK chk_overseas_fee_tier_price;
|
||
END IF;
|
||
ALTER TABLE wh_overseas_storage_fee_tier
|
||
ADD CONSTRAINT chk_overseas_fee_tier_days
|
||
CHECK (start_day >= 1 AND (end_day IS NULL OR end_day >= start_day)),
|
||
ADD CONSTRAINT chk_overseas_fee_tier_price CHECK (unit_price >= 0);
|
||
|
||
IF NOT EXISTS (
|
||
SELECT 1
|
||
FROM (
|
||
SELECT index_name, non_unique,
|
||
GROUP_CONCAT(column_name ORDER BY seq_in_index SEPARATOR ',') AS index_columns
|
||
FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND index_name = 'uk_overseas_fee_tier_start'
|
||
GROUP BY index_name, non_unique
|
||
) existing_index
|
||
WHERE non_unique = 0
|
||
AND index_columns = 'rule_id,fee_type,active_start_day'
|
||
) THEN
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND index_name = 'uk_overseas_fee_tier_start'
|
||
) THEN
|
||
ALTER TABLE wh_overseas_storage_fee_tier DROP INDEX uk_overseas_fee_tier_start;
|
||
END IF;
|
||
ALTER TABLE wh_overseas_storage_fee_tier
|
||
ADD UNIQUE KEY uk_overseas_fee_tier_start (rule_id, fee_type, active_start_day);
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL migrate_overseas_storage_fee_schema();
|
||
DROP PROCEDURE IF EXISTS migrate_overseas_storage_fee_schema;
|
||
|
||
USE baiyue_iam;
|
||
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
|
||
(5100030, 0, 'PLATFORM', 'MENU', 5100000,
|
||
'platform:overseas-storage-fee:list', '海外仓阶梯费用',
|
||
'/customers/overseas-storage-fees', 'GET', 20, 'ACTIVE', 1, 0),
|
||
(5100031, 0, 'PLATFORM', 'BUTTON', 5100030,
|
||
'platform:overseas-storage-fee:create', '新增海外仓阶梯费用',
|
||
NULL, NULL, 10, 'ACTIVE', 1, 0),
|
||
(5100032, 0, 'PLATFORM', 'BUTTON', 5100030,
|
||
'platform:overseas-storage-fee:edit', '编辑海外仓阶梯费用',
|
||
NULL, NULL, 20, 'ACTIVE', 1, 0),
|
||
(5100033, 0, 'PLATFORM', 'BUTTON', 5100030,
|
||
'platform:overseas-storage-fee:delete', '删除海外仓阶梯费用',
|
||
NULL, NULL, 30, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = VALUES(http_method), sort_no = VALUES(sort_no),
|
||
status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0
|
||
FROM iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND permission_code IN (
|
||
'platform:overseas-storage-fee:list',
|
||
'platform:overseas-storage-fee:create',
|
||
'platform:overseas-storage-fee:edit',
|
||
'platform:overseas-storage-fee:delete'
|
||
)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
DROP PROCEDURE IF EXISTS unified_assert_overseas_storage_fee;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_assert_overseas_storage_fee()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_warehouse'
|
||
AND table_name IN ('wh_overseas_storage_fee_rule', 'wh_overseas_storage_fee_tier')) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用表未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_warehouse'
|
||
AND table_name IN ('wh_overseas_storage_fee_rule', 'wh_overseas_storage_fee_tier')
|
||
AND index_name IN ('uk_overseas_fee_active_customer', 'idx_overseas_fee_page',
|
||
'idx_overseas_fee_customer_name', 'uk_overseas_fee_tier_start',
|
||
'idx_overseas_fee_tier_rule')) <> 5 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用索引未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_warehouse'
|
||
AND table_name = 'wh_overseas_storage_fee_rule'
|
||
AND column_name = 'initialization_key'
|
||
AND LOWER(column_type) = 'varchar(191)'
|
||
AND is_nullable = 'YES' AND column_default IS NULL) <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_warehouse'
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND ((column_name = 'fee_type' AND LOWER(column_type) = 'varchar(32)'
|
||
AND is_nullable = 'NO' AND column_default = 'STORAGE')
|
||
OR (column_name = 'billing_unit' AND LOWER(column_type) = 'varchar(16)'
|
||
AND is_nullable = 'NO' AND column_default = 'CBM')
|
||
OR (column_name = 'end_day' AND LOWER(column_type) = 'smallint unsigned'
|
||
AND is_nullable = 'YES' AND column_default IS NULL)
|
||
OR (column_name = 'unit_price' AND LOWER(column_type) = 'decimal(18,6)'
|
||
AND is_nullable = 'NO'))) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用字段签名不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*)
|
||
FROM (
|
||
SELECT non_unique,
|
||
GROUP_CONCAT(column_name ORDER BY seq_in_index SEPARATOR ',') AS index_columns
|
||
FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_warehouse'
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND index_name = 'uk_overseas_fee_tier_start'
|
||
GROUP BY index_name, non_unique
|
||
) exact_index
|
||
WHERE non_unique = 0
|
||
AND index_columns = 'rule_id,fee_type,active_start_day') <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.table_constraints
|
||
WHERE constraint_schema = 'baiyue_warehouse'
|
||
AND table_name = 'wh_overseas_storage_fee_tier'
|
||
AND constraint_type = 'CHECK'
|
||
AND constraint_name IN ('chk_overseas_fee_tier_days',
|
||
'chk_overseas_fee_tier_price')) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用唯一键或 CHECK 结构不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT permission_code) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND status = 'ACTIVE' AND deleted = 0
|
||
AND permission_code IN (
|
||
'platform:overseas-storage-fee:list', 'platform:overseas-storage-fee:create',
|
||
'platform:overseas-storage-fee:edit', 'platform:overseas-storage-fee:delete')) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:海外仓阶梯费用权限未完整创建';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_assert_overseas_storage_fee();
|
||
DROP PROCEDURE IF EXISTS unified_assert_overseas_storage_fee;
|
||
|
||
-- 海外仓阶梯费用结构校验已完成;最终 PASS 统一在脚本末尾输出。
|
||
|
||
-- ============================================================
|
||
-- 二期:费用项目主数据与入库单应收应付费用明细
|
||
-- ============================================================
|
||
USE baiyue_firstleg;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_fee_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '费用项目ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共费用项目作用域,固定为0;物理字段名沿用 tenant_id',
|
||
category VARCHAR(64) NOT NULL COMMENT '费用分类',
|
||
trade_scope VARCHAR(20) NOT NULL COMMENT 'DOMESTIC国内、INTERNATIONAL国际',
|
||
fee_code VARCHAR(50) NOT NULL COMMENT '稳定费用编码;平台公共主数据固定编码',
|
||
fee_type_name VARCHAR(128) NOT NULL COMMENT '费用类型名称',
|
||
pickup_payable TINYINT(1) NOT NULL DEFAULT 0 COMMENT '提柜应付是否参与',
|
||
pickup_receivable TINYINT(1) NOT NULL DEFAULT 0 COMMENT '提柜应收是否参与',
|
||
warehouse_payable TINYINT(1) NOT NULL DEFAULT 0 COMMENT '仓储应付是否参与',
|
||
warehouse_receivable TINYINT(1) NOT NULL DEFAULT 0 COMMENT '仓储应收是否参与',
|
||
allocation_fee TINYINT(1) NOT NULL DEFAULT 0 COMMENT '平摊费用是否参与',
|
||
description VARCHAR(500) NULL COMMENT '说明',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE或DISABLED',
|
||
created_by BIGINT NULL,
|
||
created_by_name VARCHAR(64) NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
updated_by BIGINT NULL,
|
||
updated_by_name VARCHAR(64) NULL,
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0,
|
||
version INT NOT NULL DEFAULT 0,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_fee_item_code (fee_code),
|
||
KEY idx_fee_item_query (tenant_id, trade_scope, category, status, deleted),
|
||
KEY idx_fee_item_name (tenant_id, fee_type_name, deleted)
|
||
) ENGINE=InnoDB COMMENT='平台公共费用项目主数据';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_fee_detail (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '入库费用明细ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID(客户隔离键);费用明细引用平台公共费用项目',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||
fee_item_id BIGINT UNSIGNED NOT NULL COMMENT '绑定费用项目ID',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
direction VARCHAR(16) NOT NULL COMMENT 'RECEIVABLE应收、PAYABLE应付',
|
||
business_date DATE NOT NULL COMMENT '业务发生日',
|
||
fee_category_snapshot VARCHAR(64) NOT NULL COMMENT '费用分类快照',
|
||
trade_scope_snapshot VARCHAR(20) NOT NULL COMMENT '国内国际快照',
|
||
fee_type_name_snapshot VARCHAR(128) NOT NULL COMMENT '费用类型名称快照',
|
||
channel_code VARCHAR(64) NULL COMMENT '渠道编码快照',
|
||
channel_name VARCHAR(128) NULL COMMENT '渠道名称快照',
|
||
destination_code VARCHAR(32) NULL COMMENT '目的仓编码快照',
|
||
destination_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
package_count INT NULL COMMENT '件数快照',
|
||
actual_gross_weight_kg DECIMAL(18,4) NULL COMMENT '仓库实测毛重',
|
||
actual_volume_cbm DECIMAL(18,6) NULL COMMENT '体积',
|
||
volume_weight_kg DECIMAL(18,4) NULL COMMENT '材积重',
|
||
chargeable_weight_kg DECIMAL(18,4) NULL COMMENT '计费重量',
|
||
chargeable_volume_cbm DECIMAL(18,6) NULL COMMENT '计费体积',
|
||
unit_price DECIMAL(18,4) NOT NULL COMMENT '单价',
|
||
currency CHAR(3) NOT NULL COMMENT '币种',
|
||
amount DECIMAL(18,2) NOT NULL COMMENT '金额',
|
||
remark VARCHAR(500) NULL COMMENT '备注',
|
||
review_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING、APPROVED、REJECTED',
|
||
reviewer_id BIGINT NULL COMMENT '审核人ID',
|
||
reviewer_name VARCHAR(64) NULL COMMENT '审核人名称快照',
|
||
reviewed_at DATETIME(3) NULL COMMENT '审核时间',
|
||
review_reason VARCHAR(500) NULL COMMENT '审核驳回原因',
|
||
created_by BIGINT NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
updated_by BIGINT NULL,
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0,
|
||
version INT NOT NULL DEFAULT 0,
|
||
PRIMARY KEY (id),
|
||
KEY idx_inbound_fee_detail (tenant_id, inbound_id, business_date, deleted),
|
||
KEY idx_inbound_fee_review (tenant_id, review_status, business_date, deleted),
|
||
KEY idx_inbound_fee_item (tenant_id, fee_item_id, deleted),
|
||
CONSTRAINT fk_inbound_fee_detail_order FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_inbound_fee_detail_item FOREIGN KEY (fee_item_id) REFERENCES fl_fee_item (id)
|
||
) ENGINE=InnoDB COMMENT='入库单应收应付费用明细';
|
||
|
||
-- 既有表同步字段注释:tenant_id 当前承载客户 ID,不是上级租户 ID。
|
||
SET @fee_tenant_comment = '当前客户ID(客户隔离键);物理字段名沿用 tenant_id,当前不表示上级租户';
|
||
SET @ddl = IF(
|
||
(SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_fee_item'
|
||
AND column_name = 'tenant_id' AND column_comment = @fee_tenant_comment) = 1,
|
||
'SELECT 1',
|
||
'ALTER TABLE fl_fee_item MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT ''当前客户ID(客户隔离键);物理字段名沿用 tenant_id,当前不表示上级租户'''
|
||
);
|
||
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
SET @ddl = IF(
|
||
(SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_fee_detail'
|
||
AND column_name = 'tenant_id' AND column_comment = @fee_tenant_comment) = 1,
|
||
'SELECT 1',
|
||
'ALTER TABLE fl_inbound_fee_detail MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT ''当前客户ID(客户隔离键);物理字段名沿用 tenant_id,当前不表示上级租户'''
|
||
);
|
||
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
|
||
USE baiyue_iam;
|
||
|
||
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
|
||
(5102070, 0, 'PLATFORM', 'MENU', 5103000, 'firstleg:fee-item:view',
|
||
'费用项目', '/master-data/fee-items', NULL, 40, 'ACTIVE', 1, 0),
|
||
(5102071, 0, 'PLATFORM', 'BUTTON', 5102070, 'firstleg:fee-item:create',
|
||
'新增费用项目', NULL, NULL, 10, 'ACTIVE', 1, 0),
|
||
(5102072, 0, 'PLATFORM', 'BUTTON', 5102070, 'firstleg:fee-item:edit',
|
||
'编辑或停用费用项目', NULL, NULL, 20, 'ACTIVE', 1, 0),
|
||
(5102073, 0, 'PLATFORM', 'BUTTON', 5102070, 'firstleg:fee-item:delete',
|
||
'删除费用项目', NULL, NULL, 30, 'ACTIVE', 1, 0),
|
||
(5102081, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:fee-detail:view',
|
||
'查看入库费用明细', NULL, NULL, 100, 'ACTIVE', 1, 0),
|
||
(5102082, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:fee-detail:edit',
|
||
'新增编辑入库费用明细', NULL, NULL, 110, 'ACTIVE', 1, 0),
|
||
(5102083, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:fee-detail:delete',
|
||
'删除入库费用明细', NULL, NULL, 120, 'ACTIVE', 1, 0),
|
||
(5102084, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:fee-detail:review',
|
||
'审核入库费用明细', NULL, NULL, 130, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
sort_no = VALUES(sort_no), status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT 4000001, id, 0
|
||
FROM iam_permission
|
||
WHERE id IN (5102070, 5102071, 5102072, 5102073, 5102081, 5102082, 5102083, 5102084)
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
-- 费用项目、入库费用明细及按钮权限结构校验延后至脚本末尾统一执行。
|
||
|
||
-- ============================================================
|
||
-- 可靠审计:本地 Outbox、RocketMQ 消费幂等和死信补偿
|
||
-- ============================================================
|
||
-- 可靠审计 Outbox、消费幂等和死信补偿增量(MySQL 8)
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_audit;
|
||
|
||
DROP PROCEDURE IF EXISTS create_reliable_audit_outbox;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE create_reliable_audit_outbox(IN schema_name VARCHAR(64))
|
||
BEGIN
|
||
SET @ddl = CONCAT(
|
||
'CREATE TABLE IF NOT EXISTS \`', schema_name, '\`.sys_audit_outbox (',
|
||
'event_id VARCHAR(64) NOT NULL,event_type VARCHAR(64) NOT NULL,resource_type VARCHAR(64) NOT NULL,',
|
||
'resource_id VARCHAR(128) NULL,service_name VARCHAR(64) NULL,payload_json JSON NOT NULL,',
|
||
'status VARCHAR(16) NOT NULL DEFAULT ''PENDING'',attempt_count INT NOT NULL DEFAULT 0,',
|
||
'compensation_count INT NOT NULL DEFAULT 0,next_retry_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),',
|
||
'lease_owner VARCHAR(128) NULL,lease_until DATETIME(3) NULL,last_error VARCHAR(1000) NULL,',
|
||
'dead_at DATETIME(3) NULL,sent_at DATETIME(3) 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(event_id),KEY idx_audit_outbox_publish(status,next_retry_at,lease_until,created_at),',
|
||
'KEY idx_audit_outbox_retention(status,sent_at)) ENGINE=InnoDB COMMENT=''可靠审计事件本地发送箱'''
|
||
);
|
||
PREPARE stmt FROM @ddl;
|
||
EXECUTE stmt;
|
||
DEALLOCATE PREPARE stmt;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL create_reliable_audit_outbox('baiyue_auth');
|
||
CALL create_reliable_audit_outbox('baiyue_iam');
|
||
CALL create_reliable_audit_outbox('baiyue_order');
|
||
CALL create_reliable_audit_outbox('baiyue_firstleg');
|
||
CALL create_reliable_audit_outbox('baiyue_warehouse');
|
||
DROP PROCEDURE IF EXISTS create_reliable_audit_outbox;
|
||
|
||
USE baiyue_audit;
|
||
DROP PROCEDURE IF EXISTS add_audit_event_id;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE add_audit_event_id()
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA='baiyue_audit' AND TABLE_NAME='sys_audit_log' AND COLUMN_NAME='event_id') THEN
|
||
ALTER TABLE sys_audit_log ADD COLUMN event_id VARCHAR(64) NULL COMMENT '审计事件全局唯一ID' AFTER id;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS
|
||
WHERE TABLE_SCHEMA='baiyue_audit' AND TABLE_NAME='sys_audit_log' AND INDEX_NAME='uk_audit_event_id') THEN
|
||
ALTER TABLE sys_audit_log ADD UNIQUE KEY uk_audit_event_id(event_id);
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL add_audit_event_id();
|
||
DROP PROCEDURE IF EXISTS add_audit_event_id;
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_audit_dead_letter (
|
||
id BIGINT NOT NULL AUTO_INCREMENT,event_id VARCHAR(64) NOT NULL,payload_text LONGTEXT NOT NULL,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',retry_count INT NOT NULL DEFAULT 0,
|
||
next_retry_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),lease_owner VARCHAR(128) NULL,
|
||
lease_until DATETIME(3) NULL,last_error VARCHAR(1000) NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),processed_at DATETIME(3) NULL,
|
||
PRIMARY KEY(id),UNIQUE KEY uk_audit_dead_event(event_id),
|
||
KEY idx_audit_dead_retry(status,next_retry_at,lease_until)
|
||
) ENGINE=InnoDB COMMENT='审计消息死信补偿表';
|
||
|
||
-- ============================================================
|
||
-- 三期智能装柜、预装冻结与装柜清单
|
||
-- 来源:firstleg/src/main/resources/db/phase3-smart-loading-schema.sql
|
||
-- ============================================================
|
||
SET NAMES utf8mb4;
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_firstleg
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_firstleg;
|
||
|
||
-- 兼容已执行二期初始化的存量库:PDA 拣货需要累计每个托盘分配已拣箱数。
|
||
DROP PROCEDURE IF EXISTS phase3_add_allocation_picked_count;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_add_allocation_picked_count()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_pallet_allocation'
|
||
) AND NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_pallet_allocation'
|
||
AND column_name = 'picked_package_count'
|
||
) THEN
|
||
ALTER TABLE fl_inbound_pallet_allocation
|
||
ADD COLUMN picked_package_count INT UNSIGNED NOT NULL DEFAULT 0
|
||
COMMENT '已拣货箱数' AFTER allocated_package_count;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_add_allocation_picked_count();
|
||
DROP PROCEDURE phase3_add_allocation_picked_count;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID(雪花ID)',
|
||
booking_no VARCHAR(40) NOT NULL COMMENT '平台唯一货柜预订单号',
|
||
container_no VARCHAR(64) NULL COMMENT '集装箱号;确认前允许修订',
|
||
cabinet_no VARCHAR(64) NOT NULL COMMENT '平台唯一内部柜号',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '装柜仓库ID;平台公共资源',
|
||
warehouse_name VARCHAR(128) NOT NULL COMMENT '装柜仓库名称快照',
|
||
bill_of_lading_no VARCHAR(128) NULL COMMENT '提单号',
|
||
vessel_name VARCHAR(128) NULL COMMENT '船名快照',
|
||
voyage_no VARCHAR(64) NULL COMMENT '航次;确认前允许修订',
|
||
container_type VARCHAR(32) NOT NULL COMMENT '箱型,例如20GP、40GP、40HQ',
|
||
shipping_company_id BIGINT UNSIGNED NULL COMMENT '船司ID快照',
|
||
shipping_company_name VARCHAR(128) NULL COMMENT '船司名称快照',
|
||
departure_port_code VARCHAR(32) NULL COMMENT '起运港编码快照',
|
||
departure_port_name VARCHAR(128) NULL COMMENT '起运港名称快照',
|
||
destination_port_code VARCHAR(32) NULL COMMENT '目的港编码快照',
|
||
destination_port_name VARCHAR(128) NULL COMMENT '目的港名称快照',
|
||
destination_warehouse_code VARCHAR(64) NULL COMMENT '目的仓编码快照',
|
||
destination_warehouse_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
capacity_cbm DECIMAL(18,4) NULL COMMENT '货柜容量m3',
|
||
max_weight_kg DECIMAL(18,3) NULL COMMENT '货柜最大承重kg',
|
||
terminal_trucking_company VARCHAR(128) NULL COMMENT '码头拖车公司',
|
||
loading_at DATETIME(3) NULL COMMENT '计划或实际装柜时间',
|
||
estimated_sailing_at DATETIME(3) NULL COMMENT '预计开船时间',
|
||
actual_sailing_at DATETIME(3) NULL COMMENT '实际开船时间',
|
||
estimated_port_arrival_at DATETIME(3) NULL COMMENT '预计到港时间',
|
||
actual_port_arrival_at DATETIME(3) NULL COMMENT '实际到港时间',
|
||
estimated_warehouse_arrival_at DATETIME(3) NULL COMMENT '预计到仓时间',
|
||
actual_warehouse_arrival_at DATETIME(3) 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_container_booking_no (booking_no),
|
||
UNIQUE KEY uk_container_booking_cabinet (cabinet_no),
|
||
KEY idx_container_booking_container (container_no),
|
||
KEY idx_container_booking_warehouse (warehouse_id, updated_at, id),
|
||
KEY idx_container_booking_voyage (voyage_no, updated_at),
|
||
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)
|
||
) ENGINE=InnoDB COMMENT='平台货柜预订资料;预装状态唯一来源为装柜计划';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_plan (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID(雪花ID)',
|
||
plan_no VARCHAR(40) NOT NULL COMMENT '平台唯一装柜计划号',
|
||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID',
|
||
plan_status VARCHAR(24) NOT NULL DEFAULT 'EDITING'
|
||
COMMENT '唯一装柜状态:EDITING、LOADING、COMPLETED',
|
||
push_idempotency_key VARCHAR(128) NULL COMMENT '推送预装幂等键',
|
||
preload_pushed_by BIGINT UNSIGNED NULL COMMENT '推送预装操作人ID',
|
||
preload_pushed_by_name VARCHAR(64) NULL COMMENT '推送预装操作人名称快照',
|
||
preload_pushed_at DATETIME(3) NULL COMMENT '开始装柜并进入LOADING的时间',
|
||
preload_completed_by BIGINT UNSIGNED NULL COMMENT '预装完成操作人ID',
|
||
preload_completed_by_name VARCHAR(64) NULL COMMENT '预装完成操作人名称快照',
|
||
preload_completed_at DATETIME(3) NULL COMMENT '预装完成时间',
|
||
confirmed_by BIGINT UNSIGNED NULL COMMENT '确认操作人ID',
|
||
confirmed_by_name VARCHAR(64) NULL COMMENT '确认操作人名称快照',
|
||
confirmed_at DATETIME(3) NULL COMMENT '确认时间',
|
||
remark VARCHAR(500) 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_loading_plan_no (plan_no),
|
||
UNIQUE KEY uk_loading_plan_booking (container_booking_id),
|
||
UNIQUE KEY uk_loading_plan_push_key (push_idempotency_key),
|
||
KEY idx_loading_plan_status (plan_status, updated_at, id),
|
||
CONSTRAINT fk_loading_plan_booking FOREIGN KEY (container_booking_id)
|
||
REFERENCES fl_container_booking (id),
|
||
CONSTRAINT chk_loading_plan_status
|
||
CHECK (plan_status IN ('EDITING', 'LOADING', 'COMPLETED'))
|
||
) ENGINE=InnoDB COMMENT='平台智能装柜计划与唯一预装状态源';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_plan_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划明细ID(雪花ID)',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID',
|
||
account_id BIGINT UNSIGNED NOT NULL COMMENT '客户账号ID;装柜业务唯一客户归属字段',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT 'Firstleg入库单ID',
|
||
inbound_detail_id BIGINT UNSIGNED NOT NULL COMMENT '订单货物或入库货物明细ID,不建立跨库外键',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID快照,不建立跨库外键',
|
||
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓ID快照',
|
||
destination_warehouse_code VARCHAR(64) NULL COMMENT '目的仓编码快照',
|
||
destination_warehouse_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
sku_code VARCHAR(128) NULL COMMENT 'SKU编码快照',
|
||
product_name_zh VARCHAR(255) NULL COMMENT '中文品名快照',
|
||
product_name_en VARCHAR(255) NULL COMMENT '英文品名快照',
|
||
product_image_file_id BIGINT UNSIGNED NULL COMMENT '产品图片File对象ID',
|
||
product_image_url VARCHAR(1000) NULL COMMENT '产品图片访问地址快照',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '本次装柜箱数',
|
||
product_quantity INT UNSIGNED NULL COMMENT '产品总数量',
|
||
units_per_carton INT UNSIGNED NULL COMMENT '每箱产品数',
|
||
pallet_count INT UNSIGNED NULL COMMENT '托盘数',
|
||
gross_weight_kg DECIMAL(18,3) NULL COMMENT '本次装柜毛重kg',
|
||
net_weight_kg DECIMAL(18,3) NULL COMMENT '本次装柜净重kg',
|
||
dimensional_weight_kg DECIMAL(18,3) NULL COMMENT '本次装柜材积重kg',
|
||
volume_cbm DECIMAL(18,4) NULL COMMENT '本次装柜体积m3',
|
||
fba_tracking_no VARCHAR(128) NULL COMMENT 'FBA追踪号快照',
|
||
shipment_no VARCHAR(128) NULL COMMENT '货件编号快照',
|
||
shipping_mark VARCHAR(500) NULL COMMENT '唛头快照',
|
||
load_sequence INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '装柜顺序',
|
||
position_snapshot JSON NULL COMMENT '智能装柜位置与摆放结果快照',
|
||
internal_remark VARCHAR(1000) NULL COMMENT '平台内部备注',
|
||
customer_remark VARCHAR(1000) NULL COMMENT '客户备注快照',
|
||
warehouse_remark VARCHAR(1000) NULL COMMENT '仓库备注快照',
|
||
frozen_at DATETIME(3) NULL COMMENT '装柜完成后的永久冻结时间',
|
||
frozen_by BIGINT UNSIGNED NULL COMMENT '冻结操作人ID',
|
||
removed_by BIGINT UNSIGNED NULL COMMENT '推送前逻辑删除操作人ID',
|
||
removed_by_name VARCHAR(64) NULL COMMENT '推送前逻辑删除操作人名称快照',
|
||
removed_at DATETIME(3) NULL COMMENT '推送前逻辑删除时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '推送前逻辑删除标记;冻结后不可修改',
|
||
active_inbound_detail_id BIGINT UNSIGNED GENERATED ALWAYS AS (
|
||
CASE WHEN deleted = 0 THEN inbound_detail_id ELSE NULL END
|
||
) STORED COMMENT '未删除明细唯一锁;删除后返回NULL释放分配资格',
|
||
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_loading_item_plan_detail (loading_plan_id, active_inbound_detail_id),
|
||
KEY idx_loading_item_active_detail (account_id, active_inbound_detail_id),
|
||
KEY idx_loading_item_plan (loading_plan_id, deleted, load_sequence, id),
|
||
KEY idx_loading_item_account (account_id, created_at, id),
|
||
KEY idx_loading_item_inbound (account_id, inbound_id, deleted),
|
||
KEY idx_loading_item_order (account_id, order_id, deleted),
|
||
CONSTRAINT fk_loading_item_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT chk_loading_item_account CHECK (account_id > 0),
|
||
CONSTRAINT chk_loading_item_package CHECK (package_count > 0),
|
||
CONSTRAINT chk_loading_item_product_quantity CHECK (product_quantity IS NULL OR product_quantity > 0),
|
||
CONSTRAINT chk_loading_item_units CHECK (units_per_carton IS NULL OR units_per_carton > 0),
|
||
CONSTRAINT chk_loading_item_pallet CHECK (pallet_count IS NULL OR pallet_count >= 0),
|
||
CONSTRAINT chk_loading_item_weight CHECK (
|
||
(gross_weight_kg IS NULL OR gross_weight_kg >= 0)
|
||
AND (net_weight_kg IS NULL OR net_weight_kg >= 0)
|
||
AND (dimensional_weight_kg IS NULL OR dimensional_weight_kg >= 0)
|
||
),
|
||
CONSTRAINT chk_loading_item_volume CHECK (volume_cbm IS NULL OR volume_cbm >= 0),
|
||
CONSTRAINT chk_loading_item_deleted CHECK (deleted IN (0, 1))
|
||
) ENGINE=InnoDB COMMENT='智能装柜客户货物明细;客户归属仅在明细层';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_container_document (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜文件关联ID(雪花ID)',
|
||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID',
|
||
document_type VARCHAR(32) NOT NULL COMMENT 'CUSTOMS_CLEARANCE、DELIVERY_ORDER、PICKUP_ORDER',
|
||
file_id BIGINT UNSIGNED NOT NULL COMMENT 'File服务文件ID,不建立跨库外键',
|
||
file_name VARCHAR(255) NOT NULL COMMENT '文件名称快照',
|
||
file_url VARCHAR(1000) NULL COMMENT '文件访问地址快照',
|
||
uploaded_by BIGINT UNSIGNED NOT NULL COMMENT '上传操作人ID',
|
||
uploaded_by_name VARCHAR(64) NULL COMMENT '上传操作人名称快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) 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_container_document_type (container_booking_id, document_type),
|
||
KEY idx_container_document_file (file_id),
|
||
CONSTRAINT fk_container_document_booking FOREIGN KEY (container_booking_id)
|
||
REFERENCES fl_container_booking (id),
|
||
CONSTRAINT chk_container_document_type
|
||
CHECK (document_type IN ('CUSTOMS_CLEARANCE', 'DELIVERY_ORDER', 'PICKUP_ORDER'))
|
||
) ENGINE=InnoDB COMMENT='平台货柜清关、DO与PU文件关联';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_plan_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '装柜操作历史ID(雪花ID)',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID',
|
||
loading_plan_item_id BIGINT UNSIGNED NULL COMMENT '装柜计划明细ID',
|
||
operation_id VARCHAR(128) NOT NULL COMMENT '操作幂等ID',
|
||
action_type VARCHAR(32) NOT NULL
|
||
COMMENT 'CREATE_PLAN、ADD_ITEM、ADJUST_ITEM、REMOVE_ITEM、START_LOADING、COMPLETE_LOADING、PICK_ITEM',
|
||
from_status VARCHAR(24) NULL COMMENT '变更前状态',
|
||
to_status VARCHAR(24) NULL COMMENT '变更后状态',
|
||
before_snapshot JSON NULL COMMENT '变更前快照',
|
||
after_snapshot JSON NULL COMMENT '变更后快照',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
operation_reason VARCHAR(500) NULL COMMENT '操作原因',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_history_operation (operation_id),
|
||
KEY idx_loading_history_plan (loading_plan_id, created_at, id),
|
||
KEY idx_loading_history_item (loading_plan_item_id, created_at, id),
|
||
CONSTRAINT fk_loading_history_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT fk_loading_history_item FOREIGN KEY (loading_plan_item_id)
|
||
REFERENCES fl_loading_plan_item (id)
|
||
) ENGINE=InnoDB COMMENT='智能装柜追加式不可变操作历史';
|
||
|
||
-- fl_loading_pick_task 曾被另一套入库汇总模型复用。先按字段签名识别并迁走,
|
||
-- 再创建智能装柜任务表;未知或混合结构必须人工处理,禁止静默套用错误表型。
|
||
DROP PROCEDURE IF EXISTS assert_loading_pick_task_shapes;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_loading_pick_task_shapes(IN require_both TINYINT)
|
||
BEGIN
|
||
DECLARE source_exists INT DEFAULT 0;
|
||
DECLARE target_exists INT DEFAULT 0;
|
||
DECLARE source_total INT DEFAULT 0;
|
||
DECLARE source_smart INT DEFAULT 0;
|
||
DECLARE source_inbound INT DEFAULT 0;
|
||
DECLARE target_total INT DEFAULT 0;
|
||
DECLARE target_inbound INT DEFAULT 0;
|
||
SELECT COUNT(*) INTO source_exists FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
|
||
SELECT COUNT(*) INTO target_exists FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
|
||
IF source_exists = 1 THEN
|
||
SELECT COUNT(*) INTO source_total FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
|
||
SELECT COUNT(*) INTO source_smart FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'container_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'NO')
|
||
OR (column_name = 'task_status' AND LOWER(column_type) = 'varchar(16)' AND is_nullable = 'NO' AND column_default = 'LOADING')
|
||
OR (column_name = 'started_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'started_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'started_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0'));
|
||
SELECT COUNT(*) INTO source_inbound FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
|
||
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
|
||
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%'));
|
||
IF NOT (
|
||
(source_total = 11 AND source_smart = 11
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_container' AND ((seq_in_index = 1 AND column_name = 'container_no') OR (seq_in_index = 2 AND column_name = 'task_status') OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan' AND referenced_column_name = 'id') = 1)
|
||
OR
|
||
(source_total = 12 AND source_inbound = 12
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0 AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'inbound_id'))) = 2
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_status' AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'status') OR (seq_in_index = 3 AND column_name = 'updated_at'))) = 3
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order' AND referenced_column_name = 'id') = 1)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务迁移失败:fl_loading_pick_task 不是完整 canonical 表型';
|
||
END IF;
|
||
END IF;
|
||
IF target_exists = 1 THEN
|
||
SELECT COUNT(*) INTO target_total FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
|
||
SELECT COUNT(*) INTO target_inbound FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
|
||
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
|
||
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%'));
|
||
IF target_total <> 12 OR target_inbound <> 12
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0 AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'inbound_id'))) <> 2
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND index_name = 'idx_loading_pick_status' AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'status') OR (seq_in_index = 3 AND column_name = 'updated_at'))) <> 3
|
||
OR (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order' AND referenced_column_name = 'id') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是完整 canonical 表型';
|
||
END IF;
|
||
END IF;
|
||
IF require_both = 1 AND (source_exists <> 1 OR target_exists <> 1 OR source_smart <> 11) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务终验失败:两张 canonical 表未同时就绪';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_loading_pick_task_shapes(0);
|
||
|
||
DROP PROCEDURE IF EXISTS migrate_legacy_loading_pick_task;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE migrate_legacy_loading_pick_task()
|
||
BEGIN
|
||
DECLARE source_exists INT DEFAULT 0;
|
||
DECLARE source_smart_columns INT DEFAULT 0;
|
||
DECLARE source_inbound_columns INT DEFAULT 0;
|
||
DECLARE target_exists INT DEFAULT 0;
|
||
DECLARE target_inbound_columns INT DEFAULT 0;
|
||
|
||
SELECT COUNT(*) INTO source_exists
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
|
||
IF source_exists = 1 THEN
|
||
SELECT COUNT(*) INTO source_smart_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name IN ('loading_plan_id', 'container_no', 'task_status');
|
||
SELECT COUNT(*) INTO source_inbound_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count');
|
||
|
||
IF source_smart_columns = 3 AND source_inbound_columns = 0 THEN
|
||
SET source_exists = 1;
|
||
ELSEIF source_smart_columns = 0 AND source_inbound_columns = 5 THEN
|
||
SELECT COUNT(*) INTO target_exists
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_loading_pick_task';
|
||
IF target_exists = 0 THEN
|
||
RENAME TABLE fl_loading_pick_task TO fl_inbound_loading_pick_task;
|
||
ELSE
|
||
SELECT COUNT(*) INTO target_inbound_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_loading_pick_task'
|
||
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count');
|
||
IF target_inbound_columns <> 5 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是入库汇总结构';
|
||
END IF;
|
||
INSERT INTO fl_inbound_loading_pick_task
|
||
(id, tenant_id, inbound_id, status, version, expected_count, picked_count,
|
||
remark, updated_by, updated_by_name, created_at, updated_at)
|
||
SELECT source.id, source.tenant_id, source.inbound_id, source.status, source.version,
|
||
source.expected_count, source.picked_count, source.remark, source.updated_by,
|
||
source.updated_by_name, source.created_at, source.updated_at
|
||
FROM fl_loading_pick_task source
|
||
LEFT JOIN fl_inbound_loading_pick_task target
|
||
ON target.id = source.id
|
||
WHERE target.id IS NULL;
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM fl_loading_pick_task source
|
||
LEFT JOIN fl_inbound_loading_pick_task target ON target.id = source.id
|
||
WHERE target.id IS NULL
|
||
OR NOT (target.tenant_id <=> source.tenant_id)
|
||
OR NOT (target.inbound_id <=> source.inbound_id)
|
||
OR NOT (target.status <=> source.status)
|
||
OR NOT (target.version <=> source.version)
|
||
OR NOT (target.expected_count <=> source.expected_count)
|
||
OR NOT (target.picked_count <=> source.picked_count)
|
||
OR NOT (target.remark <=> source.remark)
|
||
OR NOT (target.updated_by <=> source.updated_by)
|
||
OR NOT (target.updated_by_name <=> source.updated_by_name)
|
||
OR NOT (target.created_at <=> source.created_at)
|
||
OR NOT (target.updated_at <=> source.updated_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '拣货任务迁移失败:新旧入库汇总任务存在主键冲突';
|
||
END IF;
|
||
DROP TABLE fl_loading_pick_task;
|
||
END IF;
|
||
ELSE
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_loading_pick_task 字段签名未知或混合';
|
||
END IF;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL migrate_legacy_loading_pick_task();
|
||
DROP PROCEDURE migrate_legacy_loading_pick_task;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_task (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划ID',
|
||
container_no VARCHAR(64) NOT NULL COMMENT '集装箱号快照',
|
||
task_status VARCHAR(16) NOT NULL DEFAULT 'LOADING' COMMENT 'LOADING、COMPLETED',
|
||
started_by BIGINT UNSIGNED NOT NULL COMMENT '开始装柜人',
|
||
started_by_name VARCHAR(64) NULL COMMENT '开始装柜人名称',
|
||
started_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '开始时间',
|
||
completed_by BIGINT UNSIGNED NULL COMMENT '完成装柜人',
|
||
completed_by_name VARCHAR(64) NULL COMMENT '完成装柜人名称',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_pick_task_plan (loading_plan_id),
|
||
KEY idx_loading_pick_container (container_no, task_status, started_at),
|
||
CONSTRAINT fk_loading_pick_task_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT chk_loading_pick_task_status CHECK (task_status IN ('LOADING', 'COMPLETED'))
|
||
) ENGINE=InnoDB COMMENT='PDA装柜拣货任务';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货明细ID',
|
||
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||
loading_plan_item_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划明细ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '父入库单ID',
|
||
inbound_detail_id BIGINT UNSIGNED NOT NULL COMMENT '订单货物明细ID',
|
||
planned_package_count INT UNSIGNED NOT NULL COMMENT '计划拣货箱数;上层拆分按业务规则向上取整',
|
||
picked_package_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已拣货箱数',
|
||
item_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING、PICKING、PICKED、PARTIAL、SKIPPED',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
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_loading_pick_item_plan_item (loading_plan_item_id),
|
||
KEY idx_loading_pick_item_task (pick_task_id, item_status, id),
|
||
CONSTRAINT fk_loading_pick_item_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT fk_loading_pick_item_plan_item FOREIGN KEY (loading_plan_item_id) REFERENCES fl_loading_plan_item (id),
|
||
CONSTRAINT chk_loading_pick_item_planned CHECK (planned_package_count > 0),
|
||
CONSTRAINT chk_loading_pick_item_count CHECK (picked_package_count <= planned_package_count),
|
||
CONSTRAINT chk_loading_pick_item_status CHECK (item_status IN ('PENDING', 'PICKING', 'PICKED', 'PARTIAL', 'SKIPPED'))
|
||
) ENGINE=InnoDB COMMENT='PDA装柜拣货明细';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货操作历史ID',
|
||
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||
pick_item_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货明细ID',
|
||
operation_id VARCHAR(128) NOT NULL COMMENT '客户端幂等操作号',
|
||
request_hash CHAR(64) NOT NULL COMMENT '请求指纹',
|
||
action_type VARCHAR(24) NOT NULL DEFAULT 'PICK' COMMENT 'PICK、PARTIAL、SKIP',
|
||
picked_package_count INT UNSIGNED NOT NULL COMMENT '本次拣货箱数',
|
||
operation_reason VARCHAR(500) NULL COMMENT '部分拣货或跳拣原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_pick_history_operation (operation_id),
|
||
KEY idx_loading_pick_history_item (pick_item_id, created_at),
|
||
CONSTRAINT fk_loading_pick_history_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT fk_loading_pick_history_item FOREIGN KEY (pick_item_id) REFERENCES fl_loading_pick_item (id)
|
||
) ENGINE=InnoDB COMMENT='PDA拣货幂等和操作审计历史';
|
||
|
||
DROP PROCEDURE IF EXISTS repair_phase3_loading_pick_task_foreign_keys;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE repair_phase3_loading_pick_task_foreign_keys()
|
||
BEGIN
|
||
DECLARE item_fk_count INT DEFAULT 0;
|
||
DECLARE item_target_count INT DEFAULT 0;
|
||
DECLARE history_fk_count INT DEFAULT 0;
|
||
DECLARE history_target_count INT DEFAULT 0;
|
||
DECLARE item_drop_clauses TEXT;
|
||
DECLARE history_drop_clauses TEXT;
|
||
|
||
IF EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item item
|
||
LEFT JOIN fl_loading_pick_task task ON task.id = item.pick_task_id
|
||
WHERE task.id IS NULL
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '柜次PDA明细存在无法迁移到智能装柜任务的引用';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_history history
|
||
LEFT JOIN fl_loading_pick_task task ON task.id = history.pick_task_id
|
||
WHERE task.id IS NULL
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '柜次PDA历史存在无法迁移到智能装柜任务的引用';
|
||
END IF;
|
||
|
||
SELECT COUNT(*),
|
||
SUM(k.referenced_table_name = 'fl_loading_pick_task'
|
||
AND k.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = k.constraint_schema
|
||
AND peer.table_name = k.table_name
|
||
AND peer.constraint_name = k.constraint_name) = 1),
|
||
GROUP_CONCAT(CONCAT('DROP FOREIGN KEY `', REPLACE(k.constraint_name, '`', '``'), '`')
|
||
ORDER BY k.constraint_name SEPARATOR ', ')
|
||
INTO item_fk_count, item_target_count, item_drop_clauses
|
||
FROM information_schema.key_column_usage k
|
||
WHERE k.constraint_schema = 'baiyue_firstleg'
|
||
AND k.table_name = 'fl_loading_pick_item'
|
||
AND k.column_name = 'pick_task_id'
|
||
AND k.referenced_table_name IS NOT NULL;
|
||
IF item_fk_count <> 1 OR item_target_count <> 1 THEN
|
||
IF item_drop_clauses IS NOT NULL THEN
|
||
SET @phase3_fk_ddl = CONCAT('ALTER TABLE fl_loading_pick_item ', item_drop_clauses);
|
||
PREPARE phase3_fk_statement FROM @phase3_fk_ddl;
|
||
EXECUTE phase3_fk_statement;
|
||
DEALLOCATE PREPARE phase3_fk_statement;
|
||
END IF;
|
||
ALTER TABLE fl_loading_pick_item
|
||
ADD CONSTRAINT fk_loading_pick_item_task
|
||
FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id);
|
||
END IF;
|
||
|
||
SELECT COUNT(*),
|
||
SUM(k.referenced_table_name = 'fl_loading_pick_task'
|
||
AND k.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = k.constraint_schema
|
||
AND peer.table_name = k.table_name
|
||
AND peer.constraint_name = k.constraint_name) = 1),
|
||
GROUP_CONCAT(CONCAT('DROP FOREIGN KEY `', REPLACE(k.constraint_name, '`', '``'), '`')
|
||
ORDER BY k.constraint_name SEPARATOR ', ')
|
||
INTO history_fk_count, history_target_count, history_drop_clauses
|
||
FROM information_schema.key_column_usage k
|
||
WHERE k.constraint_schema = 'baiyue_firstleg'
|
||
AND k.table_name = 'fl_loading_pick_history'
|
||
AND k.column_name = 'pick_task_id'
|
||
AND k.referenced_table_name IS NOT NULL;
|
||
IF history_fk_count <> 1 OR history_target_count <> 1 THEN
|
||
IF history_drop_clauses IS NOT NULL THEN
|
||
SET @phase3_fk_ddl = CONCAT('ALTER TABLE fl_loading_pick_history ', history_drop_clauses);
|
||
PREPARE phase3_fk_statement FROM @phase3_fk_ddl;
|
||
EXECUTE phase3_fk_statement;
|
||
DEALLOCATE PREPARE phase3_fk_statement;
|
||
END IF;
|
||
ALTER TABLE fl_loading_pick_history
|
||
ADD CONSTRAINT fk_loading_pick_history_task
|
||
FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id);
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL repair_phase3_loading_pick_task_foreign_keys();
|
||
DROP PROCEDURE repair_phase3_loading_pick_task_foreign_keys;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_booking_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_booking_before_update
|
||
BEFORE UPDATE ON fl_container_booking
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status
|
||
FROM fl_loading_plan WHERE container_booking_id = OLD.id;
|
||
IF current_plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜全部只读,不允许修改';
|
||
END IF;
|
||
IF current_plan_status = 'LOADING' AND (
|
||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||
OR NOT (OLD.warehouse_id <=> NEW.warehouse_id)
|
||
OR NOT (OLD.warehouse_name <=> NEW.warehouse_name)
|
||
OR NOT (OLD.bill_of_lading_no <=> NEW.bill_of_lading_no)
|
||
OR NOT (OLD.vessel_name <=> NEW.vessel_name)
|
||
OR NOT (OLD.container_type <=> NEW.container_type)
|
||
OR NOT (OLD.shipping_company_id <=> NEW.shipping_company_id)
|
||
OR NOT (OLD.shipping_company_name <=> NEW.shipping_company_name)
|
||
OR NOT (OLD.departure_port_code <=> NEW.departure_port_code)
|
||
OR NOT (OLD.departure_port_name <=> NEW.departure_port_name)
|
||
OR NOT (OLD.destination_port_code <=> NEW.destination_port_code)
|
||
OR NOT (OLD.destination_port_name <=> NEW.destination_port_name)
|
||
OR NOT (OLD.destination_warehouse_code <=> NEW.destination_warehouse_code)
|
||
OR NOT (OLD.destination_warehouse_name <=> NEW.destination_warehouse_name)
|
||
OR NOT (OLD.capacity_cbm <=> NEW.capacity_cbm)
|
||
OR NOT (OLD.max_weight_kg <=> NEW.max_weight_kg)
|
||
OR NOT (OLD.terminal_trucking_company <=> NEW.terminal_trucking_company)
|
||
OR NOT (OLD.loading_at <=> NEW.loading_at)
|
||
OR NOT (OLD.estimated_sailing_at <=> NEW.estimated_sailing_at)
|
||
OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||
OR NOT (OLD.estimated_port_arrival_at <=> NEW.estimated_port_arrival_at)
|
||
OR NOT (OLD.actual_port_arrival_at <=> NEW.actual_port_arrival_at)
|
||
OR NOT (OLD.estimated_warehouse_arrival_at <=> NEW.estimated_warehouse_arrival_at)
|
||
OR NOT (OLD.actual_warehouse_arrival_at <=> NEW.actual_warehouse_arrival_at)
|
||
OR NOT (OLD.created_by <=> NEW.created_by)
|
||
OR NOT (OLD.created_by_name <=> NEW.created_by_name)
|
||
OR NOT (OLD.created_at <=> NEW.created_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '装柜中仅允许修改集装箱号与航次';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_plan_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_plan_before_update
|
||
BEFORE UPDATE ON fl_loading_plan
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已完成装柜计划全部只读';
|
||
END IF;
|
||
IF NOT (
|
||
(OLD.plan_status = 'EDITING' AND NEW.plan_status IN ('EDITING', 'LOADING'))
|
||
OR (OLD.plan_status = 'LOADING'
|
||
AND NEW.plan_status IN ('LOADING', 'COMPLETED'))
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜状态只允许单向推进';
|
||
END IF;
|
||
IF OLD.plan_status = 'LOADING' AND (
|
||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.plan_no <=> NEW.plan_no)
|
||
OR NOT (OLD.container_booking_id <=> NEW.container_booking_id)
|
||
OR NOT (OLD.push_idempotency_key <=> NEW.push_idempotency_key)
|
||
OR NOT (OLD.preload_pushed_by <=> NEW.preload_pushed_by)
|
||
OR NOT (OLD.preload_pushed_by_name <=> NEW.preload_pushed_by_name)
|
||
OR NOT (OLD.preload_pushed_at <=> NEW.preload_pushed_at)
|
||
OR NOT (OLD.remark <=> NEW.remark)
|
||
OR NOT (OLD.created_by <=> NEW.created_by)
|
||
OR NOT (OLD.created_by_name <=> NEW.created_by_name)
|
||
OR NOT (OLD.created_at <=> NEW.created_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '装柜中计划仅允许乐观锁触碰或完成装柜';
|
||
END IF;
|
||
IF OLD.plan_status = NEW.plan_status
|
||
AND OLD.plan_status = 'LOADING' AND (
|
||
NOT (OLD.preload_completed_by <=> NEW.preload_completed_by)
|
||
OR NOT (OLD.preload_completed_by_name <=> NEW.preload_completed_by_name)
|
||
OR NOT (OLD.preload_completed_at <=> NEW.preload_completed_at)
|
||
OR NOT (OLD.confirmed_by <=> NEW.confirmed_by)
|
||
OR NOT (OLD.confirmed_by_name <=> NEW.confirmed_by_name)
|
||
OR NOT (OLD.confirmed_at <=> NEW.confirmed_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '同状态乐观锁触碰不得修改阶段审计字段';
|
||
END IF;
|
||
IF OLD.plan_status = 'EDITING' AND NEW.plan_status = 'LOADING' THEN
|
||
IF NEW.push_idempotency_key IS NULL OR CHAR_LENGTH(TRIM(NEW.push_idempotency_key)) = 0 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '开始装柜必须提供幂等键';
|
||
END IF;
|
||
SET NEW.preload_pushed_at = COALESCE(NEW.preload_pushed_at, CURRENT_TIMESTAMP(3));
|
||
ELSEIF OLD.plan_status = 'LOADING' AND NEW.plan_status = 'COMPLETED' THEN
|
||
IF EXISTS (SELECT 1 FROM fl_loading_plan_item item
|
||
LEFT JOIN fl_loading_pick_item pick_item
|
||
ON pick_item.loading_plan_item_id = item.id
|
||
LEFT JOIN fl_loading_pick_task pick_task
|
||
ON pick_task.id = pick_item.pick_task_id
|
||
AND pick_task.loading_plan_id = OLD.id
|
||
WHERE item.loading_plan_id = OLD.id AND item.deleted = 0
|
||
AND (pick_task.id IS NULL
|
||
OR pick_item.picked_package_count <> pick_item.planned_package_count)) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '仍有未完成拣货明细,不能完成装柜';
|
||
END IF;
|
||
SET NEW.confirmed_at = COALESCE(NEW.confirmed_at, CURRENT_TIMESTAMP(3));
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_plan_after_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_plan_after_update
|
||
AFTER UPDATE ON fl_loading_plan
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.plan_status = 'LOADING' AND NEW.plan_status = 'COMPLETED' THEN
|
||
UPDATE fl_loading_plan_item
|
||
SET frozen_at = COALESCE(frozen_at, NEW.confirmed_at, CURRENT_TIMESTAMP(3)),
|
||
frozen_by = COALESCE(frozen_by, NEW.updated_by),
|
||
updated_by = NEW.updated_by,
|
||
updated_by_name = NEW.updated_by_name,
|
||
version = version + 1
|
||
WHERE loading_plan_id = NEW.id AND deleted = 0;
|
||
UPDATE fl_loading_pick_task
|
||
SET task_status = 'COMPLETED', completed_by = NEW.updated_by,
|
||
completed_by_name = NEW.updated_by_name, completed_at = CURRENT_TIMESTAMP(3),
|
||
version = version + 1
|
||
WHERE loading_plan_id = NEW.id AND task_status = 'LOADING';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_item_before_insert;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_item_before_insert
|
||
BEFORE INSERT ON fl_loading_plan_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan WHERE id = NEW.loading_plan_id;
|
||
IF current_plan_status IS NULL OR current_plan_status NOT IN ('EDITING', 'LOADING') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '仅编辑中或装柜中的计划允许新增明细';
|
||
END IF;
|
||
IF NEW.deleted <> 0 OR NEW.frozen_at IS NOT NULL OR NEW.frozen_by IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '新增装柜明细必须为未删除且未冻结状态';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_item_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_item_before_update
|
||
BEFORE UPDATE ON fl_loading_plan_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status
|
||
FROM fl_loading_plan WHERE id = OLD.loading_plan_id;
|
||
IF current_plan_status = 'LOADING' AND EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item
|
||
WHERE loading_plan_item_id = OLD.id AND picked_package_count > 0) THEN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item
|
||
WHERE loading_plan_item_id = OLD.id
|
||
AND item_status = 'PARTIAL'
|
||
AND NEW.deleted = 0
|
||
AND NEW.package_count = picked_package_count
|
||
) OR NEW.deleted <> OLD.deleted
|
||
OR NOT (NEW.customer_name <=> OLD.customer_name)
|
||
OR NOT (NEW.inbound_id <=> OLD.inbound_id)
|
||
OR NOT (NEW.inbound_detail_id <=> OLD.inbound_detail_id)
|
||
OR NOT (NEW.order_id <=> OLD.order_id)
|
||
OR NOT (NEW.order_no <=> OLD.order_no)
|
||
OR NOT (NEW.inbound_code <=> OLD.inbound_code)
|
||
OR NOT (NEW.destination_warehouse_id <=> OLD.destination_warehouse_id)
|
||
OR NOT (NEW.destination_warehouse_code <=> OLD.destination_warehouse_code)
|
||
OR NOT (NEW.destination_warehouse_name <=> OLD.destination_warehouse_name)
|
||
OR NOT (NEW.sku_code <=> OLD.sku_code)
|
||
OR NOT (NEW.product_name_zh <=> OLD.product_name_zh)
|
||
OR NOT (NEW.product_name_en <=> OLD.product_name_en)
|
||
OR NOT (NEW.product_image_file_id <=> OLD.product_image_file_id)
|
||
OR NOT (NEW.product_image_url <=> OLD.product_image_url)
|
||
OR NOT (NEW.product_quantity <=> OLD.product_quantity)
|
||
OR NOT (NEW.units_per_carton <=> OLD.units_per_carton)
|
||
OR NOT (NEW.pallet_count <=> OLD.pallet_count)
|
||
OR NOT (NEW.gross_weight_kg <=> OLD.gross_weight_kg)
|
||
OR NOT (NEW.net_weight_kg <=> OLD.net_weight_kg)
|
||
OR NOT (NEW.dimensional_weight_kg <=> OLD.dimensional_weight_kg)
|
||
OR NOT (NEW.volume_cbm <=> OLD.volume_cbm)
|
||
OR NOT (NEW.fba_tracking_no <=> OLD.fba_tracking_no)
|
||
OR NOT (NEW.shipment_no <=> OLD.shipment_no)
|
||
OR NOT (NEW.shipping_mark <=> OLD.shipping_mark)
|
||
OR NOT (NEW.load_sequence <=> OLD.load_sequence)
|
||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||
OR NOT (NEW.internal_remark <=> OLD.internal_remark)
|
||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||
OR NOT (NEW.warehouse_remark <=> OLD.warehouse_remark)
|
||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||
OR NOT (NEW.removed_by_name <=> OLD.removed_by_name)
|
||
OR NOT (NEW.removed_at <=> OLD.removed_at) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已有PDA拣货记录的装柜明细仅允许收敛部分拣货实际箱数';
|
||
END IF;
|
||
END IF;
|
||
IF OLD.frozen_at IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后的明细永久冻结';
|
||
END IF;
|
||
IF OLD.deleted = 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已删除装柜明细不可恢复或修改';
|
||
END IF;
|
||
IF NEW.loading_plan_id <> OLD.loading_plan_id OR NEW.account_id <> OLD.account_id
|
||
OR NEW.inbound_detail_id <> OLD.inbound_detail_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜明细归属键不可修改,请删除后重新分配';
|
||
END IF;
|
||
IF NEW.deleted = 1 THEN
|
||
IF NEW.removed_by IS NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '删除装柜明细必须记录操作人';
|
||
END IF;
|
||
SET NEW.removed_at = COALESCE(NEW.removed_at, CURRENT_TIMESTAMP(3));
|
||
ELSEIF NEW.removed_by IS NOT NULL OR NEW.removed_by_name IS NOT NULL OR NEW.removed_at IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '删除审计字段只能在逻辑删除时写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_task_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_task_before_update
|
||
BEFORE UPDATE ON fl_loading_pick_task
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.task_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已完成PDA拣货任务不可修改';
|
||
END IF;
|
||
IF NEW.loading_plan_id <> OLD.loading_plan_id OR NOT (NEW.container_no <=> OLD.container_no) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务归属不可修改';
|
||
END IF;
|
||
IF NOT (OLD.task_status = 'LOADING' AND NEW.task_status IN ('LOADING', 'COMPLETED')) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务状态只允许完成';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_item_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_item_before_update
|
||
BEFORE UPDATE ON fl_loading_pick_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_task_status VARCHAR(16);
|
||
SELECT MAX(task_status) INTO current_task_status
|
||
FROM fl_loading_pick_task WHERE id = OLD.pick_task_id;
|
||
IF current_task_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后PDA拣货明细不可修改';
|
||
END IF;
|
||
IF NEW.pick_task_id <> OLD.pick_task_id
|
||
OR NEW.loading_plan_item_id <> OLD.loading_plan_item_id
|
||
OR NEW.inbound_id <> OLD.inbound_id
|
||
OR NEW.inbound_detail_id <> OLD.inbound_detail_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货明细归属不可修改';
|
||
END IF;
|
||
IF NEW.item_status = 'PARTIAL' AND NOT (
|
||
OLD.item_status IN ('PENDING', 'PICKING')
|
||
AND OLD.picked_package_count < NEW.picked_package_count
|
||
AND NEW.picked_package_count < OLD.planned_package_count
|
||
AND NEW.planned_package_count = OLD.planned_package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '部分拣货必须在计划箱数内增加实际拣出箱数';
|
||
END IF;
|
||
IF NEW.item_status = 'SKIPPED' AND NOT (
|
||
OLD.item_status = 'PENDING'
|
||
AND OLD.picked_package_count = 0
|
||
AND NEW.picked_package_count = 0
|
||
AND NEW.planned_package_count = OLD.planned_package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '跳拣仅允许零拣货的待拣明细';
|
||
END IF;
|
||
IF OLD.item_status = 'PARTIAL' AND NEW.item_status = 'PICKED' AND NOT (
|
||
NEW.picked_package_count = OLD.picked_package_count
|
||
AND NEW.planned_package_count = OLD.picked_package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '部分拣货收敛后计划箱数必须等于实际拣出箱数';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_item_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_item_before_delete
|
||
BEFORE DELETE ON fl_loading_pick_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_task_status VARCHAR(16);
|
||
SELECT MAX(task_status) INTO current_task_status
|
||
FROM fl_loading_pick_task WHERE id = OLD.pick_task_id;
|
||
IF current_task_status = 'COMPLETED' OR OLD.picked_package_count > 0 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已拣货或已完成的PDA明细不可删除';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_history_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_history_before_update
|
||
BEFORE UPDATE ON fl_loading_pick_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货历史不可修改';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_history_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_history_before_delete
|
||
BEFORE DELETE ON fl_loading_pick_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货历史不可删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_item_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_item_before_delete
|
||
BEFORE DELETE ON fl_loading_plan_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜明细只允许推送前逻辑删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_document_before_insert;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_document_before_insert
|
||
BEFORE INSERT ON fl_container_document
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan
|
||
WHERE container_booking_id = NEW.container_booking_id;
|
||
IF current_plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜文件不可新增';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_document_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_document_before_update
|
||
BEFORE UPDATE ON fl_container_document
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan
|
||
WHERE container_booking_id = OLD.container_booking_id;
|
||
IF current_plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜文件不可修改';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_document_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_document_before_delete
|
||
BEFORE DELETE ON fl_container_document
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan
|
||
WHERE container_booking_id = OLD.container_booking_id;
|
||
IF current_plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜文件不可删除';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_history_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_history_before_update
|
||
BEFORE UPDATE ON fl_loading_plan_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜操作历史不可修改';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_history_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_history_before_delete
|
||
BEFORE DELETE ON fl_loading_plan_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜操作历史不可删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
-- 三期智能装柜、预装清单和装柜清单平台权限;与一期平台根菜单幂等合并。
|
||
USE baiyue_iam;
|
||
DROP PROCEDURE IF EXISTS assert_smart_loading_permission_ids_available;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_smart_loading_permission_ids_available()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE id IN (5102091, 5102092, 5102093, 5102094, 5102096, 5102097, 5102098, 5102099)
|
||
AND permission_code NOT IN (
|
||
'platform:firstleg:container:view', 'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:assign',
|
||
'platform:firstleg:smart-loading:delete', 'platform:firstleg:smart-loading:push',
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '三期权限迁移失败:5102091-5102099 存在未知权限占用';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_smart_loading_permission_ids_available();
|
||
DROP PROCEDURE assert_smart_loading_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
|
||
(5102090, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:container:list',
|
||
'货柜', '/first-leg/containers', 'GET', 40, 'ACTIVE', 1, 0),
|
||
(5102091, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:view',
|
||
'查看货柜', NULL, 'GET', 10, 'ACTIVE', 1, 0),
|
||
(5102092, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:create',
|
||
'建立货柜', NULL, 'POST', 20, 'ACTIVE', 1, 0),
|
||
(5102093, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:edit',
|
||
'编辑货柜', NULL, 'PUT', 30, 'ACTIVE', 1, 0),
|
||
(5102094, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:smart-loading:list',
|
||
'智能装柜', '/first-leg/smart-loading', 'GET', 50, 'ACTIVE', 1, 0),
|
||
(5102096, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:create',
|
||
'创建智能装柜计划', NULL, 'POST', 20, 'ACTIVE', 1, 0),
|
||
(5102097, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:assign',
|
||
'分配装柜明细', NULL, 'POST', 30, 'ACTIVE', 1, 0),
|
||
(5102098, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:delete',
|
||
'删除装柜明细', NULL, 'DELETE', 40, 'ACTIVE', 1, 0),
|
||
(5102099, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:push',
|
||
'推送预装', NULL, 'POST', 50, 'ACTIVE', 1, 0),
|
||
(5102100, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:preload:list',
|
||
'预装清单', '/first-leg/preload-list', 'GET', 60, 'ACTIVE', 1, 0),
|
||
(5102102, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:view',
|
||
'查看预装详情', NULL, 'GET', 20, 'ACTIVE', 1, 0),
|
||
(5102103, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:edit',
|
||
'编辑预装运输字段', NULL, 'PUT', 30, 'ACTIVE', 1, 0),
|
||
(5102104, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:complete',
|
||
'完成预装', NULL, 'POST', 40, 'ACTIVE', 1, 0),
|
||
(5102105, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:confirm',
|
||
'确认预装', NULL, 'POST', 50, 'ACTIVE', 1, 0),
|
||
(5102106, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:loading-list:list',
|
||
'装柜清单', '/first-leg/loading-list', 'GET', 70, 'ACTIVE', 1, 0),
|
||
(5102108, 0, 'PLATFORM', 'BUTTON', 5102106, 'platform:firstleg:loading-list:view',
|
||
'查看装柜详情', NULL, 'GET', 20, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), 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, role.tenant_id
|
||
FROM iam_role role
|
||
JOIN iam_permission permission
|
||
ON permission.tenant_id = 0
|
||
AND permission.permission_code IN (
|
||
'platform:firstleg:container:list', 'platform:firstleg:container:view',
|
||
'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:assign',
|
||
'platform:firstleg:smart-loading:delete', 'platform:firstleg:smart-loading:push',
|
||
'platform:firstleg:preload:list', 'platform:firstleg:preload:view',
|
||
'platform:firstleg:preload:edit', 'platform:firstleg:preload:complete',
|
||
'platform:firstleg:preload:confirm', 'platform:firstleg:loading-list:list',
|
||
'platform:firstleg:loading-list:view')
|
||
WHERE role.tenant_id = 0
|
||
AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR');
|
||
|
||
USE baiyue_firstleg;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_smart_loading_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_smart_loading_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name IN ('fl_container_booking', 'fl_loading_plan', 'fl_loading_plan_item',
|
||
'fl_container_document', 'fl_loading_plan_history',
|
||
'fl_loading_pick_task', 'fl_loading_pick_item',
|
||
'fl_loading_pick_history')) <> 8 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:核心表未完整创建';
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM fl_loading_plan
|
||
WHERE plan_status NOT IN ('EDITING', 'LOADING', 'COMPLETED')) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:存在旧装柜状态';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.check_constraints
|
||
WHERE constraint_schema = 'baiyue_firstleg'
|
||
AND constraint_name = 'chk_loading_plan_status'
|
||
AND UPPER(check_clause) LIKE '%EDITING%'
|
||
AND UPPER(check_clause) LIKE '%LOADING%'
|
||
AND UPPER(check_clause) LIKE '%COMPLETED%') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:装柜状态约束不是最终三状态';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_container_booking'
|
||
AND column_name IN ('booking_no', 'cabinet_no', 'warehouse_id', 'bill_of_lading_no',
|
||
'vessel_name', 'voyage_no', 'container_type', 'capacity_cbm',
|
||
'max_weight_kg', 'terminal_trucking_company')) <> 10 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:货柜原型字段缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_plan_item'
|
||
AND column_name IN ('account_id', 'customer_name', 'active_inbound_detail_id', 'frozen_at',
|
||
'destination_warehouse_name', 'product_name_zh', 'product_name_en',
|
||
'product_image_file_id', 'pallet_count', 'net_weight_kg',
|
||
'dimensional_weight_kg', 'product_quantity', 'units_per_carton',
|
||
'fba_tracking_no', 'shipment_no', 'shipping_mark', 'version')) <> 17 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:明细归属、清单快照或冻结字段缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_plan_item'
|
||
AND index_name = 'uk_loading_item_plan_detail' AND non_unique = 0) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:计划内唯一活动明细锁缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_loading_pick_item'
|
||
AND column_name IN ('pick_task_id', 'loading_plan_item_id', 'inbound_id',
|
||
'inbound_detail_id', 'planned_package_count',
|
||
'picked_package_count', 'item_status', 'version')) <> 8 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:PDA拣货明细字段缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND non_unique = 0
|
||
AND index_name IN ('uk_loading_pick_task_plan',
|
||
'uk_loading_pick_item_plan_item',
|
||
'uk_loading_pick_history_operation')) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:PDA唯一索引缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_loading_pick_item'
|
||
AND column_name = 'pick_task_id'
|
||
AND referenced_table_name IS NOT NULL) <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.key_column_usage outer_fk
|
||
WHERE outer_fk.constraint_schema = 'baiyue_firstleg'
|
||
AND outer_fk.table_name = 'fl_loading_pick_item'
|
||
AND outer_fk.column_name = 'pick_task_id'
|
||
AND outer_fk.referenced_table_name = 'fl_loading_pick_task'
|
||
AND outer_fk.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = outer_fk.constraint_schema
|
||
AND peer.table_name = outer_fk.table_name
|
||
AND peer.constraint_name = outer_fk.constraint_name) = 1) <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:柜次PDA明细任务外键不唯一或指向错误';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_loading_pick_history'
|
||
AND column_name = 'pick_task_id'
|
||
AND referenced_table_name IS NOT NULL) <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.key_column_usage outer_fk
|
||
WHERE outer_fk.constraint_schema = 'baiyue_firstleg'
|
||
AND outer_fk.table_name = 'fl_loading_pick_history'
|
||
AND outer_fk.column_name = 'pick_task_id'
|
||
AND outer_fk.referenced_table_name = 'fl_loading_pick_task'
|
||
AND outer_fk.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = outer_fk.constraint_schema
|
||
AND peer.table_name = outer_fk.table_name
|
||
AND peer.constraint_name = outer_fk.constraint_name) = 1) <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:柜次PDA历史任务外键不唯一或指向错误';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.triggers
|
||
WHERE trigger_schema = 'baiyue_firstleg'
|
||
AND trigger_name IN ('trg_container_booking_before_update',
|
||
'trg_loading_plan_before_update', 'trg_loading_plan_after_update',
|
||
'trg_loading_item_before_insert', 'trg_loading_item_before_update',
|
||
'trg_loading_item_before_delete', 'trg_container_document_before_insert',
|
||
'trg_container_document_before_update', 'trg_container_document_before_delete',
|
||
'trg_loading_history_before_update', 'trg_loading_history_before_delete',
|
||
'trg_loading_pick_task_before_update',
|
||
'trg_loading_pick_item_before_update',
|
||
'trg_loading_pick_item_before_delete',
|
||
'trg_loading_pick_history_before_update',
|
||
'trg_loading_pick_history_before_delete')) <> 16 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:不可逆状态或冻结触发器缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT permission_code) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM' AND status = 'ACTIVE' AND deleted = 0
|
||
AND permission_code IN (
|
||
'platform:firstleg:container:list', 'platform:firstleg:container:view',
|
||
'platform:firstleg:container:create', 'platform:firstleg:container:edit',
|
||
'platform:firstleg:smart-loading:list', 'platform:firstleg:smart-loading:create',
|
||
'platform:firstleg:smart-loading:assign', 'platform:firstleg:smart-loading:delete',
|
||
'platform:firstleg:smart-loading:push', 'platform:firstleg:preload:list',
|
||
'platform:firstleg:preload:view', 'platform:firstleg:preload:edit',
|
||
'platform:firstleg:preload:complete', 'platform:firstleg:preload:confirm',
|
||
'platform:firstleg:loading-list:list', 'platform:firstleg:loading-list:view')) <> 16 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:平台权限资源未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT CONCAT(role.role_code, ':', permission.permission_code))
|
||
FROM baiyue_iam.iam_role role
|
||
JOIN baiyue_iam.iam_role_permission grant_row ON grant_row.role_id = role.id
|
||
JOIN baiyue_iam.iam_permission permission ON permission.id = grant_row.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 IN (
|
||
'platform:firstleg:container:list', 'platform:firstleg:container:view',
|
||
'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:assign',
|
||
'platform:firstleg:smart-loading:delete', 'platform:firstleg:smart-loading:push',
|
||
'platform:firstleg:preload:list', 'platform:firstleg:preload:view',
|
||
'platform:firstleg:preload:edit', 'platform:firstleg:preload:complete',
|
||
'platform:firstleg:preload:confirm', 'platform:firstleg:loading-list:list',
|
||
'platform:firstleg:loading-list:view')) <> 32 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:平台角色授权未完整写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
-- ============================================================
|
||
|
||
-- 2026-08-06 平台公共仓库正式主数据:固定作业仓 + FBA 地址库
|
||
-- 来源:warehouse/src/main/resources/db/warehouse-public-master-data.sql。必须位于最终 PASS 校验之前,可重复执行。
|
||
USE baiyue_warehouse;
|
||
|
||
-- 正式平台公共仓库主数据:固定作业仓 + FBA 地址库。
|
||
-- 来源:C:\Users\BY-OY\Downloads\FBA地址表.xls,只读清洗后生成。
|
||
-- 接受 252 条,拒绝 54 条;拒绝清单见 master-data/fba-public-addresses.rejected.csv。
|
||
-- 四个固定作业仓按通用占位地址写入;清单见 master-data/platform-fixed-warehouses.csv。
|
||
-- FBA 是目的地址主数据,不创建 RECEIVING、STORAGE、PICK_PACK、RETURN 等平台作业能力。
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_wh_fixed_platform_warehouse;
|
||
CREATE TEMPORARY TABLE tmp_wh_fixed_platform_warehouse (
|
||
warehouse_id_seed BIGINT UNSIGNED NOT NULL,
|
||
address_id_seed BIGINT UNSIGNED NOT NULL,
|
||
warehouse_code VARCHAR(50) NOT NULL,
|
||
warehouse_name VARCHAR(120) NOT NULL,
|
||
warehouse_type VARCHAR(32) NOT NULL,
|
||
country_code CHAR(2) NOT NULL,
|
||
timezone VARCHAR(64) NOT NULL,
|
||
state_province VARCHAR(100) NOT NULL,
|
||
city VARCHAR(100) NOT NULL,
|
||
postal_code VARCHAR(20) NOT NULL,
|
||
address_line1 VARCHAR(255) NOT NULL,
|
||
PRIMARY KEY (warehouse_code),
|
||
UNIQUE KEY uk_tmp_fixed_warehouse_id_seed (warehouse_id_seed),
|
||
UNIQUE KEY uk_tmp_fixed_address_id_seed (address_id_seed)
|
||
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||
|
||
INSERT INTO tmp_wh_fixed_platform_warehouse
|
||
(warehouse_id_seed,address_id_seed,warehouse_code,warehouse_name,warehouse_type,country_code,timezone,state_province,city,postal_code,address_line1)
|
||
VALUES
|
||
(193000000000200001,193000000000300001,'CN-YIWU-01','义乌仓','DOMESTIC','CN','UTC+8','浙江省','义乌市','322000','义乌仓通用地址待维护'),
|
||
(193000000000200002,193000000000300002,'CN-DONGGUAN-01','东莞仓','DOMESTIC','CN','UTC+8','广东省','东莞市','523000','东莞仓通用地址待维护'),
|
||
(193000000000200003,193000000000300003,'US-LAX-01','洛杉矶仓','OVERSEAS','US','UTC-8','CA','Los Angeles','90001','Los Angeles warehouse generic address pending maintenance'),
|
||
(193000000000200004,193000000000300004,'US-NYC-01','纽约仓','OVERSEAS','US','UTC-5','NY','New York','10001','New York warehouse generic address pending maintenance');
|
||
|
||
DROP PROCEDURE IF EXISTS assert_wh_fixed_platform_warehouse_seed;
|
||
DELIMITER //
|
||
CREATE PROCEDURE assert_wh_fixed_platform_warehouse_seed()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM tmp_wh_fixed_platform_warehouse) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓源数量不是4';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM tmp_wh_fixed_platform_warehouse
|
||
WHERE warehouse_code <> UPPER(TRIM(warehouse_code))
|
||
OR warehouse_type NOT IN ('DOMESTIC', 'OVERSEAS')
|
||
OR country_code NOT IN ('CN', 'US')
|
||
OR TRIM(timezone) = ''
|
||
OR TRIM(city) = ''
|
||
OR TRIM(state_province) = ''
|
||
OR TRIM(address_line1) = ''
|
||
OR TRIM(postal_code) = ''
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓主数据存在未清洗或必填缺失记录';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_warehouse existing
|
||
JOIN tmp_wh_fixed_platform_warehouse source
|
||
ON existing.id = source.warehouse_id_seed
|
||
AND (existing.tenant_id <> 0 OR existing.warehouse_code <> source.warehouse_code OR existing.deleted <> 0)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓仓库种子ID与既有数据冲突';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_warehouse_address existing
|
||
JOIN tmp_wh_fixed_platform_warehouse source
|
||
ON existing.id = source.address_id_seed
|
||
LEFT JOIN wh_warehouse parent
|
||
ON parent.id = existing.warehouse_id
|
||
AND parent.tenant_id = existing.tenant_id
|
||
WHERE existing.tenant_id <> 0
|
||
OR existing.deleted <> 0
|
||
OR parent.warehouse_code IS NULL
|
||
OR parent.warehouse_code <> source.warehouse_code
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓地址种子ID与既有数据冲突';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_warehouse existing
|
||
JOIN tmp_wh_fixed_platform_warehouse source
|
||
ON existing.tenant_id = 0
|
||
AND existing.warehouse_code = source.warehouse_code
|
||
AND existing.deleted = 1
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓存在同编码已删除仓库,需人工确认后恢复或清理';
|
||
END IF;
|
||
END//
|
||
DELIMITER ;
|
||
CALL assert_wh_fixed_platform_warehouse_seed();
|
||
DROP PROCEDURE IF EXISTS assert_wh_fixed_platform_warehouse_seed;
|
||
|
||
INSERT INTO wh_warehouse (
|
||
id, tenant_id, warehouse_code, warehouse_name, warehouse_type, country_code, timezone,
|
||
contact_name, contact_phone, contact_email, status, remark,
|
||
created_by, created_at, updated_by, updated_at, deleted, version
|
||
)
|
||
SELECT
|
||
source.warehouse_id_seed,
|
||
0,
|
||
source.warehouse_code,
|
||
source.warehouse_name,
|
||
source.warehouse_type,
|
||
source.country_code,
|
||
source.timezone,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
'ACTIVE',
|
||
NULL,
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
0,
|
||
0
|
||
FROM tmp_wh_fixed_platform_warehouse source
|
||
ON DUPLICATE KEY UPDATE
|
||
warehouse_name = VALUES(warehouse_name),
|
||
warehouse_type = VALUES(warehouse_type),
|
||
country_code = VALUES(country_code),
|
||
timezone = VALUES(timezone),
|
||
updated_at = CURRENT_TIMESTAMP(3);
|
||
|
||
INSERT INTO wh_warehouse_address (
|
||
id, tenant_id, warehouse_id, address_type, country_code, state_province, city,
|
||
district, postal_code, address_line1, address_line2, latitude, longitude,
|
||
created_by, created_at, updated_by, updated_at, deleted, version
|
||
)
|
||
SELECT
|
||
source.address_id_seed,
|
||
0,
|
||
warehouse.id,
|
||
'OPERATING',
|
||
source.country_code,
|
||
source.state_province,
|
||
source.city,
|
||
NULL,
|
||
source.postal_code,
|
||
source.address_line1,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
0,
|
||
0
|
||
FROM tmp_wh_fixed_platform_warehouse source
|
||
JOIN wh_warehouse warehouse
|
||
ON warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_code = source.warehouse_code
|
||
AND warehouse.deleted = 0
|
||
ON DUPLICATE KEY UPDATE
|
||
tenant_id = VALUES(tenant_id);
|
||
|
||
-- 国内固定作业仓必须启用收货能力,FBA 目的地址仍不得创建作业能力。
|
||
INSERT INTO wh_service_capability (
|
||
id, tenant_id, warehouse_id, capability_code, capability_name, enabled, cutoff_time,
|
||
description, created_by, created_at, updated_by, updated_at, deleted, version
|
||
)
|
||
VALUES
|
||
(193000000000400001,0,193000000000200001,'RECEIVING','头程收货',1,'16:00:00',
|
||
'义乌仓头程订单收货能力',NULL,CURRENT_TIMESTAMP(3),NULL,CURRENT_TIMESTAMP(3),0,0),
|
||
(193000000000400002,0,193000000000200002,'RECEIVING','头程收货',1,'16:00:00',
|
||
'东莞仓头程订单收货能力',NULL,CURRENT_TIMESTAMP(3),NULL,CURRENT_TIMESTAMP(3),0,0)
|
||
ON DUPLICATE KEY UPDATE
|
||
capability_name = VALUES(capability_name),
|
||
enabled = VALUES(enabled),
|
||
cutoff_time = VALUES(cutoff_time),
|
||
description = VALUES(description),
|
||
deleted = 0,
|
||
updated_at = CURRENT_TIMESTAMP(3);
|
||
|
||
-- 平台托盘初始化主数据;内部状态仅供预占/占用,不作为平台维护字段。
|
||
INSERT INTO wh_pallet (
|
||
id, tenant_id, warehouse_id, pallet_code, pallet_type, capacity_cbm,
|
||
weight_kg, color, status, remark, created_by, updated_by
|
||
)
|
||
VALUES
|
||
(193000000000200101,0,193000000000200001,'TP-001','木托盘',1.200000,8.000,'黄色','AVAILABLE',NULL,0,0),
|
||
(193000000000200102,0,193000000000200001,'TP-002','胶托盘',2.400000,12.000,'黑色','AVAILABLE',NULL,0,0)
|
||
ON DUPLICATE KEY UPDATE
|
||
tenant_id = 0,
|
||
warehouse_id = VALUES(warehouse_id),
|
||
pallet_type = VALUES(pallet_type),
|
||
capacity_cbm = VALUES(capacity_cbm),
|
||
weight_kg = VALUES(weight_kg),
|
||
color = VALUES(color),
|
||
remark = VALUES(remark);
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_wh_fba_public_master_data;
|
||
CREATE TEMPORARY TABLE tmp_wh_fba_public_master_data (
|
||
warehouse_id_seed BIGINT UNSIGNED NOT NULL,
|
||
address_id_seed BIGINT UNSIGNED NOT NULL,
|
||
warehouse_code VARCHAR(50) NOT NULL,
|
||
warehouse_name VARCHAR(120) NOT NULL,
|
||
state_province VARCHAR(100) NOT NULL,
|
||
city VARCHAR(100) NOT NULL,
|
||
postal_code VARCHAR(20) NOT NULL,
|
||
timezone VARCHAR(64) NOT NULL,
|
||
address_line1 VARCHAR(255) NOT NULL,
|
||
source_row INT NOT NULL,
|
||
PRIMARY KEY (warehouse_code),
|
||
UNIQUE KEY uk_tmp_fba_warehouse_id_seed (warehouse_id_seed),
|
||
UNIQUE KEY uk_tmp_fba_address_id_seed (address_id_seed)
|
||
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||
|
||
INSERT INTO tmp_wh_fba_public_master_data
|
||
(warehouse_id_seed,address_id_seed,warehouse_code,warehouse_name,state_province,city,postal_code,timezone,address_line1,source_row)
|
||
VALUES
|
||
(193000000000000001,193000000000100001,'ABE2','ABE2','PA','Breinigsville','18031','UTC-5','705 Boulder Drive',238),
|
||
(193000000000000002,193000000000100002,'ABE3','ABE3','PA','Breinigsville','18031','UTC-5','650 Boulder Drive',237),
|
||
(193000000000000003,193000000000100003,'ABE4','ABE4','PA','Easton','18045','UTC-5','1610 Van Buren Road',134),
|
||
(193000000000000004,193000000000100004,'ABE5','ABE5','PA','Harrisburg','17112','UTC-5','6455 Allentown Boulevard',231),
|
||
(193000000000000005,193000000000100005,'ABE8','ABE8','NJ','Florence','08518','UTC-5','401 Independence Road 08518-2200',125),
|
||
(193000000000000006,193000000000100006,'ABQ2','ABQ2','NM','LOS LUNAS','87031','UTC-7','6251 Pioneer Trail NW',99),
|
||
(193000000000000007,193000000000100007,'ACY2','ACY2','NJ','BURLINGTON','08016','UTC-5','1101 E PEARL ST',84),
|
||
(193000000000000008,193000000000100008,'AFW1','AFW1','TX','FORT WORTH','76131','UTC-6','1851 NE LOOP 820',141),
|
||
(193000000000000009,193000000000100009,'AFW5','AFW5','TX','BALCH SPRINGS','75180-1636','UTC-6','12374 LAKE JUNE RD',27),
|
||
(193000000000000010,193000000000100010,'AKR1','AKR1','OH','Canton','44705','UTC-5','4747 Rebar Ave NE',148),
|
||
(193000000000000011,193000000000100011,'ALB1','ALB1','NY','Castleton','12033-9634','UTC-5','1835 US Route 9',174),
|
||
(193000000000000012,193000000000100012,'AMA1','AMA1','TX','Amarillo','79108','UTC-6','8590 NE 24th Avenue',129),
|
||
(193000000000000013,193000000000100013,'ATL2N','ATL2N','GA','Carrollton','30117','UTC-5','3101 N. Highway 27',31),
|
||
(193000000000000014,193000000000100014,'ATL3','ATL3','GA','Pendergrass','30567','UTC-5','445 Valentine Industrial Parkway',147),
|
||
(193000000000000015,193000000000100015,'ATL6','ATL6','GA','East Point','30344','UTC-5','4200 North Commerce',202),
|
||
(193000000000000016,193000000000100016,'AVP1','AVP1','PA','Hazle Township','18202-9361','UTC-5','550 Oak Ridge Road',233),
|
||
(193000000000000017,193000000000100017,'AVP3','AVP3','PA','GOULDSBORO','18424-9492','UTC-5','298 1ST AVE',232),
|
||
(193000000000000018,193000000000100018,'BDL1','BDL1','CT','Windsor','06095','UTC-5','801 Day Hill Rd.',289),
|
||
(193000000000000019,193000000000100019,'BFI1','BFI1','WA','Sumner','98390','UTC-8','1800 140th Avenue E.',208),
|
||
(193000000000000020,193000000000100020,'BFI3','BFI3','WA','Dupont','98327-9607','UTC-8','2700 Center Drive',207),
|
||
(193000000000000021,193000000000100021,'BFI7','BFI7','WA','Sumner','98390','UTC-8','1901 140th Ave E',206),
|
||
(193000000000000022,193000000000100022,'BJC1','BJC1','CO','AURORA','80019','UTC-7','6300 N Powhaton Road',11),
|
||
(193000000000000023,193000000000100023,'BNA1','BNA1','TN','Lebanon','37090','UTC-6','14840 Central Pike Suite 190',221),
|
||
(193000000000000024,193000000000100024,'BNA2','BNA2','TN','Lebanon','37090','UTC-6','500 Duke DR',131),
|
||
(193000000000000025,193000000000100025,'BNA3','BNA3','TN','Murfreesboro','37127','UTC-6','2020 Joe B Jackson Pkwy',140),
|
||
(193000000000000026,193000000000100026,'BNA4','BNA4','TN','Nashville','37217','UTC-6','50 airways Blvd',220),
|
||
(193000000000000027,193000000000100027,'BNA6','BNA6','TN','CLARKSVILLE','37040-5502','UTC-6','3875 GUTHRIE HWY',110),
|
||
(193000000000000028,193000000000100028,'BOS1','BOS1','NH','Nashua','03063','UTC-5','10 State St.',243),
|
||
(193000000000000029,193000000000100029,'BOS2','BOS2','MA','Drive Stoughton','02072','UTC-5','1000 Tech Centre',193),
|
||
(193000000000000030,193000000000100030,'BOS7','BOS7','MA','Fall River','02720','UTC-5','1180 Innovation Way',192),
|
||
(193000000000000031,193000000000100031,'BWI1','BWI1','VA','Sterling','20166-2058','UTC-5','45121 Global Plaza',242),
|
||
(193000000000000032,193000000000100032,'BWI2','BWI2','MD','Baltimore','21224','UTC-5','2010 Broening Hwy',241),
|
||
(193000000000000033,193000000000100033,'BWI4','BWI4','VA','CLEARBROOK','22624','UTC-5','165 Business Blvd',102),
|
||
(193000000000000034,193000000000100034,'CAE1','CAE1','SC','West Columbia','29172','UTC-5','4400 12TH STREET EXT',225),
|
||
(193000000000000035,193000000000100035,'CHA1','CHA1','TN','Chattanooga','37416','UTC-5','7200 Discovery Drive',223),
|
||
(193000000000000036,193000000000100036,'CHA2','CHA2','TN','Charleston','37310','UTC-5','225 Infinity Dr NW',222),
|
||
(193000000000000037,193000000000100037,'CHO1','CHO1','VA','FISHERSVILLE','22939','UTC-5','32 TRADER RD',75),
|
||
(193000000000000038,193000000000100038,'CLT2','CLT2','NC','Charlotte','28214-8082','UTC-5','10240 Old Dowd Rd',191),
|
||
(193000000000000039,193000000000100039,'CLT3','CLT3','NC','CONCORD','28027','UTC-5','6500 Davidson Hwy',114),
|
||
(193000000000000040,193000000000100040,'CMH2','CMH2','OH','Groveport','43125-9016','UTC-5','6050 Gateway Court',240),
|
||
(193000000000000041,193000000000100041,'CMH3','CMH3','OH','Monroe','45050-1848','UTC-5','700 GATEWAY BLVD',181),
|
||
(193000000000000042,193000000000100042,'CVG1','CVG1','KY','Hebron','41048','UTC-5','1155 Worldwide Blvd',262),
|
||
(193000000000000043,193000000000100043,'CVG2','CVG2','KY','Hebron','41048','UTC-5','1600 Worldwide Blvd',261),
|
||
(193000000000000044,193000000000100044,'CVG3','CVG3','KY','Hebron','41048','UTC-5','3680 Langley Dr.',260),
|
||
(193000000000000045,193000000000100045,'CVG5','CVG5','KY','Hebron','41048','UTC-5','2285 Litton Lane',259),
|
||
(193000000000000046,193000000000100046,'DCA6','DCA6','MD','BALTIMORE','21219','UTC-5','6001 Bethlehem Blvd',135),
|
||
(193000000000000047,193000000000100047,'DEN2','DEN2','CO','Aurora','80019','UTC-7','22205 East 19th Ave',123),
|
||
(193000000000000048,193000000000100048,'DEN8','DEN8','CO','Aurora','80018','UTC-7','21000 E 13th Ave',120),
|
||
(193000000000000049,193000000000100049,'DET1','DET1','MI','Livonia','48150-1543','UTC-5','39000 Amrhein Road',189),
|
||
(193000000000000050,193000000000100050,'DET2','DET2','MI','Shelby Township','48317-1318','UTC-5','50500 Mound Rd',182),
|
||
(193000000000000051,193000000000100051,'DFW5S','DFW5S','TX','Lancaster','75146','UTC-6','2500 E Belt Line Rd,',143),
|
||
(193000000000000052,193000000000100052,'DFW6','DFW6','TX','Coppell','75019','UTC-6','940 W Bethel Road',219),
|
||
(193000000000000053,193000000000100053,'DFW8','DFW8','TX','DFW Airport','75261','UTC-6','2700 Regent Blvd',217),
|
||
(193000000000000054,193000000000100054,'EWR4','EWR4','NJ','Robbinsville','08691','UTC-5','50 New Canton Way',256),
|
||
(193000000000000055,193000000000100055,'EWR5','EWR5','NJ','Avenel','07001','UTC-5','301 Blair Rd.',255),
|
||
(193000000000000056,193000000000100056,'EWR7','EWR7','NJ','Avenel','07001','UTC-5','301 Blair Rd.',254),
|
||
(193000000000000057,193000000000100057,'EWR9','EWR9','NJ','Carteret','07008','UTC-5','8003 Industrial Parkway',253),
|
||
(193000000000000058,193000000000100058,'FAT2','FAT2','CA','VISALIA','93291-1882','UTC-8','3315 N KELSEYST',117),
|
||
(193000000000000059,193000000000100059,'FOE1','FOE1','KS','KANSAS CITY','66109-3665','UTC-6','9400 LEAVENWORTH RD',176),
|
||
(193000000000000060,193000000000100060,'FTW1','FTW1','TX','Dallas','75241','UTC-6','33333 Lyndon B Johnson FREEWAY',212),
|
||
(193000000000000061,193000000000100061,'FTW2','FTW2','TX','Coppell','75019','UTC-6','2701 West Bethel Road',211),
|
||
(193000000000000062,193000000000100062,'FTW3','FTW3','TX','Fort Worth','76177-2517','UTC-6','15201 Heritage Pkwy',89),
|
||
(193000000000000063,193000000000100063,'FTW5','FTW5','TX','Forney','75126','UTC-6','1475 AKRON WAY',121),
|
||
(193000000000000064,193000000000100064,'FWA4','FWA4','IN','FORT WAYNE','46809-9771','UTC-5','9798 Smith Road',171),
|
||
(193000000000000065,193000000000100065,'GEG2','GEG2','WA','SPOKANE','99216','UTC-8','18007 E Garland Ave',151),
|
||
(193000000000000066,193000000000100066,'GEU2','GEU2','AZ','Glendale','85307','UTC-7','15301 West Northern Avenue',67),
|
||
(193000000000000067,193000000000100067,'GEU3','GEU3','AZ','BUCKEYE','85396','UTC-7','18900 W McDowell Road',78),
|
||
(193000000000000068,193000000000100068,'GEU5','GEU5','AZ','GOODYEAR','85395','UTC-7','4660 North Cotton Lane',68),
|
||
(193000000000000069,193000000000100069,'GSO1','GSO1','NC','KERNERSVILLE','27284','UTC-5','1656 OLD GREENSBORO RD',164),
|
||
(193000000000000070,193000000000100070,'GSP1','GSP1','SC','Spartanburg','29303','UTC-5','402 John Dodd Rd',224),
|
||
(193000000000000071,193000000000100071,'GYR2','GYR2','AZ','GOODYEAR','85395-7672','UTC-7','17341 W MINNEZONA AVE',188),
|
||
(193000000000000072,193000000000100072,'GYR3','GYR3','AZ','PHOENIX','85043-2356','UTC-7','8181 W ROOSEVELT ST',187),
|
||
(193000000000000073,193000000000100073,'HGR6','HGR6','MD','HAGERSTOWN','21740-7301','UTC-5','55 West Oak Ridge Drive',80),
|
||
(193000000000000074,193000000000100074,'HIA1','HIA1','PA','MIDDLETOWN','17057','UTC-5','3327 E Harrisburg Pik',87),
|
||
(193000000000000075,193000000000100075,'HLI2','HLI2','CA','HOLLISTER','95023','UTC-8','600 Ernie Drive',44),
|
||
(193000000000000076,193000000000100076,'HOU1','HOU1','TX','Humble','77338','UTC-6','8120 Humble Westfield Rd',216),
|
||
(193000000000000077,193000000000100077,'HOU2','HOU2','TX','Houston','77038-2324','UTC-6','10550 Ella Blvd.',215),
|
||
(193000000000000078,193000000000100078,'HOU3','HOU3','TX','Brookshire','77423','UTC-6','31555 Highway 90 E',214),
|
||
(193000000000000079,193000000000100079,'HOU8','HOU8','TX','FRESNO','77545','UTC-6','2303 Hurricane Lane',154),
|
||
(193000000000000080,193000000000100080,'HSV1','HSV1','AL','Madison','35756','UTC-6','7817 Greenbrier Road',142),
|
||
(193000000000000081,193000000000100081,'HSV2','HSV2','AL','MADISON','35756','UTC-6','28869 Fanning Dr NW',126),
|
||
(193000000000000082,193000000000100082,'IAH3','IAH3','TX','Houston','77032','UTC-6','15525 Milner Road',155),
|
||
(193000000000000083,193000000000100083,'IAZ1','IAZ1','AZ','GOODYEAR','85395-4515','UTC-7','4660 N COTTON LN 85395-4515 - GOODYEAR, AZ - United States',3),
|
||
(193000000000000084,193000000000100084,'ICT2','ICT2','KS','PARK CITY','67219','UTC-6','7130 N BROADWAY AVE',136),
|
||
(193000000000000085,193000000000100085,'IGA3','IGA3','GA','MACON','31216-6427','UTC-5','7001 Skipper Rd 31216-6427 MACON, GA - United States',2),
|
||
(193000000000000086,193000000000100086,'IGQ2','IGQ2','IL','University park','60484','UTC-6','23257 S. Central Avenue',152),
|
||
(193000000000000087,193000000000100087,'ILG1','ILG1','DE','NEW CASTLE','19720','UTC-5','780 S. DuPont Highway',119),
|
||
(193000000000000088,193000000000100088,'IMO1','IMO1','MO','LIBERTY','64068-8933','UTC-6','2361 S Withers RD',10),
|
||
(193000000000000089,193000000000100089,'IMS1','IMS1','MS','OLIVE BRANCH','38654','UTC-6','IMS1-11505 Progress Way,38654-2510,OLIVE BRANCH,MS,US',6),
|
||
(193000000000000090,193000000000100090,'INC1','INC1','NC','FAYETTEVILLE','28303-2215','UTC-5','6309 Bragg Blvd 28303-2215 - FAYETTEVILLE, NC - United States',5),
|
||
(193000000000000091,193000000000100091,'IND2','IND2','IN','Plainfield','46168-7442','UTC-5','715 Airtech Pkwy',88),
|
||
(193000000000000092,193000000000100092,'IND4','IND4','IN','Indianapolis','46231','UTC-5','710 S. Girls School Rd',276),
|
||
(193000000000000093,193000000000100093,'IND5','IND5','IN','Plainfield','46168','UTC-5','800 Perry Road',275),
|
||
(193000000000000094,193000000000100094,'IND6','IND6','IN','Jeffersonville','47130','UTC-5','1453 E 10th St',274),
|
||
(193000000000000095,193000000000100095,'IND9','IND9','IN','GREENWOOD','46143-7830','UTC-5','1151 S GRAHAM RD',173),
|
||
(193000000000000096,193000000000100096,'ITX3','ITX3','TX','AMARILLO','79108-7749','UTC-6','ITX3-8590 NE 24th Ave,79108-7749,AMARILLO,TX,US',9),
|
||
(193000000000000097,193000000000100097,'IUSF','IUSF','TX','DALLAS','75241','UTC-6','3351 Balmorhea Dr',22),
|
||
(193000000000000098,193000000000100098,'IUSJ','IUSJ','CA','BEAUMONT','92223','UTC-8','36900 West 4th Street',69),
|
||
(193000000000000099,193000000000100099,'IUSP','IUSP','CA','HESPERIA,','92344','UTC-8','8140 Caliente Rd',74),
|
||
(193000000000000100,193000000000100100,'IUSQ','IUSQ','CA','REDLANDS','92374','UTC-8','2125 W San Bernardino Ave',70),
|
||
(193000000000000101,193000000000100101,'IUSR','IUSR','SC','COWPENS','29330','UTC-5','1120 Mt Olive Rd',40),
|
||
(193000000000000102,193000000000100102,'IUST','IUST','PA','GREENCASTLE','17225','UTC-5','3PL AWD (Greencastle, PA, US) 15651 Greenmount RdGREENCASTLE, PA 17225 United States',39),
|
||
(193000000000000103,193000000000100103,'IUSW','IUSW','CA','Hesperia','92344','UTC-8','8130 Caliente Rd 92344 - Hesperia, CA - United States',33),
|
||
(193000000000000104,193000000000100104,'IUTE','IUTE','AZ','GOODYEAR','85338','UTC-7','16155 W Elwood St',21),
|
||
(193000000000000105,193000000000100105,'IUTJ','IUTJ','CA','Fontana','92335','UTC-8','9253 Dreamland Drive 92335 - Fontana, CA - United',8),
|
||
(193000000000000106,193000000000100106,'IUTL','IUTL','CA','Moreno Valley','92551','UTC-8','24405 Krameria Ave 92551 - Moreno Valley, CA - United States',7),
|
||
(193000000000000107,193000000000100107,'IWA6','IWA6','WA','PASCO','99301-6161','UTC-8','1202 S Road 40 E 99301-6161 - PASCO, WA - United States',4),
|
||
(193000000000000108,193000000000100108,'JOT1','JOT1','CA','SAN BERNARDINO','92408','UTC-8','1494 S WATERMAN AVE',72),
|
||
(193000000000000109,193000000000100109,'JVL1','JVL1','WI','BELOIT','53511','UTC-6','1255 Gateway Blvd',150),
|
||
(193000000000000110,193000000000100110,'KRB4','KRB4','CA','PERRIS','92571-9722','UTC-8','4501 PATTERSON AVE',178),
|
||
(193000000000000111,193000000000100111,'KY1','KY1','KY','Shepherdsville','40165','UTC-5','120 Velocity Way.,',161),
|
||
(193000000000000112,193000000000100112,'LAN2','LAN2','MI','LANSING','48917','UTC-5','6500 W Mt Hope Hwy',100),
|
||
(193000000000000113,193000000000100113,'LAS1','LAS1','NV','HENDERSON','89044','UTC-8','12300 Bermuda Road',247),
|
||
(193000000000000114,193000000000100114,'LAS2','LAS2','NV','Las Vegas','89030','UTC-8','3837 Bay Lake Trail, Suite 111 North',246),
|
||
(193000000000000115,193000000000100115,'LAS6','LAS6','NV','Las Vegas','89115','UTC-8','4550 Nexus Way',132),
|
||
(193000000000000116,193000000000100116,'LAX2T','LAX2T','CA','CHINO','91708','UTC-8','6730 Kimball Ave',162),
|
||
(193000000000000117,193000000000100117,'LAX9','LAX9','CA','FONTANA','92337','UTC-8','11263 Oleander Ave',183),
|
||
(193000000000000118,193000000000100118,'LBE1','LBE1','PA','Greensburg','15605','UTC-5','165 Glen Fox Road',86),
|
||
(193000000000000119,193000000000100119,'LEX1','LEX1','KY','Lexington','40511','UTC-5','1850 Mercer RD',268),
|
||
(193000000000000120,193000000000100120,'LEX2','LEX2','KY','Lexington','40511','UTC-5','172 Trade St.',267),
|
||
(193000000000000121,193000000000100121,'LFT1','LFT1','LA','CARENCRO','70520-5976','UTC-6','3550 NE EVANGELINE TRWY',107),
|
||
(193000000000000122,193000000000100122,'LGB3','LGB3','CA','Eastvale','91752','UTC-8','4950 Goodman Way',291),
|
||
(193000000000000123,193000000000100123,'LGB4','LGB4','CA','Redlands','92374','UTC-8','27517 Pioneer Avenue',292),
|
||
(193000000000000124,193000000000100124,'LGB6','LGB6','CA','Riverside','92518-1513','UTC-8','20901 Krameria Ave',293),
|
||
(193000000000000125,193000000000100125,'LGB8','LGB8','CA','Rialto','92376','UTC-8','1568 N Linden Ave',294),
|
||
(193000000000000126,193000000000100126,'LIT2','LIT2','AR','NORTH LITTLE ROCK','72117','UTC-6','13001 HIGHWAY 70',106),
|
||
(193000000000000127,193000000000100127,'MCC1','MCC1','CA','RANCHO CORDOVA','95742','UTC-8','3300 Rancho Cordova Pkwy 95742 - RANCHO CORDOVA, CA - United States',32),
|
||
(193000000000000128,193000000000100128,'MCE1','MCE1','CA','TURLOCK','95380','UTC-8','FULKERTH RD',157),
|
||
(193000000000000129,193000000000100129,'MCI1','MCI1','KS','Lenexa','66219','UTC-6','113th St & Renner Blvd.',271),
|
||
(193000000000000130,193000000000100130,'MCI4','MCI4','MO','KANSAS CITY','64163','UTC-6','MCI4 - 10501 NW 136th Street',12),
|
||
(193000000000000131,193000000000100131,'MCO2','MCO2','FL','Deltona','32725-2610','UTC-5','2600 N Normandy Blvd',115),
|
||
(193000000000000132,193000000000100132,'MDT1','MDT1','PA','Carlisle','17015','UTC-5','2Ames Drive',230),
|
||
(193000000000000133,193000000000100133,'MDT4','MDT4','PA','York','17408','UTC-5','5125 Commerce Drive',128),
|
||
(193000000000000134,193000000000100134,'MDW2','MDW2','IL','Joliet','60433-3280','UTC-6','250 Emerald Drive',200),
|
||
(193000000000000135,193000000000100135,'MDW6','MDW6','IL','ROMEOVILLE','60446-6529','UTC-6','1125 W REMINGTON BLVD ROMEOVILLE',199),
|
||
(193000000000000136,193000000000100136,'MDW7','MDW7','IL','Monee','60449','UTC-6','6605 Monee Manhattan Road',198),
|
||
(193000000000000137,193000000000100137,'MDW8','MDW8','IL','Waukegan','60085','UTC-6','1750 Bridge Drive',197),
|
||
(193000000000000138,193000000000100138,'MDW9','MDW9','IL','Aurora','60502','UTC-6','2865 Duke Parkway',196),
|
||
(193000000000000139,193000000000100139,'MEM1','MEM1','TN','Memphis','38118','UTC-6','3292 E Holmes Rd',163),
|
||
(193000000000000140,193000000000100140,'MEM6','MEM6','MS','OLIVE BRANCH','38654','UTC-6','11505 Progress Way',118),
|
||
(193000000000000141,193000000000100141,'MGE3','MGE3','GA','Jefferson','30549-2010','UTC-5','808 Hog Mountain Road',201),
|
||
(193000000000000142,193000000000100142,'MIT2','MIT2','CA','SHAFTER','93263','UTC-8','5408 Express Avenue',77),
|
||
(193000000000000143,193000000000100143,'MKC4','MKC4','KS','Edgerton','66021-9588','UTC-6','19645 Waverly Rd',270),
|
||
(193000000000000144,193000000000100144,'MKC6','MKC6','KS','Kansas City','66102','UTC-6','6925 Riverview Avenue',269),
|
||
(193000000000000145,193000000000100145,'MKE1','MKE1','WI','Kenosha','53144','UTC-6','3501 120th Ave',203),
|
||
(193000000000000146,193000000000100146,'MQJ1','MQJ1','IN','GREENFIELD','46140','UTC-5','4412 W 300 RD N',94),
|
||
(193000000000000147,193000000000100147,'MSP1','MSP1','MN','Shakopee','55379','UTC-6','2601 4th Avenue East',190),
|
||
(193000000000000148,193000000000100148,'NJ3','NJ3','NJ','Pedricktown','08067','UTC-5','3 Sorbello Way',17),
|
||
(193000000000000149,193000000000100149,'OAK3','OAK3','CA','Patterson','95363-8876','UTC-8','255 Park Center Drive',299),
|
||
(193000000000000150,193000000000100150,'OAK4','OAK4','CA','Tracy','95304-9370','UTC-8','1555 N. Chrisman Rd',300),
|
||
(193000000000000151,193000000000100151,'OKC2','OKC2','OK','OKLAHOMA CITY','73159','UTC-6','8991 S PORTLAND AVENUE',133),
|
||
(193000000000000152,193000000000100152,'OLM1','OLM1','WA','OLYMPIA','98516','UTC-8','3300 HOGUM BAY RD NE 98516-3111 - OLYMPIA, WA - United States',19),
|
||
(193000000000000153,193000000000100153,'ONT2','ONT2','CA','San Bernardino','92408','UTC-8','1910 E Central Ave',303),
|
||
(193000000000000154,193000000000100154,'ONT6','ONT6','CA','Moreno Valley','92551-9561','UTC-8','24208 San Michele Rd',302),
|
||
(193000000000000155,193000000000100155,'ONT8','ONT8','CA','Moreno Valley','92551-9534','UTC-8','24300 Nandina Ave',301),
|
||
(193000000000000156,193000000000100156,'ONT9','ONT9','CA','Redlands','92374','UTC-8','2125 W. San Bernandino Ave',298),
|
||
(193000000000000157,193000000000100157,'ORD2','ORD2','IL','CHANNAHON','60410-8758','UTC-6','23714 W Amoco Rd',103),
|
||
(193000000000000158,193000000000100158,'ORD6','ORD6','IL','Wood Dale','60191','UTC-6','1250 N Mittel Blvd',195),
|
||
(193000000000000159,193000000000100159,'ORF2','ORF2','VA','CHESAPEAKE','23321-1425','UTC-5','ORF2 - 5045 PORTSMOUTH BLVD',113),
|
||
(193000000000000160,193000000000100160,'PBI3','PBI3','FL','Jacksonville','32206','UTC-5','21 Jump Street',81),
|
||
(193000000000000161,193000000000100161,'PDX7','PDX7','OR','Salem','97317','UTC-8','4775 Depot Ct SE',127),
|
||
(193000000000000162,193000000000100162,'PHL1','PHL1','DE','New Castle','19720','UTC-5','1 Centerpoint Blvd.',288),
|
||
(193000000000000163,193000000000100163,'PHL3','PHL3','DE','New Castle','19720','UTC-5','1600 Johnson Way',287),
|
||
(193000000000000164,193000000000100164,'PHL4','PHL4','PA','Carlisle','17015','UTC-5','21 Roadway Dr',236),
|
||
(193000000000000165,193000000000100165,'PHL5','PHL5','PA','Lewisberry','17339','UTC-5','500 McCarthy Dr',235),
|
||
(193000000000000166,193000000000100166,'PHL5S','PHL5S','PA','Greencastle','17225','UTC-5','1915 Ebberts Spg Ct, Greencastle, PA 17225',20),
|
||
(193000000000000167,193000000000100167,'PHL6','PHL6','PA','Carlisle','17015','UTC-5','675 Allen Rd.',234),
|
||
(193000000000000168,193000000000100168,'PHL7','PHL7','DE','Middletown','19709','UTC-5','560 Merrimac Ave',286),
|
||
(193000000000000169,193000000000100169,'PHL8','PHL8','DE','Middletown','19709','UTC-5','727 N Broad St',285),
|
||
(193000000000000170,193000000000100170,'PHX3','PHX3','AZ','Phoenix','85043','UTC-7','6835 West Buckeye Road',307),
|
||
(193000000000000171,193000000000100171,'PHX5','PHX5','AZ','Goodyear','85338','UTC-7','16920 W Commerce Drive',306),
|
||
(193000000000000172,193000000000100172,'PHX6','PHX6','AZ','Phoenix','85043','UTC-7','4750 West Mohave St',305),
|
||
(193000000000000173,193000000000100173,'PHX7','PHX7','AZ','Phoenix','85043','UTC-7','800 N 75th Ave',304),
|
||
(193000000000000174,193000000000100174,'PIT1','PIT1','PA','Pittsburgh','15205','UTC-5','2250 Roswell Drive',229),
|
||
(193000000000000175,193000000000100175,'PIT2','PIT2','PA','IMPERIAL','15126-1002','UTC-5','1200 Westport Rd',158),
|
||
(193000000000000176,193000000000100176,'POC1','POC1','CA','FONTANA','92336-1123','UTC-8','4121 LYTLE CREEK RD',46),
|
||
(193000000000000177,193000000000100177,'POC2','POC2','CA','ONTARIO','91761','UTC-8','4000 South Hamner Avenue',38),
|
||
(193000000000000178,193000000000100178,'POC3','POC3','CA','JURUPA VALLEY','92509','UTC-8','6120 Clinker Drive',45),
|
||
(193000000000000179,193000000000100179,'PPO4','PPO4','IN','MERRILLVILLE','46410','UTC-6','9850 Mississippi Street',49),
|
||
(193000000000000180,193000000000100180,'PSC2','PSC2','WA','PASCO','99301','UTC-8','1351 S Road 40 E',93),
|
||
(193000000000000181,193000000000100181,'PSP3','PSP3','CA','DESERT HOT SPRINGS','92240','UTC-8','64165 19th AVE',28),
|
||
(193000000000000182,193000000000100182,'QXY5','QXY5','CA','STOCKTON','95215','UTC-8','4532 NEWCASTLE RD',71),
|
||
(193000000000000183,193000000000100183,'QXY8','QXY8','CA','FRESNO','93725-1962','UTC-8','3611 S NORTHPOINTE DR',35),
|
||
(193000000000000184,193000000000100184,'QZZ7','QZZ7','AZ','MESA','85212-9608','UTC-7','8560 E ELLIOT RD',14),
|
||
(193000000000000185,193000000000100185,'RDU2','RDU2','NC','Smithfield','27577','UTC-5','2150 US HWY 70 Business West',146),
|
||
(193000000000000186,193000000000100186,'RDU4','RDU4','NC','FAYETTEVILLE','28303','UTC-5','6309 Bragg Blvd',95),
|
||
(193000000000000187,193000000000100187,'RFD2','RFD2','IL','Huntley','60142','UTC-6','11500 Freeman Road',145),
|
||
(193000000000000188,193000000000100188,'RFD4','RFD4','IL','HUNTLEY','60142','UTC-6','11400 Venture Court 60142-8075 - HUNTLEY, IL - United States',16),
|
||
(193000000000000189,193000000000100189,'RIC1','RIC1','VA','Petersburg','23803','UTC-5','5000 Commerce Way',210),
|
||
(193000000000000190,193000000000100190,'RIC2','RIC2','VA','Chester','23836','UTC-5','1901 Meadowville Technology Pkwy',209),
|
||
(193000000000000191,193000000000100191,'RMN3','RMN3','VA','Fredericksburg','22406','UTC-5','220 Centreport Parkway',144),
|
||
(193000000000000192,193000000000100192,'RNO1','RNO1','NV','Fernley','89408','UTC-8','1600 East Newlands RD',248),
|
||
(193000000000000193,193000000000100193,'RNO2','RNO2','NV','Reno','85906','UTC-8','8000 North Virginia Street',245),
|
||
(193000000000000194,193000000000100194,'RNO4','RNO4','NV','Reno','89506','UTC-8','8000 North Virginia Street',244),
|
||
(193000000000000195,193000000000100195,'RYY2','RYY2','GA','White','30184','UTC-5','1250 Cassville White Road,',73),
|
||
(193000000000000196,193000000000100196,'SAT1','SAT1','TX','Schertz','78154-1461','UTC-6','6000 Enterprise Avenue',218),
|
||
(193000000000000197,193000000000100197,'SAT4','SAT4','TX','SAN ANTONIO','78245','UTC-6','10384 W US HIGHWAY 90',149),
|
||
(193000000000000198,193000000000100198,'SAT6','SAT6','TX','SAN MARCOS','78666','UTC-6','1346 Fortuna Road 78666-0001 - SAN MARCOS, TX - United States',15),
|
||
(193000000000000199,193000000000100199,'SAV3','SAV3','GA','Macon','31216-6427','UTC-5','7001 Skipper Rd',98),
|
||
(193000000000000200,193000000000100200,'SBD1','SBD1','CA','BLOOMINGTON','92316-3819','UTC-8','3388 S Cactus Ave',185),
|
||
(193000000000000201,193000000000100201,'SBD2','SBD2','CA','SAN BERNARDINO','92408-2805','UTC-8','1494 S WATERMAN AVE',179),
|
||
(193000000000000202,193000000000100202,'SCK1','SCK1','CA','STOCKTON','95215','UTC-8','4532 NEWCASTLE RD',97),
|
||
(193000000000000203,193000000000100203,'SCK3','SCK3','CA','MANTECA','95336','UTC-8','3565 N AIRPORT WAY MANTECA',60),
|
||
(193000000000000204,193000000000100204,'SCK4','SCK4','CA','STOCKTON','95215-8354','UTC-8','6001 S AUSTIN RD',186),
|
||
(193000000000000205,193000000000100205,'SCK8','SCK8','CA','OAKLEY','94561-2677','UTC-8','4700 WILBUR AVE',104),
|
||
(193000000000000206,193000000000100206,'SDF1','SDF1','KY','Campbellsville','42718','UTC-5','1050 South Columbia AVE',266),
|
||
(193000000000000207,193000000000100207,'SDF2','SDF2','KY','Watterson Park','40218','UTC-5','4360 Robards Ln',265),
|
||
(193000000000000208,193000000000100208,'SDF4','SDF4','KY','Shepherdsville','40165','UTC-5','376 Zappos.com Blvd',264),
|
||
(193000000000000209,193000000000100209,'SDF6','SDF6','KY','Shepherdsville','40165','UTC-5','271 Omega Pkwy',263),
|
||
(193000000000000210,193000000000100210,'SDF7','SDF7','KY','Shepherdsville','40165','UTC-5','300 Omicron Court',258),
|
||
(193000000000000211,193000000000100211,'SDF8','SDF8','IN','Jeffersonville','47130','UTC-5','900 Patrol Rd',273),
|
||
(193000000000000212,193000000000100212,'SDF9','SDF9','KY','Shepherdsville','40165','UTC-5','100 W. Thomas P. Echols Lane',257),
|
||
(193000000000000213,193000000000100213,'SEA6','SEA6','WA','South Seattle','98144','UTC-8','2646 Rainier Ave.',205),
|
||
(193000000000000214,193000000000100214,'SEA8','SEA8','WA','Northeast Bellevue','98005','UTC-8','1475 124th Avenue',204),
|
||
(193000000000000215,193000000000100215,'SJC7','SJC7','CA','Tracy','95377-8906','UTC-8','188 Mountain House Parkway',295),
|
||
(193000000000000216,193000000000100216,'SLC2','SLC2','UT','West Jordan','84081','UTC-7','7148 W. Old Bingham Hwy',153),
|
||
(193000000000000217,193000000000100217,'SLC3','SLC3','UT','SALT LAKE CITY','84116-4413','UTC-7','355 N JOHN GLENN RD',13),
|
||
(193000000000000218,193000000000100218,'SMF3','SMF3','CA','Stockton','95206-8202','UTC-8','3923 S B ST',184),
|
||
(193000000000000219,193000000000100219,'SMF6','SMF6','CA','SACRAMENTO','95837-9109','UTC-8','4930 ALLBAUGH DR',105),
|
||
(193000000000000220,193000000000100220,'SNA4','SNA4','CA','Rialto','92376-3009','UTC-8','2496 W Walnut Ave',296),
|
||
(193000000000000221,193000000000100221,'STL3','STL3','MO','REPUBLIC','65738-2779','UTC-6','3200 E SAWYER RD',109),
|
||
(193000000000000222,193000000000100222,'STL4','STL4','IL','Edwardsville','62025-2815','UTC-6','3050 GATEWAY COMMERCE CENTER DR S',194),
|
||
(193000000000000223,193000000000100223,'SWF1','SWF1','NY','ROCK TAVERN','12575','UTC-5','635 INTERNATIONAL BLVD',122),
|
||
(193000000000000224,193000000000100224,'SWF2','SWF2','NY','Hopewell Junction','12533','UTC-5','76 Patriot Way Hopewell Junction',76),
|
||
(193000000000000225,193000000000100225,'TCY1','TCY1','CA','STOCKTON','95206','UTC-8','2690 East Arch Airport Road',41),
|
||
(193000000000000226,193000000000100226,'TCY2','TCY2','CA','STOCKTON','95215','UTC-8','6201 Newcastle Rd',43),
|
||
(193000000000000227,193000000000100227,'TEB3','TEB3','NJ','Logan Township','08085','UTC-5','2651 Oldmans Creek Rd',250),
|
||
(193000000000000228,193000000000100228,'TEB4','TEB4','NJ','PENNS GROVE','08069','UTC-5','747 COURSES LANDING RD',130),
|
||
(193000000000000229,193000000000100229,'TEB6','TEB6','NJ','Cranbury','08512','UTC-5','22 Hightstown Cranbury State Road',249),
|
||
(193000000000000230,193000000000100230,'TEB9','TEB9','NJ','Somerset','08873','UTC-5','601 Randolph Road',156),
|
||
(193000000000000231,193000000000100231,'TMB8','TMB8','FL','HOMESTEAD','33032-8597','UTC-5','27505 SW 132nd AVE',65),
|
||
(193000000000000232,193000000000100232,'TOL3','TOL3','OH','PERRYSBURG','43551','UTC-5','9240 Fremont Pike 43551-4210 - PERRYSBURG, OH - United States',18),
|
||
(193000000000000233,193000000000100233,'TPA1','TPA1','FL','Ruskin','33570','UTC-5','3350 Laurel Ridge Ave.',284),
|
||
(193000000000000234,193000000000100234,'TPA2','TPA2','FL','Lakeland','33811','UTC-5','1760 County Line Rd',283),
|
||
(193000000000000235,193000000000100235,'TPA3','TPA3','FL','Auburndale','33823','UTC-5','676 C. Fred Jones Boulevard',124),
|
||
(193000000000000236,193000000000100236,'TUL1','TUL1','KS','Coffeyville','67337','UTC-6','2654 North Highway 169',272),
|
||
(193000000000000237,193000000000100237,'VGT2','VGT2','NV','LAS VEGAS','89115-2013','UTC-8','6401 Howdy Wells Ave',172),
|
||
(193000000000000238,193000000000100238,'VUBA','VUBA','PA','Scranton','18512','UTC-5','1000 Keystone Industrial Park',228),
|
||
(193000000000000239,193000000000100239,'VUGA','VUGA','PA','Pittston','18643','UTC-5','508 Delaware Avenue West',227),
|
||
(193000000000000240,193000000000100240,'WBW2','WBW2','PA','OLYPHANT','18447','UTC-5','1300 Corporate Way',82),
|
||
(193000000000000241,193000000000100241,'XAV3','XAV3','GA','PORT WENTWORTH','31407','UTC-5','XAV3-120Crossgate Dr 31407-Port Wentworth, GA- United States',36),
|
||
(193000000000000242,193000000000100242,'XEW5','XEW5','NJ','Cinnaminson','08077-2015','UTC-5','995 Taylors Ln 08077-2015',37),
|
||
(193000000000000243,193000000000100243,'XIN5','XIN5','IN','Avon','46123-1700','UTC-5','8838 E COUNTY ROAD 100 S',25),
|
||
(193000000000000244,193000000000100244,'XLX1','XLX1','MD','HAGERSTOWN','21740','UTC-5','1115 Wesel Blvd HAGERSTOWN,MD 21740,USA',34),
|
||
(193000000000000245,193000000000100245,'XLX6','XLX6','NC','PINEVILLE','28134','UTC-5','12220 Carolina Logistics Drive PINEVILLE,NC 28134,USA',23),
|
||
(193000000000000246,193000000000100246,'XLX7','XLX7','CA','VICTORVILLE','92394','UTC-8','18580 Gateway Dr,Victorville,CA 92394,USA',63),
|
||
(193000000000000247,193000000000100247,'XPB2','XPB2','FL','APOPKA','32712','UTC-5','XPB2 - 4661 Apopka Logistics Pkwy 32712 - Apopka, FL - United States',26),
|
||
(193000000000000248,193000000000100248,'XRI3','XRI3','VA','Richmond','23234','UTC-5','4949 Commerce Rd, Richmond, VA 23234',24),
|
||
(193000000000000249,193000000000100249,'XUSB','XUSB','TX','Fort Worth','76155','UTC-6','14900 Frye Rd.',213),
|
||
(193000000000000250,193000000000100250,'XUSC','XUSC','PA','Carlisle','17013','UTC-5','40 Logistics Drive',226),
|
||
(193000000000000251,193000000000100251,'XUSD','XUSD','CA','Stockton','95206','UTC-8','1909 Zephyr St',297),
|
||
(193000000000000252,193000000000100252,'XUSE','XUSE','IN','Whitestown','46075','UTC-5','5100 S Indianapolis Rd',280);
|
||
|
||
DROP PROCEDURE IF EXISTS assert_wh_fba_public_master_data_seed;
|
||
DELIMITER //
|
||
CREATE PROCEDURE assert_wh_fba_public_master_data_seed()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM tmp_wh_fba_public_master_data) <> 252 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共主数据源数量不是252';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM tmp_wh_fba_public_master_data
|
||
WHERE warehouse_code <> UPPER(TRIM(warehouse_code))
|
||
OR warehouse_code NOT REGEXP '^[A-Z0-9]{3,5}$'
|
||
OR TRIM(city) = ''
|
||
OR TRIM(state_province) = ''
|
||
OR TRIM(address_line1) = ''
|
||
OR TRIM(postal_code) = ''
|
||
OR TRIM(timezone) = ''
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共主数据存在未清洗或必填缺失记录';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_warehouse existing
|
||
JOIN tmp_wh_fba_public_master_data source
|
||
ON existing.id = source.warehouse_id_seed
|
||
AND (existing.tenant_id <> 0 OR existing.warehouse_code <> source.warehouse_code OR existing.deleted <> 0)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共主数据仓库种子ID与既有数据冲突';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_warehouse_address existing
|
||
JOIN tmp_wh_fba_public_master_data source
|
||
ON existing.id = source.address_id_seed
|
||
LEFT JOIN wh_warehouse parent
|
||
ON parent.id = existing.warehouse_id
|
||
AND parent.tenant_id = existing.tenant_id
|
||
WHERE existing.tenant_id <> 0
|
||
OR existing.deleted <> 0
|
||
OR parent.warehouse_code IS NULL
|
||
OR parent.warehouse_code <> source.warehouse_code
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共主数据地址种子ID与既有数据冲突';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_warehouse existing
|
||
JOIN tmp_wh_fba_public_master_data source
|
||
ON existing.tenant_id = 0
|
||
AND existing.warehouse_code = source.warehouse_code
|
||
AND existing.deleted = 1
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共主数据存在同编码已删除仓库,需人工确认后恢复或清理';
|
||
END IF;
|
||
END//
|
||
DELIMITER ;
|
||
CALL assert_wh_fba_public_master_data_seed();
|
||
DROP PROCEDURE IF EXISTS assert_wh_fba_public_master_data_seed;
|
||
|
||
INSERT INTO wh_warehouse (
|
||
id, tenant_id, warehouse_code, warehouse_name, warehouse_type, country_code, timezone,
|
||
contact_name, contact_phone, contact_email, status, remark,
|
||
created_by, created_at, updated_by, updated_at, deleted, version
|
||
)
|
||
SELECT
|
||
source.warehouse_id_seed,
|
||
0,
|
||
source.warehouse_code,
|
||
source.warehouse_name,
|
||
'FBA',
|
||
'US',
|
||
source.timezone,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
'ACTIVE',
|
||
NULL,
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
0,
|
||
0
|
||
FROM tmp_wh_fba_public_master_data source
|
||
ON DUPLICATE KEY UPDATE
|
||
warehouse_type = VALUES(warehouse_type),
|
||
country_code = VALUES(country_code),
|
||
timezone = VALUES(timezone),
|
||
updated_at = CURRENT_TIMESTAMP(3);
|
||
|
||
INSERT INTO wh_warehouse_address (
|
||
id, tenant_id, warehouse_id, address_type, country_code, state_province, city,
|
||
district, postal_code, address_line1, address_line2, latitude, longitude,
|
||
created_by, created_at, updated_by, updated_at, deleted, version
|
||
)
|
||
SELECT
|
||
source.address_id_seed,
|
||
0,
|
||
warehouse.id,
|
||
'OPERATING',
|
||
'US',
|
||
source.state_province,
|
||
source.city,
|
||
NULL,
|
||
source.postal_code,
|
||
source.address_line1,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
NULL,
|
||
CURRENT_TIMESTAMP(3),
|
||
0,
|
||
0
|
||
FROM tmp_wh_fba_public_master_data source
|
||
JOIN wh_warehouse warehouse
|
||
ON warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_code = source.warehouse_code
|
||
AND warehouse.deleted = 0
|
||
ON DUPLICATE KEY UPDATE
|
||
tenant_id = VALUES(tenant_id),
|
||
country_code = VALUES(country_code),
|
||
state_province = VALUES(state_province),
|
||
city = VALUES(city),
|
||
postal_code = VALUES(postal_code),
|
||
address_line1 = VALUES(address_line1),
|
||
updated_at = CURRENT_TIMESTAMP(3);
|
||
|
||
DROP PROCEDURE IF EXISTS verify_wh_fba_public_master_data;
|
||
DELIMITER //
|
||
CREATE PROCEDURE verify_wh_fba_public_master_data()
|
||
BEGIN
|
||
IF (SELECT COUNT(*)
|
||
FROM wh_warehouse warehouse
|
||
JOIN tmp_wh_fba_public_master_data source
|
||
ON source.warehouse_code = warehouse.warehouse_code
|
||
WHERE warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_type = 'FBA'
|
||
AND warehouse.country_code = 'US'
|
||
AND warehouse.deleted = 0) <> 252 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共仓库写入数量校验失败';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM tmp_wh_fba_public_master_data source
|
||
LEFT JOIN wh_warehouse warehouse
|
||
ON warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_code = source.warehouse_code
|
||
AND warehouse.deleted = 0
|
||
LEFT JOIN wh_warehouse_address address
|
||
ON address.tenant_id = 0
|
||
AND address.warehouse_id = warehouse.id
|
||
AND address.address_type = 'OPERATING'
|
||
AND address.deleted = 0
|
||
WHERE warehouse.id IS NULL
|
||
OR address.id IS NULL
|
||
OR address.country_code <> 'US'
|
||
OR address.state_province <> source.state_province
|
||
OR address.city <> source.city
|
||
OR address.postal_code <> source.postal_code
|
||
OR address.address_line1 <> source.address_line1
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共地址父子解析或地址字段校验失败';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM wh_service_capability capability
|
||
JOIN wh_warehouse warehouse
|
||
ON warehouse.id = capability.warehouse_id
|
||
AND warehouse.tenant_id = capability.tenant_id
|
||
WHERE warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_type = 'FBA'
|
||
AND warehouse.warehouse_code IN (SELECT warehouse_code FROM tmp_wh_fba_public_master_data)
|
||
AND capability.deleted = 0
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'FBA公共地址不得创建平台作业能力';
|
||
END IF;
|
||
END//
|
||
DELIMITER ;
|
||
CALL verify_wh_fba_public_master_data();
|
||
DROP PROCEDURE IF EXISTS verify_wh_fba_public_master_data;
|
||
DROP PROCEDURE IF EXISTS verify_wh_fixed_platform_warehouse;
|
||
DELIMITER //
|
||
CREATE PROCEDURE verify_wh_fixed_platform_warehouse()
|
||
BEGIN
|
||
IF (SELECT COUNT(*)
|
||
FROM wh_warehouse warehouse
|
||
JOIN tmp_wh_fixed_platform_warehouse source
|
||
ON source.warehouse_code = warehouse.warehouse_code
|
||
WHERE warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_type = source.warehouse_type
|
||
AND warehouse.country_code = source.country_code
|
||
AND warehouse.deleted = 0) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓写入数量校验失败';
|
||
END IF;
|
||
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM tmp_wh_fixed_platform_warehouse source
|
||
LEFT JOIN wh_warehouse warehouse
|
||
ON warehouse.tenant_id = 0
|
||
AND warehouse.warehouse_code = source.warehouse_code
|
||
AND warehouse.deleted = 0
|
||
LEFT JOIN wh_warehouse_address address
|
||
ON address.tenant_id = 0
|
||
AND address.warehouse_id = warehouse.id
|
||
AND address.address_type = 'OPERATING'
|
||
AND address.deleted = 0
|
||
WHERE warehouse.id IS NULL
|
||
OR address.id IS NULL
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '固定平台仓地址父子解析失败';
|
||
END IF;
|
||
|
||
IF (SELECT COUNT(*)
|
||
FROM wh_service_capability capability
|
||
WHERE capability.tenant_id = 0
|
||
AND capability.warehouse_id IN (193000000000200001,193000000000200002)
|
||
AND capability.capability_code = 'RECEIVING'
|
||
AND capability.enabled = 1
|
||
AND capability.deleted = 0) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '国内固定作业仓收货能力写入校验失败';
|
||
END IF;
|
||
END//
|
||
DELIMITER ;
|
||
CALL verify_wh_fixed_platform_warehouse();
|
||
DROP PROCEDURE IF EXISTS verify_wh_fixed_platform_warehouse;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_wh_fba_public_master_data;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_wh_fixed_platform_warehouse;
|
||
|
||
-- 全部增量完成后的最终结构强校验;只有通过此段才输出唯一 PASS
|
||
-- ============================================================
|
||
USE baiyue_firstleg;
|
||
DROP PROCEDURE IF EXISTS unified_publish_fee_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_publish_fee_add_column(
|
||
IN target_table VARCHAR(64), IN target_column VARCHAR(64), IN column_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = target_table AND column_name = target_column
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', target_table, '` ADD COLUMN `', target_column, '` ', column_definition);
|
||
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_publish_fee_add_column('fl_fee_item', 'fee_code',
|
||
"VARCHAR(50) NULL COMMENT '稳定费用编码;系统费用使用固定编码' AFTER trade_scope");
|
||
ALTER TABLE fl_fee_item
|
||
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共费用项目作用域,固定为0;物理字段名沿用 tenant_id',
|
||
COMMENT = '平台公共费用项目主数据';
|
||
ALTER TABLE fl_inbound_fee_detail
|
||
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID(客户隔离键);费用明细引用平台公共费用项目';
|
||
|
||
-- 费用类型.xls(SHA-256 395C9CA71816E79C7745E224E63CF79A4AB782140364F63C8374B396DF38CDB4)
|
||
-- 单表费用类型:110条(国内84、国际26);源表无编码列,按受控行序生成稳定编码。
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_fee_item_seed;
|
||
CREATE TEMPORARY TABLE tmp_fee_item_seed (
|
||
seed_no INT NOT NULL PRIMARY KEY,
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
fee_code VARCHAR(50) NOT NULL,
|
||
category VARCHAR(64) NOT NULL,
|
||
trade_scope VARCHAR(20) NOT NULL,
|
||
fee_type_name VARCHAR(128) NOT NULL,
|
||
pickup_payable TINYINT(1) NOT NULL,
|
||
pickup_receivable TINYINT(1) NOT NULL,
|
||
warehouse_payable TINYINT(1) NOT NULL,
|
||
warehouse_receivable TINYINT(1) NOT NULL,
|
||
allocation_fee TINYINT(1) NOT NULL,
|
||
description VARCHAR(500) NULL,
|
||
created_by BIGINT NULL,
|
||
created_by_name VARCHAR(64) NULL,
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_by BIGINT NULL,
|
||
updated_by_name VARCHAR(64) NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
UNIQUE KEY uk_tmp_fee_seed_code (fee_code),
|
||
UNIQUE KEY uk_tmp_fee_seed_semantic (trade_scope, fee_type_name)
|
||
) ENGINE=MEMORY;
|
||
|
||
INSERT INTO tmp_fee_item_seed (seed_no, id, fee_code, category, trade_scope, fee_type_name, pickup_payable, pickup_receivable, warehouse_payable, warehouse_receivable, allocation_fee, description, created_by, created_by_name, created_at, updated_by, updated_by_name, updated_at) VALUES
|
||
(1,193000000001000001,'FEE_TYPE_001','洛杉矶仓储成本','INTERNATIONAL','拖车费2',0,0,1,0,0,NULL,NULL,'admin','2023-07-10 00:00:00.000',NULL,'admin','2023-07-10 00:00:00.000'),
|
||
(2,193000000001000002,'FEE_TYPE_002','洛杉矶仓储成本','INTERNATIONAL','仓库水电费',0,0,1,0,0,NULL,NULL,'admin','2023-07-08 00:00:00.000',NULL,'admin','2023-07-08 00:00:00.000'),
|
||
(3,193000000001000003,'FEE_TYPE_003','洛杉矶仓储成本','INTERNATIONAL','人工费',0,0,1,0,0,'人工工资费用',NULL,'admin','2023-07-08 00:00:00.000',NULL,'admin','2023-07-08 00:00:00.000'),
|
||
(4,193000000001000004,'FEE_TYPE_004','洛杉矶仓储成本','INTERNATIONAL','仓库租赁费',0,0,1,0,0,'应付 仓库租赁费',NULL,'admin','2023-07-08 00:00:00.000',NULL,'admin','2023-07-08 00:00:00.000'),
|
||
(5,193000000001000005,'FEE_TYPE_005','默认','DOMESTIC','删单费',1,1,0,0,1,NULL,15268360352,'15268360352','2026-05-29 00:00:00.000',15268360352,'15268360352','2026-05-29 00:00:00.000'),
|
||
(6,193000000001000006,'FEE_TYPE_006','默认','INTERNATIONAL','换FBA标费用',1,1,1,1,1,NULL,NULL,'zhaoting','2026-03-04 00:00:00.000',NULL,'zhaoting','2026-03-04 00:00:00.000'),
|
||
(7,193000000001000007,'FEE_TYPE_007','默认','INTERNATIONAL','换SKU费用',1,1,1,1,1,NULL,NULL,'zhaoting','2026-03-04 00:00:00.000',NULL,'zhaoting','2026-03-04 00:00:00.000'),
|
||
(8,193000000001000008,'FEE_TYPE_008','默认','INTERNATIONAL','开箱费',0,1,1,1,1,NULL,NULL,'zhaoting','2026-03-04 00:00:00.000',NULL,'zhaoting','2026-03-04 00:00:00.000'),
|
||
(9,193000000001000009,'FEE_TYPE_009','默认','DOMESTIC','天翔八提',1,1,0,0,1,NULL,15268360352,'15268360352','2025-08-19 00:00:00.000',15268360352,'15268360352','2025-08-19 00:00:00.000'),
|
||
(10,193000000001000010,'FEE_TYPE_010','默认','DOMESTIC','天翔五提',1,1,0,0,1,NULL,15268360352,'15268360352','2025-08-05 00:00:00.000',15268360352,'15268360352','2025-08-05 00:00:00.000'),
|
||
(11,193000000001000011,'FEE_TYPE_011','默认','DOMESTIC','荣盛三提',1,1,0,0,1,NULL,15268360352,'15268360352','2025-08-05 00:00:00.000',15268360352,'15268360352','2025-08-05 00:00:00.000'),
|
||
(12,193000000001000012,'FEE_TYPE_012','默认','DOMESTIC','两地费',1,1,0,0,1,NULL,15268360352,'15268360352','2025-08-04 00:00:00.000',15268360352,'15268360352','2025-08-04 00:00:00.000'),
|
||
(13,193000000001000013,'FEE_TYPE_013','默认','DOMESTIC','绕路费',1,1,0,0,1,NULL,15268360352,'15268360352','2025-08-04 00:00:00.000',15268360352,'15268360352','2025-08-04 00:00:00.000'),
|
||
(14,193000000001000014,'FEE_TYPE_014','默认','DOMESTIC','朝华提',1,1,0,0,1,NULL,15268360352,'15268360352','2025-07-31 00:00:00.000',15268360352,'15268360352','2025-07-31 00:00:00.000'),
|
||
(15,193000000001000015,'FEE_TYPE_015','默认','DOMESTIC','长胜提',1,1,0,0,1,NULL,15268360352,'15268360352','2025-07-31 00:00:00.000',15268360352,'15268360352','2025-07-31 00:00:00.000'),
|
||
(16,193000000001000016,'FEE_TYPE_016','默认','DOMESTIC','港杂费',1,1,0,0,1,NULL,15268360352,'15268360352','2025-07-24 00:00:00.000',15268360352,'15268360352','2025-07-24 00:00:00.000'),
|
||
(17,193000000001000017,'FEE_TYPE_017','默认','DOMESTIC','极偏远费',1,1,1,1,1,NULL,NULL,'zhaoting','2025-06-11 00:00:00.000',NULL,'zhaoting','2025-06-11 00:00:00.000'),
|
||
(18,193000000001000018,'FEE_TYPE_018','默认','DOMESTIC','退舱费',1,0,0,0,0,NULL,15690305912,'15690305912','2025-05-22 00:00:00.000',15690305912,'15690305912','2025-05-22 00:00:00.000'),
|
||
(19,193000000001000019,'FEE_TYPE_019','默认','DOMESTIC','推送费',1,1,0,0,0,NULL,15690305912,'15690305912','2025-04-07 00:00:00.000',15690305912,'15690305912','2025-04-07 00:00:00.000'),
|
||
(20,193000000001000020,'FEE_TYPE_020','默认','DOMESTIC','产品附加费',1,1,1,1,1,NULL,NULL,'zhaoting','2025-04-02 00:00:00.000',NULL,'zhaoting','2025-04-02 00:00:00.000'),
|
||
(21,193000000001000021,'FEE_TYPE_021','默认','DOMESTIC','联单费',1,1,0,0,1,NULL,15268360352,'15268360352','2025-04-01 00:00:00.000',15268360352,'15268360352','2025-04-01 00:00:00.000'),
|
||
(22,193000000001000022,'FEE_TYPE_022','默认','DOMESTIC','竹木制品小额商检',1,1,0,0,1,NULL,15268360352,'15268360352','2025-04-01 00:00:00.000',15268360352,'15268360352','2025-04-01 00:00:00.000'),
|
||
(23,193000000001000023,'FEE_TYPE_023','默认','DOMESTIC','放单费',1,0,0,0,1,NULL,15268360352,'15268360352','2025-03-27 00:00:00.000',15268360352,'15268360352','2025-03-27 00:00:00.000'),
|
||
(24,193000000001000024,'FEE_TYPE_024','默认','DOMESTIC','退关费',1,1,1,1,1,NULL,15690305912,'15690305912','2025-03-17 00:00:00.000',15690305912,'15690305912','2025-03-17 00:00:00.000'),
|
||
(25,193000000001000025,'FEE_TYPE_025','默认','DOMESTIC','补收税金',0,1,0,0,0,NULL,NULL,'lijiayin','2025-03-07 00:00:00.000',NULL,'lijiayin','2025-03-07 00:00:00.000'),
|
||
(26,193000000001000026,'FEE_TYPE_026','默认','INTERNATIONAL','HC',1,1,1,1,1,NULL,15268360352,'15268360352','2025-02-27 00:00:00.000',15268360352,'15268360352','2025-02-27 00:00:00.000'),
|
||
(27,193000000001000027,'FEE_TYPE_027','默认','DOMESTIC','舱单数据传输费',1,1,1,1,1,NULL,15268360352,'15268360352','2025-02-27 00:00:00.000',15268360352,'15268360352','2025-02-27 00:00:00.000'),
|
||
(28,193000000001000028,'FEE_TYPE_028','默认','DOMESTIC','转船费',1,1,1,1,1,NULL,15268360352,'15268360352','2025-02-27 00:00:00.000',15268360352,'15268360352','2025-02-27 00:00:00.000'),
|
||
(29,193000000001000029,'FEE_TYPE_029','默认','DOMESTIC','改配费',1,1,1,1,1,NULL,15268360352,'15268360352','2025-02-27 00:00:00.000',15268360352,'15268360352','2025-02-27 00:00:00.000'),
|
||
(30,193000000001000030,'FEE_TYPE_030','默认','INTERNATIONAL','巴拿马附加费',1,1,1,1,1,NULL,15268360352,'15268360352','2025-02-21 00:00:00.000',15268360352,'15268360352','2025-02-21 00:00:00.000'),
|
||
(31,193000000001000031,'FEE_TYPE_031','默认','DOMESTIC','报关退点',1,1,0,0,1,NULL,15268360352,'15268360352','2025-02-12 00:00:00.000',15268360352,'15268360352','2025-02-12 00:00:00.000'),
|
||
(32,193000000001000032,'FEE_TYPE_032','默认','INTERNATIONAL','滞箱费',1,1,0,0,1,NULL,15268360352,'15268360352','2025-02-11 00:00:00.000',15268360352,'15268360352','2025-02-11 00:00:00.000'),
|
||
(33,193000000001000033,'FEE_TYPE_033','默认','DOMESTIC','大榭提',1,0,0,0,1,NULL,15268360352,'15268360352','2025-02-10 00:00:00.000',15268360352,'15268360352','2025-02-10 00:00:00.000'),
|
||
(34,193000000001000034,'FEE_TYPE_034','默认','DOMESTIC','进四期',1,0,0,0,1,NULL,15268360352,'15268360352','2025-02-10 00:00:00.000',15268360352,'15268360352','2025-02-10 00:00:00.000'),
|
||
(35,193000000001000035,'FEE_TYPE_035','默认','DOMESTIC','入仓费',1,1,1,1,1,NULL,NULL,'zhaoting','2025-01-22 00:00:00.000',NULL,'zhaoting','2025-01-22 00:00:00.000'),
|
||
(36,193000000001000036,'FEE_TYPE_036','默认','DOMESTIC','晚截关',1,0,0,0,0,NULL,15268360352,'15268360352','2025-01-16 00:00:00.000',15268360352,'15268360352','2025-01-16 00:00:00.000'),
|
||
(37,193000000001000037,'FEE_TYPE_037','默认','DOMESTIC','装柜费',0,1,0,0,1,NULL,15268360352,'15268360352','2024-12-14 00:00:00.000',15268360352,'15268360352','2024-12-14 00:00:00.000'),
|
||
(38,193000000001000038,'FEE_TYPE_038','默认','DOMESTIC','亏舱费',0,1,0,0,1,NULL,NULL,'zhaoting','2024-09-23 00:00:00.000',NULL,'zhaoting','2024-09-23 00:00:00.000'),
|
||
(39,193000000001000039,'FEE_TYPE_039','默认','INTERNATIONAL','暂扣费用',0,1,0,0,0,NULL,NULL,'lvhonglu','2024-08-08 00:00:00.000',NULL,'lvhonglu','2024-08-08 00:00:00.000'),
|
||
(40,193000000001000040,'FEE_TYPE_040','默认','DOMESTIC','改单费',1,1,0,0,1,NULL,NULL,'zhaoting','2024-07-30 00:00:00.000',NULL,'zhaoting','2024-07-30 00:00:00.000'),
|
||
(41,193000000001000041,'FEE_TYPE_041','默认','DOMESTIC','叉车费',1,1,0,0,0,NULL,NULL,'zhaoting','2024-07-03 00:00:00.000',NULL,'zhaoting','2024-07-03 00:00:00.000'),
|
||
(42,193000000001000042,'FEE_TYPE_042','默认','INTERNATIONAL','全包价',1,0,1,0,1,'提柜+派送',NULL,'huangmengting','2024-06-27 00:00:00.000',NULL,'huangmengting','2024-06-27 00:00:00.000'),
|
||
(43,193000000001000043,'FEE_TYPE_043','默认','INTERNATIONAL','快递费',1,0,0,0,0,'快递打单费用',NULL,'lvhonglu','2024-05-10 00:00:00.000',NULL,'lvhonglu','2024-05-10 00:00:00.000'),
|
||
(44,193000000001000044,'FEE_TYPE_044','默认','DOMESTIC','超偏远费',0,1,0,0,0,'海派超偏远地区收费(超偏远费+¥50/件)',NULL,'lijiayin','2024-05-10 00:00:00.000',NULL,'lijiayin','2024-05-10 00:00:00.000'),
|
||
(45,193000000001000045,'FEE_TYPE_045','默认','DOMESTIC','偏远费',0,1,0,0,0,'海派偏远地区收费(偏远费+¥30/件)',NULL,'lijiayin','2024-05-10 00:00:00.000',NULL,'lijiayin','2024-05-10 00:00:00.000'),
|
||
(46,193000000001000046,'FEE_TYPE_046','默认','DOMESTIC','超周长附加费',0,1,0,0,0,'(最短边+次长边)*2+最长边>260CM',NULL,'lijiayin','2024-05-10 00:00:00.000',NULL,'lijiayin','2024-05-10 00:00:00.000'),
|
||
(47,193000000001000047,'FEE_TYPE_047','默认','DOMESTIC','超重费',0,1,0,0,0,'海派单箱超22KG',NULL,'lijiayin','2024-05-10 00:00:00.000',NULL,'lijiayin','2024-05-10 00:00:00.000'),
|
||
(48,193000000001000048,'FEE_TYPE_048','默认','DOMESTIC','超长费',0,1,0,0,0,'箱子单边超120cm',NULL,'lijiayin','2024-05-10 00:00:00.000',NULL,'lijiayin','2024-05-10 00:00:00.000'),
|
||
(49,193000000001000049,'FEE_TYPE_049','默认','INTERNATIONAL','ISF',1,1,1,1,1,NULL,NULL,'lvhonglu','2023-07-21 00:00:00.000',NULL,'lvhonglu','2023-07-21 00:00:00.000'),
|
||
(50,193000000001000050,'FEE_TYPE_050','默认','INTERNATIONAL','国外查验费',1,1,0,0,0,NULL,NULL,'lvhonglu','2023-07-21 00:00:00.000',NULL,'lvhonglu','2023-07-21 00:00:00.000'),
|
||
(51,193000000001000051,'FEE_TYPE_051','默认','INTERNATIONAL','目的港关税',1,1,0,0,0,NULL,NULL,'lvhonglu','2023-07-21 00:00:00.000',NULL,'lvhonglu','2023-07-21 00:00:00.000'),
|
||
(52,193000000001000052,'FEE_TYPE_052','默认','DOMESTIC','停车费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(53,193000000001000053,'FEE_TYPE_053','默认','DOMESTIC','过磅费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(54,193000000001000054,'FEE_TYPE_054','默认','DOMESTIC','提箱费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(55,193000000001000055,'FEE_TYPE_055','默认','DOMESTIC','进港费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(56,193000000001000056,'FEE_TYPE_056','默认','DOMESTIC','保险费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(57,193000000001000057,'FEE_TYPE_057','默认','DOMESTIC','舱单费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(58,193000000001000058,'FEE_TYPE_058','默认','DOMESTIC','EDI',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(59,193000000001000059,'FEE_TYPE_059','默认','DOMESTIC','箱单费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(60,193000000001000060,'FEE_TYPE_060','默认','DOMESTIC','文件费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(61,193000000001000061,'FEE_TYPE_061','默认','DOMESTIC','THC',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(62,193000000001000062,'FEE_TYPE_062','默认','DOMESTIC','订舱费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(63,193000000001000063,'FEE_TYPE_063','默认','DOMESTIC','包干费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(64,193000000001000064,'FEE_TYPE_064','默认','DOMESTIC','换单费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-14 00:00:00.000',NULL,'yingchaoqun','2023-07-14 00:00:00.000'),
|
||
(65,193000000001000065,'FEE_TYPE_065','默认','DOMESTIC','整柜运费',1,1,1,1,1,NULL,NULL,'lvhonglu','2023-07-13 00:00:00.000',NULL,'lvhonglu','2023-07-13 00:00:00.000'),
|
||
(66,193000000001000066,'FEE_TYPE_066','默认','DOMESTIC','查验费',1,1,1,1,0,'国外海外查验',NULL,'zhaoting','2023-02-16 00:00:00.000',NULL,'admin','2023-05-18 00:00:00.000'),
|
||
(67,193000000001000067,'FEE_TYPE_067','默认','DOMESTIC','FDA申报费',1,1,1,1,0,'FDA申报费',NULL,'zhaoting','2023-02-16 00:00:00.000',NULL,'admin','2023-05-18 00:00:00.000'),
|
||
(68,193000000001000068,'FEE_TYPE_068','默认','DOMESTIC','LACEY ACT申报费',1,1,1,1,0,'LACEY ACT申报费',NULL,'zhaoting','2023-02-16 00:00:00.000',NULL,'zhaoting','2023-02-16 00:00:00.000'),
|
||
(69,193000000001000069,'FEE_TYPE_069','默认','DOMESTIC','报关费',1,1,1,1,0,'退税报关',NULL,'zhaoting','2023-02-16 00:00:00.000',NULL,'zhaoting','2023-02-16 00:00:00.000'),
|
||
(70,193000000001000070,'FEE_TYPE_070','默认','DOMESTIC','提货费',1,1,1,1,0,'提货费',NULL,'zhaoting','2023-02-16 00:00:00.000',NULL,'admin','2023-07-06 00:00:00.000'),
|
||
(71,193000000001000071,'FEE_TYPE_071','默认','INTERNATIONAL','贴标费',1,0,1,1,0,'贴标费',NULL,'admin','2022-11-25 00:00:00.000',NULL,'admin','2023-07-05 00:00:00.000'),
|
||
(72,193000000001000072,'FEE_TYPE_072','默认','DOMESTIC','补运费',1,1,1,1,0,'移仓补运费',NULL,'admin','2022-11-23 00:00:00.000',NULL,'admin','2022-11-23 00:00:00.000'),
|
||
(73,193000000001000073,'FEE_TYPE_073','默认','DOMESTIC','其他扣费',1,1,1,1,0,'其他扣费',NULL,'admin','2022-11-23 00:00:00.000',NULL,'admin','2022-11-23 00:00:00.000'),
|
||
(74,193000000001000074,'FEE_TYPE_074','默认','DOMESTIC','抵扣费用',1,1,1,1,0,'应付-抵扣费用',NULL,'admin','2022-11-23 00:00:00.000',NULL,'admin','2022-11-23 00:00:00.000'),
|
||
(75,193000000001000075,'FEE_TYPE_075','默认','INTERNATIONAL','派送费',1,0,1,1,0,'派送费',NULL,'admin','2022-04-16 00:00:00.000',NULL,'admin','2023-07-05 00:00:00.000'),
|
||
(76,193000000001000076,'FEE_TYPE_076','默认','DOMESTIC','运费',1,1,1,1,0,'头程运费',NULL,'admin','2022-04-16 00:00:00.000',NULL,'admin','2023-07-06 00:00:00.000'),
|
||
(77,193000000001000077,'FEE_TYPE_077','默认','INTERNATIONAL','仓储费',1,0,1,1,0,'仓储费',NULL,'admin','2022-04-16 00:00:00.000',NULL,'admin','2023-07-05 00:00:00.000'),
|
||
(78,193000000001000078,'FEE_TYPE_078','集装箱费用','INTERNATIONAL','提柜-额外费用',1,1,1,1,1,NULL,NULL,'jinshanshan','2024-12-05 00:00:00.000',NULL,'jinshanshan','2024-12-05 00:00:00.000'),
|
||
(79,193000000001000079,'FEE_TYPE_079','集装箱费用','INTERNATIONAL','移柜费',1,1,1,1,1,NULL,NULL,'jinshanshan','2024-11-22 00:00:00.000',NULL,'jinshanshan','2024-11-22 00:00:00.000'),
|
||
(80,193000000001000080,'FEE_TYPE_080','集装箱费用','DOMESTIC','四期提',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-12 00:00:00.000',NULL,'yingchaoqun','2024-08-12 00:00:00.000'),
|
||
(81,193000000001000081,'FEE_TYPE_081','集装箱费用','DOMESTIC','进大榭',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-12 00:00:00.000',NULL,'yingchaoqun','2024-08-12 00:00:00.000'),
|
||
(82,193000000001000082,'FEE_TYPE_082','集装箱费用','DOMESTIC','过夜费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(83,193000000001000083,'FEE_TYPE_083','集装箱费用','DOMESTIC','搞定费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(84,193000000001000084,'FEE_TYPE_084','集装箱费用','DOMESTIC','梅山提',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(85,193000000001000085,'FEE_TYPE_085','集装箱费用','DOMESTIC','进梅山',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(86,193000000001000086,'FEE_TYPE_086','集装箱费用','DOMESTIC','舱单录入费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(87,193000000001000087,'FEE_TYPE_087','集装箱费用','DOMESTIC','进洋山',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(88,193000000001000088,'FEE_TYPE_088','集装箱费用','DOMESTIC','洋山提',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(89,193000000001000089,'FEE_TYPE_089','集装箱费用','DOMESTIC','落箱费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-10 00:00:00.000',NULL,'yingchaoqun','2024-08-10 00:00:00.000'),
|
||
(90,193000000001000090,'FEE_TYPE_090','集装箱费用','DOMESTIC','预进港',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-09 00:00:00.000',NULL,'yingchaoqun','2024-08-09 00:00:00.000'),
|
||
(91,193000000001000091,'FEE_TYPE_091','集装箱费用','DOMESTIC','改运抵',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-09 00:00:00.000',NULL,'yingchaoqun','2024-08-09 00:00:00.000'),
|
||
(92,193000000001000092,'FEE_TYPE_092','集装箱费用','DOMESTIC','预提费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-08 00:00:00.000',NULL,'yingchaoqun','2024-08-08 00:00:00.000'),
|
||
(93,193000000001000093,'FEE_TYPE_093','集装箱费用','DOMESTIC','设备交接单',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-08 00:00:00.000',NULL,'yingchaoqun','2024-08-08 00:00:00.000'),
|
||
(94,193000000001000094,'FEE_TYPE_094','集装箱费用','DOMESTIC','操作费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-08 00:00:00.000',NULL,'yingchaoqun','2024-08-08 00:00:00.000'),
|
||
(95,193000000001000095,'FEE_TYPE_095','集装箱费用','DOMESTIC','VGM',1,1,1,1,1,NULL,NULL,'yingchaoqun','2024-08-08 00:00:00.000',NULL,'yingchaoqun','2024-08-08 00:00:00.000'),
|
||
(96,193000000001000096,'FEE_TYPE_096','集装箱费用','INTERNATIONAL','托盘费',1,0,0,0,1,NULL,NULL,'jinshanshan','2023-08-14 00:00:00.000',NULL,'jinshanshan','2023-08-14 00:00:00.000'),
|
||
(97,193000000001000097,'FEE_TYPE_097','集装箱费用','INTERNATIONAL','拆柜费',1,0,0,0,1,NULL,NULL,'jinshanshan','2023-08-14 00:00:00.000',NULL,'jinshanshan','2023-08-14 00:00:00.000'),
|
||
(98,193000000001000098,'FEE_TYPE_098','集装箱费用','DOMESTIC','加封费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-08-03 00:00:00.000',NULL,'yingchaoqun','2023-08-03 00:00:00.000'),
|
||
(99,193000000001000099,'FEE_TYPE_099','集装箱费用','DOMESTIC','电子锁',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-08-03 00:00:00.000',NULL,'yingchaoqun','2023-08-03 00:00:00.000'),
|
||
(100,193000000001000100,'FEE_TYPE_100','集装箱费用','DOMESTIC','吊机费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-19 00:00:00.000',NULL,'yingchaoqun','2023-07-19 00:00:00.000'),
|
||
(101,193000000001000101,'FEE_TYPE_101','集装箱费用','DOMESTIC','条码费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-19 00:00:00.000',NULL,'yingchaoqun','2023-07-19 00:00:00.000'),
|
||
(102,193000000001000102,'FEE_TYPE_102','集装箱费用','DOMESTIC','AMS',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-19 00:00:00.000',NULL,'yingchaoqun','2023-07-19 00:00:00.000'),
|
||
(103,193000000001000103,'FEE_TYPE_103','集装箱费用','DOMESTIC','海运费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-19 00:00:00.000',NULL,'yingchaoqun','2023-07-19 00:00:00.000'),
|
||
(104,193000000001000104,'FEE_TYPE_104','集装箱费用','DOMESTIC','堆存费',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-19 00:00:00.000',NULL,'yingchaoqun','2023-07-19 00:00:00.000'),
|
||
(105,193000000001000105,'FEE_TYPE_105','集装箱费用','DOMESTIC','电子装箱单',1,1,1,1,1,NULL,NULL,'yingchaoqun','2023-07-19 00:00:00.000',NULL,'yingchaoqun','2023-07-19 00:00:00.000'),
|
||
(106,193000000001000106,'FEE_TYPE_106','集装箱费用','INTERNATIONAL','提柜费',1,1,1,1,0,'提柜费',NULL,'admin','2023-05-18 00:00:00.000',NULL,'admin','2023-05-18 00:00:00.000'),
|
||
(107,193000000001000107,'FEE_TYPE_107','集装箱费用','INTERNATIONAL','清关费',1,1,1,1,0,'清关费',NULL,'admin','2023-05-18 00:00:00.000',NULL,'admin','2023-05-18 00:00:00.000'),
|
||
(108,193000000001000108,'FEE_TYPE_108','集装箱费用','DOMESTIC','拖车费',1,1,1,1,0,'拖车费',NULL,'admin','2023-05-18 00:00:00.000',NULL,'admin','2023-07-06 00:00:00.000'),
|
||
(109,193000000001000109,'FEE_TYPE_109','集装箱费用','DOMESTIC','加载费',1,1,1,1,0,'加载费',NULL,'admin','2023-05-18 00:00:00.000',NULL,'admin','2023-05-18 00:00:00.000'),
|
||
(110,193000000001000110,'FEE_TYPE_110','BOL费用','INTERNATIONAL','装车费',1,1,1,1,1,NULL,NULL,'jinshanshan','2025-01-17 00:00:00.000',NULL,'jinshanshan','2025-01-17 00:00:00.000');
|
||
|
||
DROP PROCEDURE IF EXISTS unified_publish_assert_fee_item_preflight;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_publish_assert_fee_item_preflight()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM tmp_fee_item_seed) <> 110 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:Excel种子总数不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM tmp_fee_item_seed WHERE trade_scope = 'DOMESTIC') <> 84 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:Excel国内种子数量不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM tmp_fee_item_seed WHERE trade_scope = 'INTERNATIONAL') <> 26 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:Excel国际种子数量不正确';
|
||
END IF;
|
||
IF EXISTS (SELECT fee_code FROM tmp_fee_item_seed GROUP BY fee_code HAVING COUNT(*) > 1) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:Excel种子编码重复';
|
||
END IF;
|
||
IF EXISTS (SELECT trade_scope, fee_type_name FROM tmp_fee_item_seed GROUP BY trade_scope, fee_type_name HAVING COUNT(*) > 1) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:Excel种子语义键重复';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT f.id
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.id = f.id
|
||
WHERE f.tenant_id <> 0 OR f.fee_code <> s.fee_code
|
||
OR f.category <> s.category OR f.trade_scope <> s.trade_scope
|
||
OR f.fee_type_name <> s.fee_type_name
|
||
OR f.pickup_payable <> s.pickup_payable OR f.pickup_receivable <> s.pickup_receivable
|
||
OR f.warehouse_payable <> s.warehouse_payable OR f.warehouse_receivable <> s.warehouse_receivable
|
||
OR f.allocation_fee <> s.allocation_fee OR NOT (f.description <=> s.description)
|
||
) THEN
|
||
SELECT 'FIXED_ID_CONFLICT' AS conflict_type, f.id, f.tenant_id, f.fee_code,
|
||
f.trade_scope, f.fee_type_name
|
||
FROM fl_fee_item f JOIN tmp_fee_item_seed s ON s.id = f.id
|
||
WHERE f.tenant_id <> 0 OR f.fee_code <> s.fee_code
|
||
OR f.category <> s.category OR f.trade_scope <> s.trade_scope
|
||
OR f.fee_type_name <> s.fee_type_name
|
||
OR f.pickup_payable <> s.pickup_payable OR f.pickup_receivable <> s.pickup_receivable
|
||
OR f.warehouse_payable <> s.warehouse_payable OR f.warehouse_receivable <> s.warehouse_receivable
|
||
OR f.allocation_fee <> s.allocation_fee OR NOT (f.description <=> s.description);
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:固定主键已被不同费用项目占用';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT f.id
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.fee_code = f.fee_code
|
||
WHERE f.tenant_id = 0
|
||
AND (f.id <> s.id OR f.category <> s.category OR f.trade_scope <> s.trade_scope
|
||
OR f.fee_type_name <> s.fee_type_name OR f.pickup_payable <> s.pickup_payable
|
||
OR f.pickup_receivable <> s.pickup_receivable OR f.warehouse_payable <> s.warehouse_payable
|
||
OR f.warehouse_receivable <> s.warehouse_receivable OR f.allocation_fee <> s.allocation_fee
|
||
OR NOT (f.description <=> s.description) OR f.status <> 'ACTIVE' OR f.deleted <> 0)
|
||
) THEN
|
||
SELECT 'PUBLIC_CODE_CONFLICT' AS conflict_type, f.id, f.tenant_id, f.fee_code,
|
||
f.trade_scope, f.fee_type_name, s.id AS expected_id, s.fee_type_name AS expected_name
|
||
FROM fl_fee_item f JOIN tmp_fee_item_seed s ON s.fee_code = f.fee_code
|
||
WHERE f.tenant_id = 0
|
||
AND (f.id <> s.id OR f.category <> s.category OR f.trade_scope <> s.trade_scope
|
||
OR f.fee_type_name <> s.fee_type_name OR f.pickup_payable <> s.pickup_payable
|
||
OR f.pickup_receivable <> s.pickup_receivable OR f.warehouse_payable <> s.warehouse_payable
|
||
OR f.warehouse_receivable <> s.warehouse_receivable OR f.allocation_fee <> s.allocation_fee
|
||
OR NOT (f.description <=> s.description) OR f.status <> 'ACTIVE' OR f.deleted <> 0);
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:平台费用编码存在语义冲突';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT f.id
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.trade_scope = f.trade_scope AND s.fee_type_name = f.fee_type_name
|
||
WHERE f.tenant_id = 0 AND (f.fee_code <> s.fee_code OR f.category <> s.category
|
||
OR f.pickup_payable <> s.pickup_payable OR f.pickup_receivable <> s.pickup_receivable
|
||
OR f.warehouse_payable <> s.warehouse_payable OR f.warehouse_receivable <> s.warehouse_receivable
|
||
OR f.allocation_fee <> s.allocation_fee OR NOT (f.description <=> s.description))
|
||
) THEN
|
||
SELECT 'PUBLIC_SEMANTIC_CONFLICT' AS conflict_type, f.id, f.tenant_id, f.fee_code,
|
||
f.trade_scope, f.fee_type_name, s.fee_code AS expected_code
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.trade_scope = f.trade_scope AND s.fee_type_name = f.fee_type_name
|
||
WHERE f.tenant_id = 0 AND (f.fee_code <> s.fee_code OR f.category <> s.category
|
||
OR f.pickup_payable <> s.pickup_payable OR f.pickup_receivable <> s.pickup_receivable
|
||
OR f.warehouse_payable <> s.warehouse_payable OR f.warehouse_receivable <> s.warehouse_receivable
|
||
OR f.allocation_fee <> s.allocation_fee OR NOT (f.description <=> s.description));
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:平台费用语义键存在编码或字段冲突';
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM fl_fee_item WHERE tenant_id = 0 AND (fee_code IS NULL OR TRIM(fee_code) = '')) THEN
|
||
SELECT 'PUBLIC_EMPTY_CODE' AS conflict_type, id, tenant_id, fee_code, trade_scope, fee_type_name
|
||
FROM fl_fee_item
|
||
WHERE tenant_id = 0 AND (fee_code IS NULL OR TRIM(fee_code) = '');
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:平台既有费用项目存在空编码';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT f.id
|
||
FROM fl_fee_item f
|
||
LEFT JOIN tmp_fee_item_seed matched_seed
|
||
ON matched_seed.fee_code = NULLIF(TRIM(f.fee_code), '')
|
||
OR (matched_seed.trade_scope = f.trade_scope
|
||
AND matched_seed.fee_type_name = f.fee_type_name)
|
||
WHERE f.tenant_id > 0 AND matched_seed.seed_no IS NULL
|
||
) THEN
|
||
SELECT 'UNMAPPED_CUSTOMER_ITEM' AS conflict_type, f.id, f.tenant_id, f.fee_code,
|
||
f.trade_scope, f.fee_type_name
|
||
FROM fl_fee_item f
|
||
LEFT JOIN tmp_fee_item_seed matched_seed
|
||
ON matched_seed.fee_code = NULLIF(TRIM(f.fee_code), '')
|
||
OR (matched_seed.trade_scope = f.trade_scope
|
||
AND matched_seed.fee_type_name = f.fee_type_name)
|
||
WHERE f.tenant_id > 0 AND matched_seed.seed_no IS NULL;
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:存在无法匹配公共种子的客户费用项目';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT f.id
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.fee_code = NULLIF(TRIM(f.fee_code), '')
|
||
WHERE f.tenant_id > 0 AND (f.category <> s.category OR f.trade_scope <> s.trade_scope
|
||
OR f.fee_type_name <> s.fee_type_name OR f.pickup_payable <> s.pickup_payable
|
||
OR f.pickup_receivable <> s.pickup_receivable OR f.warehouse_payable <> s.warehouse_payable
|
||
OR f.warehouse_receivable <> s.warehouse_receivable OR f.allocation_fee <> s.allocation_fee
|
||
OR NOT (f.description <=> s.description))
|
||
) THEN
|
||
SELECT 'CUSTOMER_CODE_CONFLICT' AS conflict_type, f.id, f.tenant_id, f.fee_code,
|
||
f.trade_scope, f.fee_type_name, s.fee_type_name AS expected_name
|
||
FROM fl_fee_item f JOIN tmp_fee_item_seed s ON s.fee_code = NULLIF(TRIM(f.fee_code), '')
|
||
WHERE f.tenant_id > 0 AND (f.category <> s.category OR f.trade_scope <> s.trade_scope
|
||
OR f.fee_type_name <> s.fee_type_name OR f.pickup_payable <> s.pickup_payable
|
||
OR f.pickup_receivable <> s.pickup_receivable OR f.warehouse_payable <> s.warehouse_payable
|
||
OR f.warehouse_receivable <> s.warehouse_receivable OR f.allocation_fee <> s.allocation_fee
|
||
OR NOT (f.description <=> s.description));
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:客户费用编码存在语义冲突';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT f.id
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.trade_scope = f.trade_scope AND s.fee_type_name = f.fee_type_name
|
||
WHERE f.tenant_id > 0 AND (NULLIF(TRIM(f.fee_code), '') IS NOT NULL
|
||
AND f.fee_code <> s.fee_code AND f.fee_code <> CONCAT('MANUAL_', f.id)
|
||
OR f.category <> s.category OR f.pickup_payable <> s.pickup_payable
|
||
OR f.pickup_receivable <> s.pickup_receivable OR f.warehouse_payable <> s.warehouse_payable
|
||
OR f.warehouse_receivable <> s.warehouse_receivable OR f.allocation_fee <> s.allocation_fee
|
||
OR NOT (f.description <=> s.description))
|
||
) THEN
|
||
SELECT 'CUSTOMER_SEMANTIC_CONFLICT' AS conflict_type, f.id, f.tenant_id, f.fee_code,
|
||
f.trade_scope, f.fee_type_name, s.fee_code AS expected_code
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s ON s.trade_scope = f.trade_scope AND s.fee_type_name = f.fee_type_name
|
||
WHERE f.tenant_id > 0 AND (NULLIF(TRIM(f.fee_code), '') IS NOT NULL
|
||
AND f.fee_code <> s.fee_code AND f.fee_code <> CONCAT('MANUAL_', f.id)
|
||
OR f.category <> s.category OR f.pickup_payable <> s.pickup_payable
|
||
OR f.pickup_receivable <> s.pickup_receivable OR f.warehouse_payable <> s.warehouse_payable
|
||
OR f.warehouse_receivable <> s.warehouse_receivable OR f.allocation_fee <> s.allocation_fee
|
||
OR NOT (f.description <=> s.description));
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:客户费用语义键存在编码或字段冲突';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT d.id
|
||
FROM fl_inbound_fee_detail d
|
||
JOIN fl_fee_item f ON f.id = d.fee_item_id
|
||
WHERE d.tenant_id = 0 OR (f.tenant_id > 0 AND f.tenant_id <> d.tenant_id)
|
||
) THEN
|
||
SELECT 'CROSS_CUSTOMER_DETAIL_REFERENCE' AS conflict_type, d.id AS fee_detail_id,
|
||
d.tenant_id AS detail_customer_id, d.fee_item_id, f.tenant_id AS fee_item_scope
|
||
FROM fl_inbound_fee_detail d JOIN fl_fee_item f ON f.id = d.fee_item_id
|
||
WHERE d.tenant_id = 0 OR (f.tenant_id > 0 AND f.tenant_id <> d.tenant_id);
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期费用项目迁移失败:发现跨客户或平台作用域费用明细引用';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_publish_assert_fee_item_preflight();
|
||
DROP PROCEDURE IF EXISTS unified_publish_assert_fee_item_preflight;
|
||
|
||
DROP PROCEDURE IF EXISTS unified_publish_migrate_fee_items;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_publish_migrate_fee_items()
|
||
BEGIN
|
||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||
BEGIN
|
||
ROLLBACK;
|
||
RESIGNAL;
|
||
END;
|
||
START TRANSACTION;
|
||
INSERT INTO fl_fee_item (id, tenant_id, fee_code, category, trade_scope, fee_type_name,
|
||
pickup_payable, pickup_receivable, warehouse_payable, warehouse_receivable, allocation_fee,
|
||
description, status, created_by, created_by_name, created_at, updated_by, updated_by_name, updated_at)
|
||
SELECT s.id, 0, s.fee_code, s.category, s.trade_scope, s.fee_type_name,
|
||
s.pickup_payable, s.pickup_receivable, s.warehouse_payable, s.warehouse_receivable, s.allocation_fee,
|
||
s.description, 'ACTIVE', s.created_by, s.created_by_name, s.created_at,
|
||
s.updated_by, s.updated_by_name, s.updated_at
|
||
FROM tmp_fee_item_seed s
|
||
WHERE NOT EXISTS (SELECT 1 FROM fl_fee_item f WHERE f.id = s.id);
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_fee_item_map;
|
||
CREATE TEMPORARY TABLE tmp_fee_item_map (
|
||
old_id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
|
||
public_id BIGINT UNSIGNED NOT NULL
|
||
) ENGINE=MEMORY;
|
||
INSERT INTO tmp_fee_item_map (old_id, public_id)
|
||
SELECT f.id, s.id
|
||
FROM fl_fee_item f
|
||
JOIN tmp_fee_item_seed s
|
||
ON (s.fee_code = NULLIF(TRIM(f.fee_code), '')
|
||
OR (s.trade_scope = f.trade_scope AND s.fee_type_name = f.fee_type_name))
|
||
WHERE f.tenant_id > 0;
|
||
UPDATE fl_inbound_fee_detail d
|
||
JOIN tmp_fee_item_map m ON m.old_id = d.fee_item_id
|
||
SET d.fee_item_id = m.public_id;
|
||
DELETE f FROM fl_fee_item f JOIN tmp_fee_item_map m ON m.old_id = f.id;
|
||
COMMIT;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_publish_migrate_fee_items();
|
||
DROP PROCEDURE IF EXISTS unified_publish_migrate_fee_items;
|
||
|
||
SET @fee_code_ddl = CASE
|
||
WHEN
|
||
(SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_fee_item' AND index_name = 'uk_fee_item_code'
|
||
AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'fee_code') = 1
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_fee_item' AND index_name = 'uk_fee_item_code') = 1
|
||
THEN 'SELECT 1'
|
||
WHEN EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_fee_item' AND index_name = 'uk_fee_item_code')
|
||
THEN 'ALTER TABLE fl_fee_item DROP INDEX uk_fee_item_code, ADD UNIQUE KEY uk_fee_item_code (fee_code)'
|
||
ELSE 'ALTER TABLE fl_fee_item ADD UNIQUE KEY uk_fee_item_code (fee_code)'
|
||
END;
|
||
PREPARE statement FROM @fee_code_ddl;
|
||
EXECUTE statement;
|
||
DEALLOCATE PREPARE statement;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_fee_item_map;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_fee_item_seed;
|
||
SET @fee_code_ddl = IF(
|
||
(SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_fee_item'
|
||
AND column_name = 'fee_code' AND is_nullable = 'NO') = 1,
|
||
'SELECT 1',
|
||
'ALTER TABLE fl_fee_item MODIFY COLUMN fee_code VARCHAR(50) NOT NULL COMMENT ''稳定费用编码;平台公共主数据固定编码'' AFTER trade_scope'
|
||
);
|
||
PREPARE statement FROM @fee_code_ddl;
|
||
EXECUTE statement;
|
||
DEALLOCATE PREPARE statement;
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'source_type',
|
||
"VARCHAR(32) NOT NULL DEFAULT 'MANUAL' COMMENT 'MANUAL或FBA_PRICING' AFTER fee_item_id");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'source_fee_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '来源计费快照ID' AFTER source_type");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'source_fee_version',
|
||
"INT UNSIGNED NULL COMMENT '来源计费快照版本' AFTER source_fee_id");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'source_fee_no',
|
||
"VARCHAR(40) NULL COMMENT '来源费用单号' AFTER source_fee_version");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'customer_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '客户ID快照' AFTER business_date");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'customer_name',
|
||
"VARCHAR(128) NULL COMMENT '客户公司名称快照' AFTER customer_id");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'invoice_title',
|
||
"VARCHAR(256) NULL COMMENT '发票抬头快照' AFTER customer_name");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'chargeable_quantity',
|
||
"DECIMAL(18,4) NULL COMMENT '本费用最终计费量' AFTER chargeable_volume_cbm");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'charge_unit',
|
||
"VARCHAR(16) NULL COMMENT 'KG或CBM' AFTER chargeable_quantity");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'publication_status',
|
||
"VARCHAR(16) NOT NULL DEFAULT 'DRAFT' COMMENT 'DRAFT、PUBLISHED、SUPERSEDED' AFTER review_reason");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'published_by',
|
||
"BIGINT NULL COMMENT '发布人ID' AFTER publication_status");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'published_by_name',
|
||
"VARCHAR(64) NULL COMMENT '发布人名称快照' AFTER published_by");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'published_at',
|
||
"DATETIME(3) NULL COMMENT '发布时间' AFTER published_by_name");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'inbounded_at',
|
||
"DATETIME(3) NULL COMMENT '入库完成时间快照' AFTER published_at");
|
||
CALL unified_publish_fee_add_column('fl_inbound_fee_detail', 'settlement_included',
|
||
"TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否参与结算' AFTER inbounded_at");
|
||
DROP PROCEDURE IF EXISTS unified_publish_fee_add_column;
|
||
|
||
DROP PROCEDURE IF EXISTS unified_publish_fee_add_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_publish_fee_add_index(
|
||
IN target_table VARCHAR(64), IN target_index VARCHAR(64), IN index_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = target_table AND index_name = target_index
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', target_table, '` ADD ', index_definition);
|
||
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_publish_fee_add_index('fl_fee_item', 'uk_fee_item_code',
|
||
'UNIQUE KEY `uk_fee_item_code` (`fee_code`)');
|
||
CALL unified_publish_fee_add_index('fl_inbound_fee_detail', 'uk_inbound_fee_source',
|
||
'UNIQUE KEY `uk_inbound_fee_source` (`tenant_id`, `source_type`, `source_fee_id`)');
|
||
CALL unified_publish_fee_add_index('fl_inbound_fee_detail', 'idx_inbound_fee_published',
|
||
'KEY `idx_inbound_fee_published` (`tenant_id`, `inbound_id`, `publication_status`, `source_fee_version`)');
|
||
DROP PROCEDURE IF EXISTS unified_publish_fee_add_index;
|
||
|
||
-- ============================================================================
|
||
-- 2026-08-06:三期分柜、预装、拣货、装柜和派送当前基线
|
||
-- ============================================================================
|
||
USE baiyue_firstleg;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_preload_plan (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '预装清单ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单/订单ID',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/CONFIRMING/CONFIRMED',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
|
||
items_json JSON NOT NULL COMMENT '可编辑分柜明细快照',
|
||
remark VARCHAR(1000) NULL COMMENT '预装备注',
|
||
created_by BIGINT UNSIGNED NOT NULL,
|
||
created_by_name VARCHAR(64) NULL,
|
||
updated_by BIGINT UNSIGNED NOT NULL,
|
||
updated_by_name VARCHAR(64) 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_preload_tenant_inbound (tenant_id, inbound_id),
|
||
KEY idx_preload_status (tenant_id, status, updated_at),
|
||
CONSTRAINT fk_preload_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单分柜预装清单';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_split_operation (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '幂等记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||
operator_id BIGINT UNSIGNED NOT NULL,
|
||
operation VARCHAR(40) NOT NULL,
|
||
idempotency_key VARCHAR(128) NOT NULL,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'PROCESSING' COMMENT 'PROCESSING/SUCCESS',
|
||
result_json JSON NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_split_operation (tenant_id, inbound_id, operator_id, operation, idempotency_key),
|
||
KEY idx_split_operation_time (tenant_id, inbound_id, created_at),
|
||
CONSTRAINT fk_split_operation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='分柜操作幂等与审计索引';
|
||
|
||
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 NOT NULL COMMENT 'File 服务逻辑文件ID',
|
||
file_name VARCHAR(255) NULL,
|
||
seal_no VARCHAR(64) NULL,
|
||
remark VARCHAR(1000) NULL,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/VOID',
|
||
void_reason VARCHAR(500) NULL,
|
||
voided_by BIGINT UNSIGNED NULL,
|
||
voided_by_name VARCHAR(64) NULL,
|
||
voided_at DATETIME(3) NULL,
|
||
version INT NOT NULL DEFAULT 0,
|
||
created_by BIGINT UNSIGNED NOT NULL,
|
||
created_by_name VARCHAR(64) NULL,
|
||
updated_by BIGINT UNSIGNED NOT NULL,
|
||
updated_by_name VARCHAR(64) 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),
|
||
KEY idx_loading_evidence_type (tenant_id, inbound_id, evidence_type, status),
|
||
KEY idx_loading_evidence_seal (tenant_id, seal_no, status),
|
||
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='订单装柜四步照片和铅封证据';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_operation (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||
operator_id BIGINT UNSIGNED NOT NULL,
|
||
operation VARCHAR(48) 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_loading_operation (tenant_id, inbound_id, operator_id, operation, idempotency_key),
|
||
KEY idx_loading_operation_time (tenant_id, inbound_id, created_at),
|
||
CONSTRAINT fk_loading_operation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='装柜与派送写操作幂等记录';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_loading_pick_task (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||
status VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/IN_PROGRESS/COMPLETED',
|
||
version INT NOT NULL DEFAULT 0,
|
||
expected_count INT UNSIGNED NULL,
|
||
picked_count INT UNSIGNED NULL,
|
||
remark VARCHAR(1000) NULL,
|
||
updated_by BIGINT UNSIGNED NOT NULL,
|
||
updated_by_name VARCHAR(64) 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_loading_pick_tenant_inbound (tenant_id, inbound_id),
|
||
KEY idx_loading_pick_status (tenant_id, status, updated_at),
|
||
CONSTRAINT fk_loading_pick_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='PDA拣货任务及结果';
|
||
|
||
USE baiyue_iam;
|
||
|
||
DROP PROCEDURE IF EXISTS unified_relocate_inbound_loading_permissions;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_relocate_inbound_loading_permissions()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE id BETWEEN 5102121 AND 5102129
|
||
AND permission_code <> CASE id
|
||
WHEN 5102121 THEN 'firstleg:inbound:split'
|
||
WHEN 5102122 THEN 'firstleg:inbound:preload'
|
||
WHEN 5102123 THEN 'firstleg:inbound:preload-view'
|
||
WHEN 5102124 THEN 'platform:firstleg:inbound:loading:edit'
|
||
WHEN 5102125 THEN 'platform:firstleg:inbound:loading:view'
|
||
WHEN 5102126 THEN 'platform:firstleg:inbound:loading:complete'
|
||
WHEN 5102127 THEN 'platform:firstleg:inbound:delivery:complete'
|
||
WHEN 5102128 THEN 'platform:firstleg:inbound:picking:edit'
|
||
WHEN 5102129 THEN 'platform:firstleg:inbound:picking:view'
|
||
END
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '三期权限迁移失败:5102121-5102129 已被其他权限占用';
|
||
END IF;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_phase3_inbound_role_grant;
|
||
CREATE TEMPORARY TABLE tmp_phase3_inbound_role_grant (
|
||
role_id BIGINT UNSIGNED NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
permission_code VARCHAR(128) NOT NULL,
|
||
PRIMARY KEY (role_id, tenant_id, permission_code)
|
||
) ENGINE=MEMORY;
|
||
INSERT IGNORE INTO tmp_phase3_inbound_role_grant (role_id, tenant_id, permission_code)
|
||
SELECT role_permission.role_id, role_permission.tenant_id, permission.permission_code
|
||
FROM iam_role_permission role_permission
|
||
JOIN iam_permission permission ON permission.id = role_permission.permission_id
|
||
WHERE permission.permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
DELETE role_permission FROM iam_role_permission role_permission
|
||
JOIN iam_permission permission ON permission.id = role_permission.permission_id
|
||
WHERE permission.permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
DELETE FROM iam_permission WHERE permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_relocate_inbound_loading_permissions();
|
||
DROP PROCEDURE unified_relocate_inbound_loading_permissions;
|
||
|
||
-- 如果旧入库权限占用了 5102091-5102099,上面的语义迁移已释放这些 ID;
|
||
-- 此处重新写入智能装柜权限,修复此前 ON DUPLICATE 只改元数据、不改 code 的污染。
|
||
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
|
||
(5102091, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:view', '查看货柜', NULL, 'GET', 10, 'ACTIVE', 1, 0),
|
||
(5102092, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:create', '建立货柜', NULL, 'POST', 20, 'ACTIVE', 1, 0),
|
||
(5102093, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:edit', '编辑货柜', NULL, 'PUT', 30, 'ACTIVE', 1, 0),
|
||
(5102094, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:smart-loading:list', '智能装柜', '/first-leg/smart-loading', 'GET', 50, 'ACTIVE', 1, 0),
|
||
(5102096, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:create', '创建智能装柜计划', NULL, 'POST', 20, 'ACTIVE', 1, 0),
|
||
(5102097, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:assign', '分配装柜明细', NULL, 'POST', 30, 'ACTIVE', 1, 0),
|
||
(5102098, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:delete', '删除装柜明细', NULL, 'DELETE', 40, 'ACTIVE', 1, 0),
|
||
(5102099, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:push', '推送预装', NULL, 'POST', 50, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = VALUES(http_method), sort_no = VALUES(sort_no), status = 'ACTIVE',
|
||
built_in = 1, deleted = 0;
|
||
|
||
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
|
||
(5102121, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'firstleg:inbound:split', '智能分柜', NULL, NULL, 140, 'ACTIVE', 1, 0),
|
||
(5102122, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'firstleg:inbound:preload', '编辑并确认预装清单', NULL, NULL, 150, 'ACTIVE', 1, 0),
|
||
(5102123, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'firstleg:inbound:preload-view', '查看预装清单', NULL, NULL, 160, 'ACTIVE', 1, 0),
|
||
(5102124, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:loading:edit', '维护装柜证据', NULL, NULL, 170, 'ACTIVE', 1, 0),
|
||
(5102125, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:loading:view', '查看装柜证据', NULL, NULL, 180, 'ACTIVE', 1, 0),
|
||
(5102126, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:loading:complete', '完成装柜', NULL, NULL, 190, 'ACTIVE', 1, 0),
|
||
(5102127, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:delivery:complete', '完成派送', NULL, NULL, 200, 'ACTIVE', 1, 0),
|
||
(5102128, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:picking:edit', '执行拣货', NULL, NULL, 210, 'ACTIVE', 1, 0),
|
||
(5102129, 0, 'PLATFORM', 'BUTTON', 5102030,
|
||
'platform:firstleg:inbound:picking:view', '查看拣货任务', NULL, NULL, 220, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = VALUES(http_method), sort_no = VALUES(sort_no),
|
||
status = 'ACTIVE', built_in = 1, deleted = 0;
|
||
|
||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT role.id, permission.id, role.tenant_id
|
||
FROM iam_role role
|
||
JOIN iam_permission permission ON permission.tenant_id = 0 AND permission.app_code = 'PLATFORM'
|
||
AND permission.permission_code IN (
|
||
'firstleg:inbound:split',
|
||
'firstleg:inbound:preload',
|
||
'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit',
|
||
'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete',
|
||
'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit',
|
||
'platform:firstleg:inbound:picking:view'
|
||
)
|
||
WHERE role.tenant_id = 0 AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR')
|
||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||
|
||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT grant_row.role_id, permission.id, grant_row.tenant_id
|
||
FROM tmp_phase3_inbound_role_grant grant_row
|
||
JOIN iam_permission permission ON permission.permission_code = grant_row.permission_code
|
||
AND permission.tenant_id = 0 AND permission.app_code = 'PLATFORM';
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_phase3_inbound_role_grant;
|
||
|
||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT role.id, permission.id, role.tenant_id
|
||
FROM iam_role role
|
||
JOIN iam_permission permission ON permission.tenant_id = 0
|
||
AND permission.permission_code IN (
|
||
'platform:firstleg:container:view', 'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:assign',
|
||
'platform:firstleg:smart-loading:delete', 'platform:firstleg:smart-loading:push'
|
||
)
|
||
WHERE role.tenant_id = 0 AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR');
|
||
|
||
-- 污染存量库必须先完成旧入库权限搬迁和两类授权恢复,再校验智能装柜权限。
|
||
USE baiyue_firstleg;
|
||
CALL assert_phase3_smart_loading_schema();
|
||
DROP PROCEDURE IF EXISTS assert_phase3_smart_loading_schema;
|
||
|
||
-- ============================================================================
|
||
-- 兼容已经执行过旧单行配置模型的开发/测试库。仅精确识别旧结构时丢弃旧值并重建;
|
||
-- 已是四箱规结构时不做 DDL,避免破坏 Admin 已编辑的数据。
|
||
DROP PROCEDURE IF EXISTS unified_upgrade_legacy_customer_dimension_config;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE unified_upgrade_legacy_customer_dimension_config()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config') = 1
|
||
AND (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config'
|
||
AND column_name = 'box_type') = 0 THEN
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config'
|
||
AND column_name IN ('id', 'tenant_id', 'edge_addition_cm', 'remark', 'initialization_key',
|
||
'created_by', 'created_at', 'updated_by', 'updated_at', 'deleted', 'version')) <> 11
|
||
OR (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config') <> 11
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config'
|
||
AND index_name = 'uk_customer_dimension_config_tenant'
|
||
AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'tenant_id') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户尺寸配置表不是可识别的旧单行结构';
|
||
END IF;
|
||
DROP TABLE baiyue_firstleg.fl_customer_dimension_config;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL unified_upgrade_legacy_customer_dimension_config();
|
||
DROP PROCEDURE IF EXISTS unified_upgrade_legacy_customer_dimension_config;
|
||
|
||
CREATE TABLE IF NOT EXISTS baiyue_firstleg.fl_customer_dimension_config (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '配置ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
box_type VARCHAR(24) NOT NULL COMMENT '固定箱规类型:SMALL/MEDIUM/LARGE/EXTRA_LARGE',
|
||
inner_length_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内长(厘米)',
|
||
inner_width_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内宽(厘米)',
|
||
inner_height_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内高(厘米)',
|
||
edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '每个尺寸方向的单侧加宽厘米数',
|
||
remark VARCHAR(500) NULL COMMENT '备注',
|
||
initialization_key VARCHAR(128) NOT NULL COMMENT '创建流程幂等键及补偿归属',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_customer_dimension_config_tenant_box (tenant_id, box_type),
|
||
KEY idx_customer_dimension_config_initialization (tenant_id, initialization_key),
|
||
CONSTRAINT chk_customer_dimension_box_type
|
||
CHECK (box_type IN ('SMALL', 'MEDIUM', 'LARGE', 'EXTRA_LARGE')),
|
||
CONSTRAINT chk_customer_dimension_inner_size
|
||
CHECK (inner_length_cm >= 0.001 AND inner_width_cm >= 0.001 AND inner_height_cm >= 0.001),
|
||
CONSTRAINT chk_customer_dimension_edge_addition
|
||
CHECK (edge_addition_cm >= 0.001 AND edge_addition_cm <= 100.000)
|
||
) ENGINE=InnoDB COMMENT='客户固定四箱规尺寸配置';
|
||
|
||
-- 新结构首次部署或旧结构受控重建后,为所有现有企业客户补齐缺失箱规。
|
||
-- 复跑命中 (tenant_id, box_type) 唯一键时只做空更新,不覆盖尺寸、备注、版本或审计字段。
|
||
INSERT INTO baiyue_firstleg.fl_customer_dimension_config (
|
||
id, tenant_id, box_type, inner_length_cm, inner_width_cm, inner_height_cm,
|
||
edge_addition_cm, remark, initialization_key, created_by, created_at,
|
||
updated_by, updated_at, deleted, version
|
||
)
|
||
SELECT UUID_SHORT(), tenant.tenant_id, defaults.box_type,
|
||
defaults.inner_length_cm, defaults.inner_width_cm, defaults.inner_height_cm,
|
||
2.000, defaults.remark,
|
||
CONCAT('unified-four-box-default-v1:', tenant.tenant_id),
|
||
COALESCE(tenant.created_by, 0), CURRENT_TIMESTAMP(3),
|
||
COALESCE(tenant.created_by, 0), CURRENT_TIMESTAMP(3), 0, 0
|
||
FROM (
|
||
SELECT id AS tenant_id, created_by
|
||
FROM baiyue_iam.iam_tenant
|
||
WHERE tenant_type = 'MERCHANT' AND deleted = 0 AND id > 0
|
||
) tenant
|
||
CROSS JOIN (
|
||
SELECT 'SMALL' AS box_type, 30.000 AS inner_length_cm, 20.000 AS inner_width_cm,
|
||
20.000 AS inner_height_cm, '标准小箱' AS remark
|
||
UNION ALL
|
||
SELECT 'MEDIUM', 40.000, 30.000, 30.000, '标准中箱'
|
||
UNION ALL
|
||
SELECT 'LARGE', 50.000, 40.000, 40.000, '标准大箱'
|
||
UNION ALL
|
||
SELECT 'EXTRA_LARGE', 60.000, 50.000, 50.000, '特大箱'
|
||
) defaults
|
||
WHERE TRUE
|
||
ON DUPLICATE KEY UPDATE id = id;
|
||
|
||
-- ============================================================================
|
||
-- 来源:order/src/main/resources/db/phase3-order-parent-child-schema.sql
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_order;
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_order_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_order_add_column(IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_definition TEXT)
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name=p_table AND column_name=p_column) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_order_add_column('ord_order','order_role',"VARCHAR(12) NOT NULL DEFAULT 'MASTER' COMMENT '订单层级:MASTER、CHILD'");
|
||
CALL phase3_order_add_column('ord_order','parent_order_id','BIGINT UNSIGNED NULL COMMENT ''直属父订单ID;禁止子订单嵌套''');
|
||
CALL phase3_order_add_column('ord_order','root_order_id','BIGINT UNSIGNED NULL COMMENT ''根主订单ID''');
|
||
CALL phase3_order_add_column('ord_order','split_sequence','INT UNSIGNED NULL COMMENT ''同一父订单下的同级顺序''');
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_order_add_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_order_add_index(IN p_table VARCHAR(64), IN p_name VARCHAR(64), IN p_definition TEXT)
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name=p_table AND index_name=p_name) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `',p_table,'` ADD ',p_definition);
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_order_add_index('ord_order','uk_order_parent_sequence',
|
||
'UNIQUE KEY `uk_order_parent_sequence` (`tenant_id`,`parent_order_id`,`split_sequence`)');
|
||
CALL phase3_order_add_index('ord_order','idx_order_root_role',
|
||
'KEY `idx_order_root_role` (`tenant_id`,`root_order_id`,`order_role`,`created_at`,`id`)');
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_order_parent_fk;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_order_parent_fk()
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||
AND table_name='ord_order' AND constraint_name='fk_order_parent') THEN
|
||
ALTER TABLE ord_order DROP FOREIGN KEY fk_order_parent;
|
||
END IF;
|
||
ALTER TABLE ord_order ADD CONSTRAINT fk_order_parent FOREIGN KEY (parent_order_id) REFERENCES ord_order(id);
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_order_parent_fk();
|
||
DROP PROCEDURE IF EXISTS phase3_order_parent_fk;
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_fulfillment_split (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
parent_order_id BIGINT UNSIGNED NOT NULL,
|
||
source_order_id BIGINT UNSIGNED NOT NULL,
|
||
child_order_id BIGINT UNSIGNED NOT NULL,
|
||
split_role VARCHAR(12) NOT NULL COMMENT 'LOADED或REMAINDER',
|
||
loading_plan_id BIGINT UNSIGNED NULL,
|
||
container_booking_id BIGINT UNSIGNED NULL,
|
||
source_package_count INT UNSIGNED NOT NULL,
|
||
loaded_package_count INT UNSIGNED NOT NULL,
|
||
remaining_package_count INT UNSIGNED NOT NULL,
|
||
source_detail_json JSON NULL,
|
||
operation_id VARCHAR(64) NOT NULL,
|
||
idempotency_key VARCHAR(128) NOT NULL,
|
||
request_hash CHAR(64) NOT NULL,
|
||
expected_parent_version INT NOT NULL,
|
||
created_by BIGINT UNSIGNED NOT NULL,
|
||
created_by_name VARCHAR(64) NULL,
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
version INT NOT NULL DEFAULT 0,
|
||
PRIMARY KEY(id),
|
||
UNIQUE KEY uk_order_split_operation (tenant_id,operation_id,split_role),
|
||
UNIQUE KEY uk_order_split_idempotency (tenant_id,parent_order_id,idempotency_key,split_role),
|
||
UNIQUE KEY uk_order_split_child (tenant_id,child_order_id),
|
||
KEY idx_order_split_parent (tenant_id,parent_order_id,created_at,id),
|
||
KEY idx_order_split_source (tenant_id,source_order_id,split_role),
|
||
KEY idx_order_split_container (tenant_id,container_booking_id,created_at,id),
|
||
CONSTRAINT fk_order_split_parent FOREIGN KEY(parent_order_id) REFERENCES ord_order(id),
|
||
CONSTRAINT fk_order_split_child FOREIGN KEY(child_order_id) REFERENCES ord_order(id),
|
||
CONSTRAINT chk_order_split_quantity CHECK (source_package_count > 0 AND loaded_package_count > 0
|
||
AND loaded_package_count + remaining_package_count = source_package_count)
|
||
) ENGINE=InnoDB COMMENT='父订单→子订单→柜次数量台账';
|
||
|
||
DROP TRIGGER IF EXISTS trg_ord_order_parent_insert;
|
||
DROP TRIGGER IF EXISTS trg_ord_order_parent_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_ord_order_parent_insert BEFORE INSERT ON ord_order FOR EACH ROW
|
||
BEGIN
|
||
IF NEW.order_role='MASTER' THEN
|
||
SET NEW.parent_order_id=NULL;
|
||
IF NEW.root_order_id IS NULL THEN SET NEW.root_order_id=NEW.id; END IF;
|
||
ELSEIF NEW.order_role='CHILD' THEN
|
||
IF NEW.parent_order_id IS NULL OR NEW.root_order_id IS NULL OR NEW.split_sequence IS NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='子订单必须有父订单、根订单和顺序号';
|
||
END IF;
|
||
IF NEW.root_order_id<>NEW.parent_order_id OR EXISTS(
|
||
SELECT 1 FROM ord_order p WHERE p.id=NEW.parent_order_id AND p.order_role<>'MASTER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='子订单只允许直接挂在主订单下';
|
||
END IF;
|
||
IF NOT EXISTS(SELECT 1 FROM ord_order p WHERE p.id=NEW.parent_order_id AND p.tenant_id=NEW.tenant_id AND p.deleted=0) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='父订单不存在或客户归属不一致';
|
||
END IF;
|
||
ELSE
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单层级非法';
|
||
END IF;
|
||
END$$
|
||
CREATE TRIGGER trg_ord_order_parent_update BEFORE UPDATE ON ord_order FOR EACH ROW
|
||
BEGIN
|
||
IF NEW.order_role<>OLD.order_role OR NOT (NEW.parent_order_id<=>OLD.parent_order_id)
|
||
OR NOT (NEW.root_order_id<=>OLD.root_order_id) OR NOT (NEW.split_sequence<=>OLD.split_sequence) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单父子关系不可修改';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase3_order_add_column('ord_order_fulfillment_split','split_role',"VARCHAR(12) NOT NULL DEFAULT 'LOADED' COMMENT 'LOADED或REMAINDER'");
|
||
CALL phase3_order_add_column('ord_order_fulfillment_split','source_order_id','BIGINT UNSIGNED NULL COMMENT ''实际被消费的来源订单ID''');
|
||
CALL phase3_order_add_column('ord_order_fulfillment_split','request_hash',"CHAR(64) NULL COMMENT '请求内容SHA-256;存量记录允许为空'");
|
||
-- 旧版台账没有来源字段;当时只允许从根主订单拆分,因此可无歧义回填为父订单。
|
||
UPDATE ord_order_fulfillment_split
|
||
SET source_order_id = parent_order_id
|
||
WHERE source_order_id IS NULL;
|
||
-- 旧记录没有请求哈希。生成稳定哨兵哈希可保持非空约束,并让旧幂等键重放明确冲突。
|
||
UPDATE ord_order_fulfillment_split
|
||
SET request_hash = SHA2(CONCAT('LEGACY:', tenant_id, ':', id), 256)
|
||
WHERE request_hash IS NULL OR request_hash = '';
|
||
ALTER TABLE ord_order_fulfillment_split
|
||
MODIFY source_order_id BIGINT UNSIGNED NOT NULL COMMENT '实际被消费的来源订单ID',
|
||
MODIFY request_hash CHAR(64) NOT NULL COMMENT '请求内容SHA-256';
|
||
CALL phase3_order_add_index('ord_order_fulfillment_split','idx_order_split_source',
|
||
'KEY `idx_order_split_source` (`tenant_id`,`source_order_id`,`split_role`)');
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_order_drop_split_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_order_drop_split_index()
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name='ord_order_fulfillment_split' AND index_name='uk_order_split_operation') THEN
|
||
ALTER TABLE ord_order_fulfillment_split DROP INDEX uk_order_split_operation;
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name='ord_order_fulfillment_split' AND index_name='uk_order_split_idempotency') THEN
|
||
ALTER TABLE ord_order_fulfillment_split DROP INDEX uk_order_split_idempotency;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_order_drop_split_index();
|
||
DROP PROCEDURE IF EXISTS phase3_order_drop_split_index;
|
||
CALL phase3_order_add_index('ord_order_fulfillment_split','uk_order_split_operation',
|
||
'UNIQUE KEY `uk_order_split_operation` (`tenant_id`,`operation_id`,`split_role`)');
|
||
CALL phase3_order_add_index('ord_order_fulfillment_split','uk_order_split_idempotency',
|
||
'UNIQUE KEY `uk_order_split_idempotency` (`tenant_id`,`parent_order_id`,`idempotency_key`,`split_role`)');
|
||
DROP PROCEDURE IF EXISTS phase3_order_add_column;
|
||
DROP PROCEDURE IF EXISTS phase3_order_add_index;
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_order_assert;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_order_assert()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='ord_order' AND column_name IN ('order_role','parent_order_id','root_order_id','split_sequence'))<>4 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单父子字段未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE()
|
||
AND table_name='ord_order_fulfillment_split')<>1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单拆分台账未创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='ord_order_fulfillment_split'
|
||
AND column_name IN ('source_order_id','request_hash') AND is_nullable='NO')<>2 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单拆分来源和请求哈希必须为非空';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||
WHERE table_schema=DATABASE() AND table_name='ord_order_fulfillment_split'
|
||
AND index_name IN ('idx_order_split_source','uk_order_split_operation',
|
||
'uk_order_split_idempotency'))<>3 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单拆分来源或幂等索引未完整创建';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_order_assert();
|
||
DROP PROCEDURE IF EXISTS phase3_order_assert;
|
||
|
||
-- 来源:firstleg/src/main/resources/db/phase3-inbound-parent-child-schema.sql
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
/*
|
||
* 三期主子入仓单结构迁移。
|
||
* 仅建立结构和数据库保护,不自动拆分或回填存量业务数据。
|
||
* 新写入采用“来源单承接实装、仅创建余量子单”;LOADED 子单和 loaded_child_inbound_id
|
||
* 仅为历史双子单读取保留,不删除、不清洗存量记录。
|
||
* tenant_id 继续表示客户隔离键;数量统一使用 package_count。
|
||
*/
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_add_column(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table_name, '` ADD COLUMN `',
|
||
p_column_name, '` ', p_definition);
|
||
PREPARE stmt FROM @ddl;
|
||
EXECUTE stmt;
|
||
DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'inbound_role',
|
||
"VARCHAR(16) NOT NULL DEFAULT 'MASTER' COMMENT '入仓单层级:MASTER主单、CHILD子单' AFTER inbound_code");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'parent_inbound_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '直属主入仓单ID;主单为空' AFTER inbound_role");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'root_inbound_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '根主入仓单ID;存量主单可为空,按自身ID解释' AFTER parent_inbound_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'split_sequence',
|
||
"INT UNSIGNED NULL COMMENT '同一主单下的子单顺序号' AFTER root_inbound_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'split_status',
|
||
"VARCHAR(24) NOT NULL DEFAULT 'NOT_SPLIT' COMMENT '拆分状态:NOT_SPLIT、PARTIALLY_SPLIT、FULLY_SPLIT' AFTER split_sequence");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'package_count',
|
||
"INT UNSIGNED NULL COMMENT '入仓单箱数;三期唯一数量口径' AFTER total_package_count");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'split_purpose',
|
||
"VARCHAR(16) NOT NULL DEFAULT 'MASTER' COMMENT '履约分片用途:MASTER、LOADED、REMAINDER' AFTER package_count");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'master_order_id',
|
||
"BIGINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN inbound_role = 'MASTER' AND deleted = 0 THEN order_id ELSE NULL END) STORED COMMENT '仅用于约束有效主单订单唯一性'");
|
||
|
||
-- 旧实现只生成未装余量子单,可无歧义映射为 REMAINDER。
|
||
UPDATE fl_inbound_order
|
||
SET split_purpose = 'REMAINDER'
|
||
WHERE inbound_role = 'CHILD'
|
||
AND split_purpose = 'MASTER';
|
||
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_order', 'active_remainder_root_id',
|
||
"BIGINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN inbound_role = 'CHILD' AND split_purpose = 'REMAINDER' AND split_status = 'NOT_SPLIT' AND deleted = 0 THEN root_inbound_id ELSE NULL END) STORED COMMENT '有效余量唯一约束键;历史已完成分片为空'");
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_column;
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_add_index(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_index_name VARCHAR(64),
|
||
IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND index_name = p_index_name
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table_name, '` ADD ', p_definition);
|
||
PREPARE stmt FROM @ddl;
|
||
EXECUTE stmt;
|
||
DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_order', 'uk_inbound_master_order',
|
||
'UNIQUE KEY `uk_inbound_master_order` (`tenant_id`, `master_order_id`)');
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_order', 'idx_inbound_root_role',
|
||
'KEY `idx_inbound_root_role` (`tenant_id`, `root_inbound_id`, `inbound_role`, `created_at`, `id`)');
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_assert_active_remainder_unique_ready;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_assert_active_remainder_unique_ready()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM fl_inbound_order
|
||
WHERE inbound_role = 'CHILD'
|
||
AND split_purpose = 'REMAINDER'
|
||
AND split_status = 'NOT_SPLIT'
|
||
AND deleted = 0
|
||
AND root_inbound_id IS NOT NULL
|
||
GROUP BY tenant_id, root_inbound_id
|
||
HAVING COUNT(*) > 1
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '同一根入仓单存在多个有效余量,请先人工核对历史数据';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_assert_active_remainder_unique_ready();
|
||
DROP PROCEDURE IF EXISTS phase3_assert_active_remainder_unique_ready;
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_order', 'uk_inbound_one_active_remainder',
|
||
'UNIQUE KEY `uk_inbound_one_active_remainder` (`tenant_id`, `active_remainder_root_id`)');
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_legacy_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_drop_legacy_index()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_order'
|
||
AND index_name = 'uk_inbound_tenant_order'
|
||
) THEN
|
||
ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_tenant_order;
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_order'
|
||
AND index_name = 'uk_inbound_parent_sequence'
|
||
) THEN
|
||
ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_parent_sequence;
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_order'
|
||
AND index_name = 'uk_inbound_one_child'
|
||
) THEN
|
||
ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_one_child;
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_order'
|
||
AND index_name = 'uk_inbound_one_secondary'
|
||
) THEN
|
||
ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_one_secondary;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_inbound_drop_legacy_index();
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_legacy_index;
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_order', 'uk_inbound_parent_split_sequence',
|
||
'UNIQUE KEY `uk_inbound_parent_split_sequence` (`tenant_id`, `parent_inbound_id`, `split_sequence`)');
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_split_batch (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '拆分批次ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
parent_inbound_id BIGINT UNSIGNED NOT NULL COMMENT '主入仓单ID',
|
||
operation_id VARCHAR(64) NOT NULL COMMENT '业务操作幂等号',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '请求幂等键',
|
||
expected_parent_version INT NOT NULL COMMENT '操作时主单乐观锁版本',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '本批次拆分箱数',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'PREPARED' COMMENT 'PREPARED、APPLIED、CANCELLED',
|
||
remark VARCHAR(500) 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),
|
||
applied_at DATETIME(3) NULL COMMENT '批次生效时间',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_split_operation (tenant_id, operation_id),
|
||
UNIQUE KEY uk_inbound_split_idempotency (tenant_id, parent_inbound_id, idempotency_key),
|
||
KEY idx_inbound_split_parent (tenant_id, parent_inbound_id, created_at, id),
|
||
CONSTRAINT chk_inbound_split_package CHECK (package_count > 0),
|
||
CONSTRAINT chk_inbound_split_status CHECK (status IN ('PREPARED', 'APPLIED', 'CANCELLED')),
|
||
CONSTRAINT fk_inbound_split_parent FOREIGN KEY (parent_inbound_id) REFERENCES fl_inbound_order (id)
|
||
) ENGINE=InnoDB COMMENT='主入仓单拆分批次;数量口径仅为箱数';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_split_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '拆分明细ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
split_batch_id BIGINT UNSIGNED NOT NULL COMMENT '拆分批次ID',
|
||
parent_inbound_id BIGINT UNSIGNED NOT NULL COMMENT '主入仓单ID',
|
||
child_inbound_id BIGINT UNSIGNED NOT NULL COMMENT '子入仓单ID',
|
||
line_no INT UNSIGNED NOT NULL COMMENT '同一拆分批次内的行号',
|
||
source_inbound_detail_id BIGINT UNSIGNED NULL COMMENT '订单货物明细逻辑ID',
|
||
source_measurement_id BIGINT UNSIGNED NOT NULL COMMENT '来源测量批次ID',
|
||
source_measurement_item_id BIGINT UNSIGNED NOT NULL COMMENT '来源测量箱规明细ID',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '分配给子单的箱数',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_split_batch_line (tenant_id, split_batch_id, line_no),
|
||
UNIQUE KEY uk_inbound_split_batch_source_child (tenant_id, split_batch_id, source_measurement_item_id, child_inbound_id),
|
||
KEY idx_inbound_split_detail (tenant_id, source_inbound_detail_id, created_at),
|
||
CONSTRAINT chk_inbound_split_item_package CHECK (package_count > 0),
|
||
CONSTRAINT fk_inbound_split_item_batch FOREIGN KEY (split_batch_id) REFERENCES fl_inbound_split_batch (id),
|
||
CONSTRAINT fk_inbound_split_item_parent FOREIGN KEY (parent_inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_inbound_split_item_child FOREIGN KEY (child_inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_inbound_split_item_measurement FOREIGN KEY (source_measurement_id) REFERENCES fl_inbound_measurement (id),
|
||
CONSTRAINT fk_inbound_split_item_measure_item FOREIGN KEY (source_measurement_item_id) REFERENCES fl_inbound_measurement_item (id)
|
||
) ENGINE=InnoDB COMMENT='主子入仓单拆分数量台账';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_inbound_split_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '拆分历史ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户隔离ID',
|
||
split_batch_id BIGINT UNSIGNED NOT NULL COMMENT '拆分批次ID',
|
||
operation_id VARCHAR(64) NOT NULL COMMENT '历史操作幂等号',
|
||
action_type VARCHAR(24) NOT NULL COMMENT 'CREATE、APPLY、CANCEL',
|
||
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 '操作人名称快照',
|
||
operation_reason VARCHAR(500) NULL COMMENT '操作原因',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_inbound_split_history_operation (tenant_id, operation_id),
|
||
KEY idx_inbound_split_history (tenant_id, split_batch_id, created_at, id),
|
||
CONSTRAINT chk_inbound_split_action CHECK (action_type IN ('CREATE', 'APPLY', 'CANCEL')),
|
||
CONSTRAINT fk_inbound_split_history_batch FOREIGN KEY (split_batch_id) REFERENCES fl_inbound_split_batch (id)
|
||
) ENGINE=InnoDB COMMENT='不可变主子入仓单拆分审计历史';
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_add_column(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_column_name VARCHAR(64),
|
||
IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table_name, '` ADD COLUMN `',
|
||
p_column_name, '` ', p_definition);
|
||
PREPARE stmt FROM @ddl;
|
||
EXECUTE stmt;
|
||
DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_measurement', 'split_batch_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '产生该测量批次的入仓拆分批次ID' AFTER inbound_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_measurement', 'source_measurement_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '子单继承或复核时的来源测量批次ID' AFTER split_batch_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_measurement_item', 'split_item_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '产生该子单测量明细的拆分台账ID' AFTER measurement_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_measurement_item', 'source_measurement_item_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '被拆分继承的来源测量箱规明细ID' AFTER split_item_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_item', 'line_no',
|
||
"INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '同一拆分批次内的行号' AFTER child_inbound_id");
|
||
ALTER TABLE fl_inbound_split_item
|
||
MODIFY source_measurement_id BIGINT UNSIGNED NOT NULL COMMENT '来源测量批次ID',
|
||
MODIFY source_measurement_item_id BIGINT UNSIGNED NOT NULL COMMENT '来源测量箱规明细ID';
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'loaded_child_inbound_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '本批次实装子单ID' AFTER package_count");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'source_inbound_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '本批次实际消耗的MASTER或REMAINDER分片ID' AFTER parent_inbound_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'remainder_child_inbound_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '本批次余量子单ID' AFTER loaded_child_inbound_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'loaded_package_count',
|
||
"INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '本批次实装箱数' AFTER remainder_child_inbound_id");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'remaining_package_count',
|
||
"INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '本批次未装箱数' AFTER loaded_package_count");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'loading_plan_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '关联装柜计划/柜次' AFTER remaining_package_count");
|
||
CALL phase3_inbound_add_column(
|
||
'fl_inbound_split_batch', 'container_booking_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '关联柜次标识(兼容查询)' AFTER loading_plan_id");
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_column;
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_relation;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_add_relation(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_constraint_name VARCHAR(64),
|
||
IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.table_constraints
|
||
WHERE constraint_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND constraint_name = p_constraint_name
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table_name, '` ADD CONSTRAINT `',
|
||
p_constraint_name, '` ', p_definition);
|
||
PREPARE stmt FROM @ddl;
|
||
EXECUTE stmt;
|
||
DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_order', 'fk_inbound_parent_order',
|
||
'FOREIGN KEY (`parent_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_order', 'fk_inbound_root_order',
|
||
'FOREIGN KEY (`root_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_split_batch', 'fk_inbound_split_loaded_child',
|
||
'FOREIGN KEY (`loaded_child_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_split_batch', 'fk_inbound_split_source',
|
||
'FOREIGN KEY (`source_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_split_batch', 'fk_inbound_split_remainder_child',
|
||
'FOREIGN KEY (`remainder_child_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_measurement', 'fk_measure_split_batch',
|
||
'FOREIGN KEY (`split_batch_id`) REFERENCES `fl_inbound_split_batch` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_measurement', 'fk_measure_source_batch',
|
||
'FOREIGN KEY (`source_measurement_id`) REFERENCES `fl_inbound_measurement` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_measurement_item', 'fk_measure_item_split_item',
|
||
'FOREIGN KEY (`split_item_id`) REFERENCES `fl_inbound_split_item` (`id`)');
|
||
CALL phase3_inbound_add_relation(
|
||
'fl_inbound_measurement_item', 'fk_measure_item_source_item',
|
||
'FOREIGN KEY (`source_measurement_item_id`) REFERENCES `fl_inbound_measurement_item` (`id`)');
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_relation;
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_add_index(
|
||
IN p_table_name VARCHAR(64),
|
||
IN p_index_name VARCHAR(64),
|
||
IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = p_table_name
|
||
AND index_name = p_index_name
|
||
) THEN
|
||
SET @ddl = CONCAT('ALTER TABLE `', p_table_name, '` ADD ', p_definition);
|
||
PREPARE stmt FROM @ddl;
|
||
EXECUTE stmt;
|
||
DEALLOCATE PREPARE stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_measurement', 'idx_measure_split_source',
|
||
'KEY `idx_measure_split_source` (`tenant_id`, `split_batch_id`, `source_measurement_id`, `created_at`)');
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_measurement_item', 'idx_measure_item_split_source',
|
||
'KEY `idx_measure_item_split_source` (`tenant_id`, `split_item_id`, `source_measurement_item_id`)');
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_split_item', 'uk_inbound_split_batch_line',
|
||
'UNIQUE KEY `uk_inbound_split_batch_line` (`tenant_id`, `split_batch_id`, `line_no`)');
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_split_batch', 'idx_inbound_split_loading_plan',
|
||
'KEY `idx_inbound_split_loading_plan` (`tenant_id`, `loading_plan_id`, `container_booking_id`, `status`, `id`)');
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_split_batch', 'uk_inbound_split_source_once',
|
||
'UNIQUE KEY `uk_inbound_split_source_once` (`tenant_id`, `source_inbound_id`)');
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_split_legacy_index;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_inbound_drop_split_legacy_index()
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_split_item' AND index_name = 'uk_inbound_split_child') THEN
|
||
ALTER TABLE fl_inbound_split_item DROP INDEX uk_inbound_split_child;
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_split_item' AND index_name = 'uk_inbound_split_batch_child') THEN
|
||
ALTER TABLE fl_inbound_split_item DROP INDEX uk_inbound_split_batch_child;
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_split_item' AND index_name = 'uk_inbound_split_batch_source_item') THEN
|
||
ALTER TABLE fl_inbound_split_item DROP INDEX uk_inbound_split_batch_source_item;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_inbound_drop_split_legacy_index();
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_split_legacy_index;
|
||
CALL phase3_inbound_add_index(
|
||
'fl_inbound_split_item', 'uk_inbound_split_batch_source_child',
|
||
'UNIQUE KEY `uk_inbound_split_batch_source_child` (`tenant_id`, `split_batch_id`, `source_measurement_item_id`, `child_inbound_id`)');
|
||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||
|
||
DROP TRIGGER IF EXISTS trg_inbound_order_parent_before_insert;
|
||
DROP TRIGGER IF EXISTS trg_inbound_order_parent_before_update;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_item_before_insert;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_batch_before_insert;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_batch_before_update;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_item_before_update;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_item_before_delete;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_history_before_update;
|
||
DROP TRIGGER IF EXISTS trg_inbound_split_history_before_delete;
|
||
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_inbound_order_parent_before_insert
|
||
BEFORE INSERT ON fl_inbound_order
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF NEW.inbound_role NOT IN ('MASTER', 'CHILD') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单层级非法';
|
||
END IF;
|
||
IF NEW.split_status NOT IN ('NOT_SPLIT', 'PARTIALLY_SPLIT', 'FULLY_SPLIT') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单拆分状态非法';
|
||
END IF;
|
||
IF NEW.split_purpose NOT IN ('MASTER', 'LOADED', 'REMAINDER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单分片用途非法';
|
||
END IF;
|
||
IF NEW.package_count IS NOT NULL AND NEW.package_count = 0 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单箱数必须大于0';
|
||
END IF;
|
||
IF NEW.inbound_role = 'MASTER' AND NEW.parent_inbound_id IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主入仓单不能绑定父单';
|
||
END IF;
|
||
IF NEW.inbound_role = 'MASTER' AND NEW.split_purpose <> 'MASTER' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主入仓单用途必须为MASTER';
|
||
END IF;
|
||
IF NEW.inbound_role = 'CHILD' THEN
|
||
IF NEW.parent_inbound_id IS NULL OR NEW.root_inbound_id IS NULL
|
||
OR NEW.split_sequence IS NULL OR NEW.package_count IS NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单缺少主单、顺序号或箱数';
|
||
END IF;
|
||
IF NEW.split_purpose <> 'REMAINDER' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '新建子入仓单只能作为余量分片';
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order parent_order
|
||
WHERE parent_order.id = NEW.parent_inbound_id
|
||
AND parent_order.tenant_id = NEW.tenant_id
|
||
AND parent_order.inbound_role = 'MASTER'
|
||
AND parent_order.deleted = 0
|
||
) OR NEW.root_inbound_id <> NEW.parent_inbound_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单必须绑定同一客户的有效主单';
|
||
END IF;
|
||
END IF;
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_order_parent_before_update
|
||
BEFORE UPDATE ON fl_inbound_order
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF NOT (OLD.tenant_id <=> NEW.tenant_id)
|
||
OR NOT (OLD.inbound_role <=> NEW.inbound_role)
|
||
OR NOT (OLD.parent_inbound_id <=> NEW.parent_inbound_id)
|
||
OR NOT (OLD.root_inbound_id <=> NEW.root_inbound_id)
|
||
OR NOT (OLD.split_sequence <=> NEW.split_sequence)
|
||
OR NOT (OLD.split_purpose <=> NEW.split_purpose) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单主子归属创建后不可修改';
|
||
END IF;
|
||
IF NEW.split_purpose NOT IN ('MASTER', 'LOADED', 'REMAINDER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单分片用途非法';
|
||
END IF;
|
||
IF NEW.inbound_role = 'CHILD'
|
||
AND (NEW.package_count IS NULL OR NEW.package_count = 0) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单箱数必须大于0';
|
||
END IF;
|
||
IF NEW.split_status NOT IN ('NOT_SPLIT', 'PARTIALLY_SPLIT', 'FULLY_SPLIT') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单拆分状态非法';
|
||
END IF;
|
||
IF (NEW.inbound_role = 'CHILD' AND NEW.split_purpose = 'MASTER')
|
||
OR (NEW.inbound_role = 'MASTER' AND NEW.split_purpose <> 'MASTER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单层级与用途不一致';
|
||
END IF;
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_item_before_insert
|
||
BEFORE INSERT ON fl_inbound_split_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF NEW.source_measurement_id IS NULL OR NEW.source_measurement_item_id IS NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分明细必须绑定真实测量来源';
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1
|
||
FROM fl_inbound_split_batch batch
|
||
JOIN fl_inbound_order parent_order ON parent_order.id = batch.parent_inbound_id
|
||
JOIN fl_inbound_order child_order ON child_order.id = NEW.child_inbound_id
|
||
WHERE batch.id = NEW.split_batch_id
|
||
AND batch.tenant_id = NEW.tenant_id
|
||
AND batch.parent_inbound_id = NEW.parent_inbound_id
|
||
AND batch.status = 'PREPARED'
|
||
AND batch.loaded_child_inbound_id IS NULL
|
||
AND batch.remainder_child_inbound_id IS NOT NULL
|
||
AND NEW.child_inbound_id = batch.remainder_child_inbound_id
|
||
AND parent_order.tenant_id = NEW.tenant_id
|
||
AND parent_order.inbound_role = 'MASTER'
|
||
AND child_order.tenant_id = NEW.tenant_id
|
||
AND child_order.inbound_role = 'CHILD'
|
||
AND child_order.split_purpose = 'REMAINDER'
|
||
AND child_order.parent_inbound_id = NEW.parent_inbound_id
|
||
AND child_order.package_count >= NEW.package_count
|
||
AND child_order.deleted = 0
|
||
AND EXISTS (
|
||
SELECT 1
|
||
FROM fl_inbound_measurement_item source_item
|
||
JOIN fl_inbound_measurement source_batch ON source_batch.id = source_item.measurement_id
|
||
WHERE source_item.id = NEW.source_measurement_item_id
|
||
AND source_item.tenant_id = NEW.tenant_id
|
||
AND source_item.measurement_id = NEW.source_measurement_id
|
||
AND source_batch.inbound_id = COALESCE(batch.source_inbound_id, batch.parent_inbound_id)
|
||
)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分明细与批次、主单或子单不一致';
|
||
END IF;
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_batch_before_insert
|
||
BEFORE INSERT ON fl_inbound_split_batch
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF NEW.status <> 'PREPARED' OR NEW.applied_at IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次必须从PREPARED创建';
|
||
END IF;
|
||
IF NEW.loaded_child_inbound_id IS NOT NULL
|
||
OR NEW.loaded_package_count = 0
|
||
OR NEW.loaded_package_count + NEW.remaining_package_count <> NEW.package_count
|
||
OR (NEW.remaining_package_count = 0 AND NEW.remainder_child_inbound_id IS NOT NULL)
|
||
OR (NEW.remaining_package_count > 0 AND NEW.remainder_child_inbound_id IS NULL) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '新拆分批次必须由来源单承接实装且最多创建一个余量子单';
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order parent_order
|
||
WHERE parent_order.id = NEW.parent_inbound_id
|
||
AND parent_order.tenant_id = NEW.tenant_id
|
||
AND parent_order.inbound_role = 'MASTER'
|
||
AND parent_order.deleted = 0
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次主单不存在或客户归属不一致';
|
||
END IF;
|
||
IF NEW.source_inbound_id IS NULL OR NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order source_order
|
||
WHERE source_order.id = NEW.source_inbound_id
|
||
AND source_order.tenant_id = NEW.tenant_id
|
||
AND source_order.deleted = 0
|
||
AND ((source_order.id = NEW.parent_inbound_id
|
||
AND source_order.inbound_role = 'MASTER'
|
||
AND ((source_order.split_status = 'NOT_SPLIT'
|
||
AND COALESCE(source_order.package_count, source_order.total_package_count) = NEW.package_count)
|
||
OR (source_order.split_status = 'FULLY_SPLIT'
|
||
AND source_order.package_count = NEW.loaded_package_count))) OR
|
||
(source_order.inbound_role = 'CHILD'
|
||
AND source_order.parent_inbound_id = NEW.parent_inbound_id
|
||
AND source_order.split_purpose = 'REMAINDER'
|
||
AND ((source_order.split_status = 'NOT_SPLIT'
|
||
AND source_order.package_count = NEW.package_count)
|
||
OR (source_order.split_status = 'FULLY_SPLIT'
|
||
AND source_order.package_count = NEW.loaded_package_count))))
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次来源必须是箱数守恒的根主单或余量分片';
|
||
END IF;
|
||
IF NEW.loaded_package_count > 0 AND (
|
||
NEW.loading_plan_id IS NULL OR NEW.container_booking_id IS NULL OR NOT EXISTS (
|
||
SELECT 1 FROM fl_loading_plan plan
|
||
WHERE plan.id = NEW.loading_plan_id
|
||
AND plan.container_booking_id = NEW.container_booking_id
|
||
AND EXISTS (SELECT 1 FROM fl_loading_plan_item plan_item
|
||
WHERE plan_item.loading_plan_id = plan.id
|
||
AND plan_item.account_id = NEW.tenant_id
|
||
AND plan_item.inbound_id = NEW.source_inbound_id
|
||
AND plan_item.deleted = 0)
|
||
)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '实装拆分批次必须绑定匹配的装柜计划和柜次';
|
||
END IF;
|
||
IF NEW.remainder_child_inbound_id IS NOT NULL AND NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order remainder_order
|
||
WHERE remainder_order.id = NEW.remainder_child_inbound_id
|
||
AND remainder_order.tenant_id = NEW.tenant_id
|
||
AND remainder_order.parent_inbound_id = NEW.parent_inbound_id
|
||
AND remainder_order.root_inbound_id = NEW.parent_inbound_id
|
||
AND remainder_order.inbound_role = 'CHILD'
|
||
AND remainder_order.split_purpose = 'REMAINDER'
|
||
AND remainder_order.split_status = 'NOT_SPLIT'
|
||
AND remainder_order.package_count = NEW.remaining_package_count
|
||
AND remainder_order.deleted = 0
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '余量子入仓单与拆分批次数量或根归属不一致';
|
||
END IF;
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_batch_before_update
|
||
BEFORE UPDATE ON fl_inbound_split_batch
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.status IN ('APPLIED', 'CANCELLED') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已结束拆分批次不可修改';
|
||
END IF;
|
||
IF NOT (OLD.parent_inbound_id <=> NEW.parent_inbound_id)
|
||
OR NOT (OLD.source_inbound_id <=> NEW.source_inbound_id)
|
||
OR NOT (OLD.loaded_child_inbound_id <=> NEW.loaded_child_inbound_id)
|
||
OR NOT (OLD.remainder_child_inbound_id <=> NEW.remainder_child_inbound_id)
|
||
OR NOT (OLD.package_count <=> NEW.package_count)
|
||
OR NOT (OLD.loaded_package_count <=> NEW.loaded_package_count)
|
||
OR NOT (OLD.remaining_package_count <=> NEW.remaining_package_count)
|
||
OR NOT (OLD.loading_plan_id <=> NEW.loading_plan_id)
|
||
OR NOT (OLD.container_booking_id <=> NEW.container_booking_id) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次来源、子单、柜次与数量创建后不可修改';
|
||
END IF;
|
||
IF NEW.status = 'APPLIED' THEN
|
||
IF NEW.applied_at IS NULL
|
||
OR (NEW.loaded_package_count > 0 AND (
|
||
NEW.loading_plan_id IS NULL OR NEW.container_booking_id IS NULL OR NOT EXISTS (
|
||
SELECT 1 FROM fl_loading_plan plan
|
||
WHERE plan.id = NEW.loading_plan_id
|
||
AND plan.container_booking_id = NEW.container_booking_id
|
||
AND EXISTS (SELECT 1 FROM fl_loading_plan_item plan_item
|
||
WHERE plan_item.loading_plan_id = plan.id
|
||
AND plan_item.account_id = NEW.tenant_id
|
||
AND plan_item.inbound_id = NEW.source_inbound_id
|
||
AND plan_item.deleted = 0))))
|
||
OR NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order parent_order
|
||
WHERE parent_order.id = NEW.parent_inbound_id
|
||
AND parent_order.tenant_id = NEW.tenant_id
|
||
AND parent_order.inbound_role = 'MASTER'
|
||
AND parent_order.version > NEW.expected_parent_version
|
||
) OR NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order source_order
|
||
WHERE source_order.id = NEW.source_inbound_id
|
||
AND source_order.tenant_id = NEW.tenant_id
|
||
AND source_order.split_status = 'FULLY_SPLIT'
|
||
AND source_order.package_count = NEW.loaded_package_count
|
||
) OR
|
||
(SELECT COALESCE(SUM(item.package_count), 0)
|
||
FROM fl_inbound_split_item item
|
||
WHERE item.tenant_id = NEW.tenant_id
|
||
AND item.split_batch_id = NEW.id) <> NEW.remaining_package_count OR
|
||
(SELECT COUNT(DISTINCT item.child_inbound_id)
|
||
FROM fl_inbound_split_item item
|
||
WHERE item.tenant_id = NEW.tenant_id
|
||
AND item.split_batch_id = NEW.id) <> CASE WHEN NEW.remaining_package_count > 0 THEN 1 ELSE 0 END OR
|
||
(NEW.loaded_child_inbound_id IS NOT NULL) OR
|
||
NEW.loaded_package_count + NEW.remaining_package_count <> NEW.package_count OR
|
||
(NEW.remaining_package_count > 0 AND NEW.remainder_child_inbound_id IS NULL) OR
|
||
(NEW.remainder_child_inbound_id IS NOT NULL AND NOT EXISTS (
|
||
SELECT 1 FROM fl_inbound_order c WHERE c.id = NEW.remainder_child_inbound_id
|
||
AND c.parent_inbound_id = NEW.parent_inbound_id AND c.root_inbound_id = NEW.parent_inbound_id
|
||
AND c.split_purpose = 'REMAINDER' AND c.split_status = 'NOT_SPLIT'
|
||
AND c.package_count = NEW.remaining_package_count AND c.deleted = 0)) OR
|
||
EXISTS (SELECT 1 FROM fl_inbound_split_item item
|
||
WHERE item.tenant_id = NEW.tenant_id
|
||
AND item.split_batch_id = NEW.id
|
||
AND item.child_inbound_id <> NEW.remainder_child_inbound_id) OR
|
||
EXISTS (SELECT 1
|
||
FROM fl_inbound_split_item item
|
||
JOIN fl_inbound_order child_order ON child_order.id = item.child_inbound_id
|
||
WHERE item.tenant_id = NEW.tenant_id
|
||
AND item.split_batch_id = NEW.id
|
||
GROUP BY child_order.id, child_order.package_count
|
||
HAVING child_order.package_count <> SUM(item.package_count)) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二级子入仓单或拆分明细箱数合计不一致';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM fl_inbound_split_item split_item
|
||
JOIN fl_inbound_measurement_item source_item
|
||
ON source_item.id = split_item.source_measurement_item_id
|
||
WHERE split_item.tenant_id = NEW.tenant_id
|
||
AND split_item.split_batch_id = NEW.id
|
||
GROUP BY split_item.source_measurement_item_id, source_item.package_count
|
||
HAVING SUM(split_item.package_count) > source_item.package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子单承接箱数超过来源测量箱规箱数';
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1
|
||
FROM fl_inbound_order root_order
|
||
WHERE root_order.id = NEW.parent_inbound_id
|
||
AND root_order.tenant_id = NEW.tenant_id
|
||
AND root_order.inbound_role = 'MASTER'
|
||
AND root_order.deleted = 0
|
||
AND root_order.total_package_count =
|
||
COALESCE(root_order.package_count, 0) +
|
||
COALESCE((SELECT SUM(child.package_count)
|
||
FROM fl_inbound_order child
|
||
WHERE child.tenant_id = NEW.tenant_id
|
||
AND child.root_inbound_id = root_order.id
|
||
AND child.inbound_role = 'CHILD'
|
||
AND child.deleted = 0), 0)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '根入仓单原始箱数与已完成分片及当前余量不守恒';
|
||
END IF;
|
||
ELSEIF NEW.status = 'CANCELLED' THEN
|
||
IF EXISTS (
|
||
SELECT 1 FROM fl_inbound_split_item item
|
||
WHERE item.tenant_id = NEW.tenant_id
|
||
AND item.split_batch_id = NEW.id
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已有子单明细的拆分批次不可取消';
|
||
END IF;
|
||
ELSEIF NEW.status <> 'PREPARED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次状态流转非法';
|
||
END IF;
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_item_before_update
|
||
BEFORE UPDATE ON fl_inbound_split_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分数量台账不可修改';
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_item_before_delete
|
||
BEFORE DELETE ON fl_inbound_split_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分数量台账不可删除';
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_history_before_update
|
||
BEFORE UPDATE ON fl_inbound_split_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分历史不可修改';
|
||
END$$
|
||
|
||
CREATE TRIGGER trg_inbound_split_history_before_delete
|
||
BEFORE DELETE ON fl_inbound_split_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分历史不可删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_assert_inbound_parent_child_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_assert_inbound_parent_child_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name IN ('fl_inbound_split_batch', 'fl_inbound_split_item',
|
||
'fl_inbound_split_history')) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单拆分台账未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_order'
|
||
AND column_name IN ('inbound_role', 'parent_inbound_id', 'root_inbound_id',
|
||
'split_sequence', 'split_status', 'package_count',
|
||
'master_order_id', 'split_purpose', 'active_remainder_root_id')) <> 9 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单关系字段未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_order'
|
||
AND index_name IN ('uk_inbound_master_order', 'uk_inbound_parent_split_sequence',
|
||
'uk_inbound_one_active_remainder')) <> 3
|
||
OR (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_split_batch'
|
||
AND index_name IN ('uk_inbound_split_operation', 'uk_inbound_split_idempotency',
|
||
'uk_inbound_split_source_once')) <> 3
|
||
OR (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_split_item'
|
||
AND index_name IN ('uk_inbound_split_batch_line',
|
||
'uk_inbound_split_batch_source_child')) <> 2
|
||
OR (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_split_history'
|
||
AND index_name = 'uk_inbound_split_history_operation') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单唯一约束未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_split_batch'
|
||
AND column_name IN ('source_inbound_id', 'loaded_child_inbound_id',
|
||
'remainder_child_inbound_id', 'loaded_package_count',
|
||
'remaining_package_count', 'loading_plan_id',
|
||
'container_booking_id')) <> 7 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次来源、子单、数量或柜次字段未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_split_item'
|
||
AND column_name IN ('source_measurement_id', 'source_measurement_item_id')
|
||
AND is_nullable = 'NO') <> 2 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分数量台账必须绑定非空测量来源';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.table_constraints
|
||
WHERE constraint_schema = DATABASE()
|
||
AND constraint_name IN ('fk_inbound_parent_order', 'fk_inbound_root_order',
|
||
'fk_inbound_split_source', 'fk_inbound_split_loaded_child', 'fk_inbound_split_remainder_child',
|
||
'fk_measure_split_batch', 'fk_measure_source_batch',
|
||
'fk_measure_item_split_item', 'fk_measure_item_source_item')) <> 9 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单与测量关联约束未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.triggers
|
||
WHERE trigger_schema = DATABASE()
|
||
AND trigger_name IN ('trg_inbound_order_parent_before_insert',
|
||
'trg_inbound_order_parent_before_update',
|
||
'trg_inbound_split_item_before_insert',
|
||
'trg_inbound_split_batch_before_insert',
|
||
'trg_inbound_split_batch_before_update',
|
||
'trg_inbound_split_item_before_update',
|
||
'trg_inbound_split_item_before_delete',
|
||
'trg_inbound_split_history_before_update',
|
||
'trg_inbound_split_history_before_delete')) <> 9 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单数据库保护触发器未完整创建';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_order'
|
||
AND index_name IN ('uk_inbound_tenant_order', 'uk_inbound_one_child',
|
||
'uk_inbound_one_secondary', 'uk_inbound_parent_sequence')
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '旧订单唯一索引仍阻止创建子入仓单';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_inbound_split_item'
|
||
AND index_name IN ('uk_inbound_split_child', 'uk_inbound_split_batch_child',
|
||
'uk_inbound_split_batch_source_item')
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '旧拆分明细唯一索引仍阻止数量守恒分配';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_assert_inbound_parent_child_schema();
|
||
DROP PROCEDURE IF EXISTS phase3_assert_inbound_parent_child_schema;
|
||
|
||
-- 来源:firstleg/src/main/resources/db/phase3-ocean-booking-schema.sql
|
||
-- 来源:firstleg/src/main/resources/db/phase3-truck-appointment-schema.sql
|
||
-- ============================================================
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_ocean_booking (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
booking_no VARCHAR(40) NOT NULL,
|
||
status VARCHAR(24) NOT NULL DEFAULT 'DRAFT',
|
||
customer_id BIGINT UNSIGNED NOT NULL COMMENT '当前产品客户ID',
|
||
customer_name VARCHAR(128) NOT NULL,
|
||
bill_of_lading_no VARCHAR(128) NULL,
|
||
shipping_company_id BIGINT UNSIGNED NULL,
|
||
shipping_company_name VARCHAR(128) NULL,
|
||
booking_agent_id BIGINT UNSIGNED NULL,
|
||
booking_agent_name VARCHAR(128) NULL,
|
||
vessel_name VARCHAR(128) NULL,
|
||
voyage_no VARCHAR(64) NULL,
|
||
departure_port_code VARCHAR(32) NULL,
|
||
departure_port_name VARCHAR(128) NULL,
|
||
destination_port_code VARCHAR(32) NULL,
|
||
destination_port_name VARCHAR(128) NULL,
|
||
place_of_delivery VARCHAR(128) NULL,
|
||
service_term VARCHAR(32) NULL,
|
||
freight_term VARCHAR(32) NULL,
|
||
estimated_sailing_at DATETIME(3) NULL,
|
||
estimated_arrival_at DATETIME(3) NULL,
|
||
pending_vessel_name VARCHAR(128) NULL,
|
||
pending_voyage_no VARCHAR(64) NULL,
|
||
pending_estimated_sailing_at DATETIME(3) NULL,
|
||
pending_estimated_arrival_at DATETIME(3) NULL,
|
||
change_reason VARCHAR(1000) NULL,
|
||
failure_reason VARCHAR(1000) NULL,
|
||
cancellation_reason VARCHAR(1000) NULL,
|
||
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_ocean_booking_no (booking_no),
|
||
KEY idx_ocean_booking_customer (customer_id,status,updated_at,id),
|
||
KEY idx_ocean_booking_voyage (vessel_name,voyage_no,updated_at),
|
||
CONSTRAINT chk_ocean_booking_customer CHECK (customer_id > 0),
|
||
CONSTRAINT chk_ocean_booking_status CHECK (status IN
|
||
('DRAFT','SUBMITTED','CONFIRMED','RELEASED','FAILED','CHANGE_PENDING','CANCELLED')),
|
||
CONSTRAINT chk_ocean_booking_schedule CHECK
|
||
(estimated_sailing_at IS NULL OR estimated_arrival_at IS NULL OR estimated_arrival_at >= estimated_sailing_at),
|
||
CONSTRAINT chk_ocean_booking_pending_schedule CHECK
|
||
(pending_estimated_sailing_at IS NULL OR pending_estimated_arrival_at IS NULL
|
||
OR pending_estimated_arrival_at >= pending_estimated_sailing_at)
|
||
) ENGINE=InnoDB COMMENT='独立海运订舱聚合;柜次仍由fl_container_booking承载';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_party (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
ocean_booking_id BIGINT UNSIGNED NOT NULL,
|
||
party_type VARCHAR(24) NOT NULL COMMENT 'SHIPPER/CONSIGNEE/NOTIFY_PARTY',
|
||
party_name VARCHAR(255) NOT NULL,
|
||
address VARCHAR(1000) NULL,
|
||
contact_name VARCHAR(128) NULL,
|
||
contact_phone VARCHAR(64) NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_ocean_booking_party (ocean_booking_id,party_type),
|
||
CONSTRAINT fk_ocean_booking_party FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||
CONSTRAINT chk_ocean_booking_party_type CHECK (party_type IN ('SHIPPER','CONSIGNEE','NOTIFY_PARTY'))
|
||
) ENGINE=InnoDB COMMENT='订舱提单关系方快照';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_container_requirement (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
ocean_booking_id BIGINT UNSIGNED NOT NULL,
|
||
container_type VARCHAR(32) NOT NULL,
|
||
container_kind VARCHAR(32) NOT NULL,
|
||
quantity INT UNSIGNED NOT NULL,
|
||
sort_no INT UNSIGNED NOT NULL DEFAULT 0,
|
||
PRIMARY KEY (id),
|
||
KEY idx_ocean_requirement_booking (ocean_booking_id,sort_no,id),
|
||
CONSTRAINT fk_ocean_requirement_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||
CONSTRAINT chk_ocean_requirement_quantity CHECK (quantity > 0)
|
||
) ENGINE=InnoDB COMMENT='订舱箱型箱种及数量需求';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_container_allocation (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
ocean_booking_id BIGINT UNSIGNED NOT NULL,
|
||
requirement_id BIGINT UNSIGNED NOT NULL,
|
||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '明确引用现有柜次ID',
|
||
allocated_by BIGINT UNSIGNED NOT NULL,
|
||
allocated_by_name VARCHAR(64) NULL,
|
||
allocated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_ocean_allocation_container (container_booking_id),
|
||
KEY idx_ocean_allocation_booking (ocean_booking_id,requirement_id,allocated_at),
|
||
CONSTRAINT fk_ocean_allocation_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||
CONSTRAINT fk_ocean_allocation_requirement FOREIGN KEY (requirement_id)
|
||
REFERENCES fl_ocean_booking_container_requirement(id),
|
||
CONSTRAINT fk_ocean_allocation_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id)
|
||
) ENGINE=InnoDB COMMENT='订舱需求与现有柜次的唯一分配关系';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_status_history (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
ocean_booking_id BIGINT UNSIGNED NOT NULL,
|
||
from_status VARCHAR(24) NULL,
|
||
to_status VARCHAR(24) NOT NULL,
|
||
action VARCHAR(40) 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_ocean_history_booking (ocean_booking_id,created_at,id),
|
||
CONSTRAINT fk_ocean_history_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id)
|
||
) ENGINE=InnoDB COMMENT='海运订舱不可变状态历史';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_operation (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
ocean_booking_id BIGINT UNSIGNED NULL COMMENT '创建请求完成前允许为空',
|
||
operator_id BIGINT UNSIGNED NOT NULL,
|
||
operation VARCHAR(40) 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_ocean_operation (operator_id,operation,idempotency_key),
|
||
KEY idx_ocean_operation_booking (ocean_booking_id,created_at),
|
||
CONSTRAINT fk_ocean_operation_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||
CONSTRAINT chk_ocean_operation_status CHECK (status IN ('PROCESSING','SUCCESS'))
|
||
) ENGINE=InnoDB COMMENT='海运订舱写操作幂等与请求哈希';
|
||
|
||
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 IN ('RELEASED','CANCELLED') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已放舱或已取消订舱不可修改';
|
||
END IF;
|
||
IF OLD.status <> NEW.status AND NOT (
|
||
(OLD.status='DRAFT' AND NEW.status IN ('SUBMITTED','CANCELLED'))
|
||
OR (OLD.status='SUBMITTED' AND NEW.status IN ('CONFIRMED','FAILED','CANCELLED'))
|
||
OR (OLD.status='CONFIRMED' AND NEW.status IN ('RELEASED','CHANGE_PENDING','CANCELLED'))
|
||
OR (OLD.status='CHANGE_PENDING' AND NEW.status IN ('CONFIRMED','FAILED','CANCELLED'))
|
||
OR (OLD.status='FAILED' AND NEW.status='CANCELLED')) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='海运订舱状态迁移非法';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_ocean_party_before_insert;
|
||
DROP TRIGGER IF EXISTS trg_ocean_party_before_update;
|
||
DROP TRIGGER IF EXISTS trg_ocean_party_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_ocean_party_before_insert BEFORE INSERT ON fl_ocean_booking_party FOR EACH ROW
|
||
BEGIN IF (SELECT status FROM fl_ocean_booking WHERE id=NEW.ocean_booking_id) <> 'DRAFT' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='仅草稿可维护订舱方'; END IF; END$$
|
||
CREATE TRIGGER trg_ocean_party_before_update BEFORE UPDATE ON fl_ocean_booking_party FOR EACH ROW
|
||
BEGIN IF (SELECT status FROM fl_ocean_booking WHERE id=OLD.ocean_booking_id) <> 'DRAFT' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='仅草稿可维护订舱方'; END IF; END$$
|
||
CREATE TRIGGER trg_ocean_party_before_delete BEFORE DELETE ON fl_ocean_booking_party FOR EACH ROW
|
||
BEGIN IF (SELECT status FROM fl_ocean_booking WHERE id=OLD.ocean_booking_id) <> 'DRAFT' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='仅草稿可维护订舱方'; END IF; END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_ocean_requirement_before_insert;
|
||
DROP TRIGGER IF EXISTS trg_ocean_requirement_before_update;
|
||
DROP TRIGGER IF EXISTS trg_ocean_requirement_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_ocean_requirement_before_insert BEFORE INSERT ON fl_ocean_booking_container_requirement FOR EACH ROW
|
||
BEGIN IF (SELECT status FROM fl_ocean_booking WHERE id=NEW.ocean_booking_id) <> 'DRAFT' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='仅草稿可维护箱型需求'; END IF; END$$
|
||
CREATE TRIGGER trg_ocean_requirement_before_update BEFORE UPDATE ON fl_ocean_booking_container_requirement FOR EACH ROW
|
||
BEGIN IF (SELECT status FROM fl_ocean_booking WHERE id=OLD.ocean_booking_id) <> 'DRAFT' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='仅草稿可维护箱型需求'; END IF; END$$
|
||
CREATE TRIGGER trg_ocean_requirement_before_delete BEFORE DELETE ON fl_ocean_booking_container_requirement FOR EACH ROW
|
||
BEGIN IF (SELECT status FROM fl_ocean_booking WHERE id=OLD.ocean_booking_id) <> 'DRAFT' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='仅草稿可维护箱型需求'; END IF; END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_ocean_allocation_before_insert;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_ocean_allocation_before_insert BEFORE INSERT ON fl_ocean_booking_container_allocation FOR EACH ROW
|
||
BEGIN
|
||
IF (SELECT ocean_booking_id FROM fl_ocean_booking_container_requirement WHERE id=NEW.requirement_id) <> NEW.ocean_booking_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='箱型需求不属于当前订舱';
|
||
END IF;
|
||
IF (SELECT status FROM fl_ocean_booking WHERE id=NEW.ocean_booking_id) NOT IN
|
||
('SUBMITTED','CONFIRMED','CHANGE_PENDING') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='当前订舱状态不可分配柜次';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_ocean_history_before_update;
|
||
DROP TRIGGER IF EXISTS trg_ocean_history_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_ocean_history_before_update BEFORE UPDATE ON fl_ocean_booking_status_history FOR EACH ROW
|
||
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订舱状态历史不可修改'; END$$
|
||
CREATE TRIGGER trg_ocean_history_before_delete BEFORE DELETE ON fl_ocean_booking_status_history FOR EACH ROW
|
||
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订舱状态历史不可删除'; END$$
|
||
DELIMITER ;
|
||
|
||
USE baiyue_iam;
|
||
DROP PROCEDURE IF EXISTS assert_ocean_booking_permission_ids_available;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_ocean_booking_permission_ids_available()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE (id BETWEEN 5102140 AND 5102149
|
||
AND permission_code <> CASE id
|
||
WHEN 5102140 THEN 'platform:firstleg:ocean-booking:list'
|
||
WHEN 5102141 THEN 'platform:firstleg:ocean-booking:view'
|
||
WHEN 5102142 THEN 'platform:firstleg:ocean-booking:create'
|
||
WHEN 5102143 THEN 'platform:firstleg:ocean-booking:edit'
|
||
WHEN 5102144 THEN 'platform:firstleg:ocean-booking:submit'
|
||
WHEN 5102145 THEN 'platform:firstleg:ocean-booking:confirm'
|
||
WHEN 5102146 THEN 'platform:firstleg:ocean-booking:release'
|
||
WHEN 5102147 THEN 'platform:firstleg:ocean-booking:cancel'
|
||
WHEN 5102148 THEN 'platform:firstleg:ocean-booking:change'
|
||
WHEN 5102149 THEN 'platform:firstleg:ocean-booking:allocate'
|
||
END)
|
||
OR (permission_code LIKE 'platform:firstleg:ocean-booking:%'
|
||
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT BETWEEN 5102140 AND 5102149))
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '三期订舱权限迁移失败:5102140-5102149 ID 或权限码被其他语义占用';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_ocean_booking_permission_ids_available();
|
||
DROP PROCEDURE IF EXISTS assert_ocean_booking_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
|
||
(5102140,0,'PLATFORM','MENU',5102000,'platform:firstleg:ocean-booking:list','订舱管理','/first-leg/ocean-bookings','GET',80,'ACTIVE',1,0),
|
||
(5102141,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:view','查看订舱',NULL,'GET',10,'ACTIVE',1,0),
|
||
(5102142,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:create','创建订舱',NULL,'POST',20,'ACTIVE',1,0),
|
||
(5102143,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:edit','编辑订舱',NULL,'PUT',30,'ACTIVE',1,0),
|
||
(5102144,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:submit','提交订舱',NULL,'POST',40,'ACTIVE',1,0),
|
||
(5102145,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:confirm','确认订舱',NULL,'POST',50,'ACTIVE',1,0),
|
||
(5102146,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:release','确认放舱',NULL,'POST',60,'ACTIVE',1,0),
|
||
(5102147,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:cancel','取消订舱',NULL,'POST',70,'ACTIVE',1,0),
|
||
(5102148,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:change','变更船期',NULL,'POST',80,'ACTIVE',1,0),
|
||
(5102149,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:allocate','分配柜次',NULL,'POST',90,'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.permission_code LIKE 'platform:firstleg:ocean-booking:%'
|
||
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR');
|
||
|
||
USE baiyue_firstleg;
|
||
|
||
|
||
SET NAMES utf8mb4;
|
||
CREATE TABLE IF NOT EXISTS fl_truck_appointment (
|
||
id BIGINT UNSIGNED NOT NULL, appointment_no VARCHAR(40) NOT NULL, outbound_order_id BIGINT UNSIGNED NULL,
|
||
ocean_booking_id BIGINT UNSIGNED NULL, container_booking_id BIGINT UNSIGNED NULL, loading_plan_id BIGINT UNSIGNED NULL,
|
||
warehouse_id BIGINT UNSIGNED NULL, customer_id BIGINT UNSIGNED NULL, customer_name VARCHAR(128) NULL,
|
||
carrier_id BIGINT UNSIGNED NULL, carrier_name VARCHAR(128) NULL, fleet_contact VARCHAR(64) NULL, fleet_phone VARCHAR(32) NULL,
|
||
driver_name VARCHAR(64) NULL, driver_phone VARCHAR(32) NULL, driver_id_card VARCHAR(32) NULL, vehicle_no VARCHAR(32) NULL,
|
||
store_name VARCHAR(128) NULL, store_contact VARCHAR(64) NULL, store_phone VARCHAR(32) NULL, store_address VARCHAR(255) NULL,
|
||
store_city VARCHAR(64) NULL, store_zipcode VARCHAR(16) NULL, package_count INT UNSIGNED NULL, gross_weight_kg DECIMAL(18,3) NULL,
|
||
volume_cbm DECIMAL(18,4) NULL, pallet_count INT UNSIGNED NULL, po_no VARCHAR(255) NULL, planned_loading_at DATETIME(3) NULL,
|
||
actual_arrived_warehouse_at DATETIME(3) NULL, actual_loading_at DATETIME(3) NULL, actual_departed_at DATETIME(3) NULL, transit_minutes INT UNSIGNED NULL, payable_amount DECIMAL(18,2) NULL, receivable_amount DECIMAL(18,2) NULL,
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'RMB', status VARCHAR(32) NOT NULL DEFAULT 'DRAFT', revision INT UNSIGNED NOT NULL DEFAULT 0,
|
||
version INT UNSIGNED NOT NULL DEFAULT 0, remark VARCHAR(1000) 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_truck_appointment_no(appointment_no),
|
||
KEY idx_truck_appointment_status(status,updated_at), KEY idx_truck_appointment_container(container_booking_id), KEY idx_truck_appointment_customer(customer_id),
|
||
CONSTRAINT chk_truck_appointment_status CHECK(status IN ('DRAFT','PENDING_DISPATCH','DISPATCHED','PICKUP_IN_PROGRESS','ARRIVED_WAREHOUSE','LOADING','DEPARTED','COMPLETED','RESCHEDULED','EXCEPTION','CANCELLED'))
|
||
) ENGINE=InnoDB COMMENT='三期拖车预约派车聚合';
|
||
CREATE TABLE IF NOT EXISTS fl_truck_appointment_history (
|
||
id BIGINT UNSIGNED NOT NULL, appointment_id BIGINT UNSIGNED NOT NULL, revision INT UNSIGNED NOT NULL, action VARCHAR(32) NOT NULL,
|
||
from_status VARCHAR(32) NULL, to_status VARCHAR(32) NULL, reason VARCHAR(500) NULL, operator_id BIGINT UNSIGNED NOT NULL,
|
||
operator_name VARCHAR(64) NULL, request_id VARCHAR(128) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY(id), KEY idx_truck_history(appointment_id,revision)
|
||
) ENGINE=InnoDB COMMENT='拖车状态与改约历史';
|
||
CREATE TABLE IF NOT EXISTS fl_truck_appointment_attachment (
|
||
id BIGINT UNSIGNED NOT NULL, appointment_id BIGINT UNSIGNED NOT NULL, file_id BIGINT UNSIGNED NOT NULL, attachment_type VARCHAR(32) NOT NULL,
|
||
file_name VARCHAR(255) NULL, file_hash CHAR(64) NULL, visibility VARCHAR(24) NOT NULL DEFAULT 'INTERNAL', uploaded_by BIGINT UNSIGNED NOT NULL,
|
||
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY(id), UNIQUE KEY uk_truck_attachment(appointment_id,file_id), KEY idx_truck_attachment(appointment_id)
|
||
) ENGINE=InnoDB COMMENT='拖车附件文件引用';
|
||
CREATE TABLE IF NOT EXISTS fl_truck_appointment_operation (
|
||
id BIGINT UNSIGNED NOT NULL, appointment_id BIGINT UNSIGNED NOT NULL, idempotency_key VARCHAR(128) NOT NULL, request_hash CHAR(64) NOT NULL,
|
||
action VARCHAR(32) NOT NULL, response_snapshot JSON NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY(id), UNIQUE KEY uk_truck_operation(idempotency_key), KEY idx_truck_operation_appointment(appointment_id)
|
||
) ENGINE=InnoDB COMMENT='拖车操作幂等记录';
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_truck_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_truck_add_column(
|
||
IN p_column_name VARCHAR(64), IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'fl_truck_appointment'
|
||
AND column_name = p_column_name
|
||
) THEN
|
||
SET @phase3_truck_ddl = CONCAT(
|
||
'ALTER TABLE fl_truck_appointment ADD COLUMN `', p_column_name, '` ', p_definition
|
||
);
|
||
PREPARE phase3_truck_stmt FROM @phase3_truck_ddl;
|
||
EXECUTE phase3_truck_stmt;
|
||
DEALLOCATE PREPARE phase3_truck_stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CALL phase3_truck_add_column(
|
||
'actual_arrived_warehouse_at',
|
||
'DATETIME(3) NULL AFTER planned_loading_at'
|
||
);
|
||
CALL phase3_truck_add_column(
|
||
'actual_departed_at',
|
||
'DATETIME(3) NULL AFTER actual_loading_at'
|
||
);
|
||
DROP PROCEDURE IF EXISTS phase3_truck_add_column;
|
||
|
||
USE baiyue_iam;
|
||
DROP PROCEDURE IF EXISTS assert_truck_appointment_permission_ids_available;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_truck_appointment_permission_ids_available()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE (id BETWEEN 5102150 AND 5102154
|
||
AND permission_code <> CASE id
|
||
WHEN 5102150 THEN 'platform:firstleg:truck-appointment:list'
|
||
WHEN 5102151 THEN 'platform:firstleg:truck-appointment:view'
|
||
WHEN 5102152 THEN 'platform:firstleg:truck-appointment:create'
|
||
WHEN 5102153 THEN 'platform:firstleg:truck-appointment:edit'
|
||
WHEN 5102154 THEN 'platform:firstleg:truck-appointment:operate'
|
||
END)
|
||
OR (permission_code LIKE 'platform:firstleg:truck-appointment:%'
|
||
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT BETWEEN 5102150 AND 5102154))
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '三期拖车权限迁移失败:5102150-5102154 ID 或权限码被其他语义占用';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_truck_appointment_permission_ids_available();
|
||
DROP PROCEDURE IF EXISTS assert_truck_appointment_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
|
||
(5102150,0,'PLATFORM','MENU',5102000,'platform:firstleg:truck-appointment:list','拖车预约','/first-leg/truck-appointments','GET',90,'ACTIVE',1,0),
|
||
(5102151,0,'PLATFORM','BUTTON',5102150,'platform:firstleg:truck-appointment:view','查看拖车预约',NULL,'GET',10,'ACTIVE',1,0),
|
||
(5102152,0,'PLATFORM','BUTTON',5102150,'platform:firstleg:truck-appointment:create','创建拖车预约',NULL,'POST',20,'ACTIVE',1,0),
|
||
(5102153,0,'PLATFORM','BUTTON',5102150,'platform:firstleg:truck-appointment:edit','编辑拖车预约',NULL,'PUT',30,'ACTIVE',1,0),
|
||
(5102154,0,'PLATFORM','BUTTON',5102150,'platform:firstleg:truck-appointment:operate','操作拖车预约',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:truck-appointment:%'
|
||
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR');
|
||
|
||
USE baiyue_firstleg;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_service_center_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_service_center_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema='baiyue_firstleg'
|
||
AND table_name IN ('fl_ocean_booking','fl_ocean_booking_party',
|
||
'fl_ocean_booking_container_requirement','fl_ocean_booking_container_allocation',
|
||
'fl_ocean_booking_status_history','fl_ocean_booking_operation',
|
||
'fl_truck_appointment','fl_truck_appointment_history',
|
||
'fl_truck_appointment_attachment','fl_truck_appointment_operation')) <> 10 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:订舱或拖车预约表未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id=0 AND app_code='PLATFORM' AND id BETWEEN 5102140 AND 5102149
|
||
AND permission_code LIKE 'platform:firstleg:ocean-booking:%') <> 10
|
||
OR (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id=0 AND app_code='PLATFORM' AND id BETWEEN 5102150 AND 5102154
|
||
AND permission_code LIKE 'platform:firstleg:truck-appointment:%') <> 5 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:订舱或拖车预约权限未完整写入';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT CONCAT(r.role_code, '|', p.permission_code))
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id=rp.role_id AND r.tenant_id=rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
|
||
WHERE r.tenant_id=0 AND r.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
|
||
AND p.permission_code LIKE 'platform:firstleg:ocean-booking:%') <> 20
|
||
OR (SELECT COUNT(DISTINCT CONCAT(r.role_code, '|', p.permission_code))
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id=rp.role_id AND r.tenant_id=rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
|
||
WHERE r.tenant_id=0 AND r.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
|
||
AND p.permission_code LIKE 'platform:firstleg:truck-appointment:%') <> 10 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:订舱或拖车预约角色授权未完整写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_service_center_schema();
|
||
DROP PROCEDURE IF EXISTS assert_phase3_service_center_schema;
|
||
|
||
|
||
-- Phase 3 domestic outbound aggregate and dispatch gate.
|
||
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;
|
||
|
||
-- Phase 3 independent port and sailing schedule observations.
|
||
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;
|
||
|
||
-- Phase 3 customs document, supplementation, broker submission and receipt lifecycle.
|
||
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;
|
||
|
||
-- Phase 3 loading evidence hardening for existing databases. Evidence is append-only:
|
||
-- multiple active/void records of the same type are allowed; voiding is an audit action.
|
||
USE baiyue_firstleg;
|
||
DROP PROCEDURE IF EXISTS phase3_loading_evidence_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_loading_evidence_add_column(IN p_column VARCHAR(64), IN p_definition VARCHAR(255))
|
||
BEGIN
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.tables
|
||
WHERE table_schema=DATABASE() AND table_name='fl_loading_evidence')
|
||
AND NOT EXISTS(SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema=DATABASE() AND table_name='fl_loading_evidence' AND column_name=p_column),
|
||
CONCAT('ALTER TABLE fl_loading_evidence ADD COLUMN `', p_column, '` ', p_definition),
|
||
'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_loading_evidence_add_column('status', 'VARCHAR(16) NOT NULL DEFAULT ''ACTIVE'' COMMENT ''ACTIVE/VOID''');
|
||
CALL phase3_loading_evidence_add_column('void_reason', 'VARCHAR(500) NULL');
|
||
CALL phase3_loading_evidence_add_column('voided_by', 'BIGINT UNSIGNED NULL');
|
||
CALL phase3_loading_evidence_add_column('voided_by_name', 'VARCHAR(64) NULL');
|
||
CALL phase3_loading_evidence_add_column('voided_at', 'DATETIME(3) NULL');
|
||
DROP PROCEDURE IF EXISTS phase3_loading_evidence_add_column;
|
||
|
||
UPDATE fl_loading_evidence SET status='ACTIVE' WHERE status IS NULL OR status='';
|
||
UPDATE fl_loading_evidence SET seal_no=NULL WHERE evidence_type<>'SEAL' AND seal_no IS NOT NULL;
|
||
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema=DATABASE() AND table_name='fl_loading_evidence'
|
||
AND index_name='uk_loading_evidence_type'),
|
||
'ALTER TABLE fl_loading_evidence DROP INDEX uk_loading_evidence_type', 'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
SET @ddl = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema=DATABASE() AND table_name='fl_loading_evidence'
|
||
AND index_name='uk_loading_evidence_seal'),
|
||
'ALTER TABLE fl_loading_evidence DROP INDEX uk_loading_evidence_seal', 'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
SET @ddl = IF(
|
||
NOT EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema=DATABASE() AND table_name='fl_loading_evidence'
|
||
AND index_name='idx_loading_evidence_type'),
|
||
'ALTER TABLE fl_loading_evidence ADD KEY idx_loading_evidence_type (tenant_id,inbound_id,evidence_type,status)',
|
||
'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
SET @ddl = IF(
|
||
NOT EXISTS(SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema=DATABASE() AND table_name='fl_loading_evidence'
|
||
AND index_name='idx_loading_evidence_seal'),
|
||
'ALTER TABLE fl_loading_evidence ADD KEY idx_loading_evidence_seal (tenant_id,seal_no,status)',
|
||
'SELECT 1');
|
||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||
|
||
/* Phase 3: link inbound preload plans to one or more container loading plans. */
|
||
CREATE TABLE IF NOT EXISTS fl_preload_loading_plan_link (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
inbound_id BIGINT UNSIGNED NOT NULL,
|
||
preload_plan_id BIGINT UNSIGNED NOT NULL,
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL,
|
||
idempotency_key VARCHAR(128) NOT NULL,
|
||
request_hash CHAR(64) NOT NULL,
|
||
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_preload_loading_pair (tenant_id, inbound_id, loading_plan_id),
|
||
UNIQUE KEY uk_preload_loading_idempotency (created_by, idempotency_key),
|
||
KEY idx_preload_loading_preload (preload_plan_id, loading_plan_id),
|
||
KEY idx_preload_loading_plan (loading_plan_id, tenant_id, inbound_id),
|
||
CONSTRAINT fk_preload_loading_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
|
||
CONSTRAINT fk_preload_loading_preload FOREIGN KEY (preload_plan_id) REFERENCES fl_preload_plan (id),
|
||
CONSTRAINT fk_preload_loading_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT chk_preload_loading_tenant CHECK (tenant_id > 0)
|
||
) ENGINE=InnoDB COMMENT='入库单预装与柜次装柜计划多对多关联';
|
||
|
||
INSERT IGNORE INTO fl_preload_loading_plan_link
|
||
(id, tenant_id, inbound_id, preload_plan_id, loading_plan_id, idempotency_key,
|
||
request_hash, created_by, created_by_name, created_at)
|
||
SELECT CAST(CONV(SUBSTRING(SHA2(CONCAT('PRELOAD_LOADING|', item.account_id, '|', item.inbound_id,
|
||
'|', item.loading_plan_id), 256), 1, 15), 16, 10) AS UNSIGNED),
|
||
item.account_id, item.inbound_id, preload.id, item.loading_plan_id,
|
||
CONCAT('BACKFILL-', item.loading_plan_id, '-', item.inbound_id),
|
||
SHA2(CONCAT('BACKFILL|', item.account_id, '|', item.inbound_id, '|', item.loading_plan_id), 256),
|
||
preload.created_by, preload.created_by_name, LEAST(preload.created_at, plan.created_at)
|
||
FROM (SELECT DISTINCT account_id, inbound_id, loading_plan_id
|
||
FROM fl_loading_plan_item WHERE deleted = 0) item
|
||
JOIN fl_preload_plan preload ON preload.tenant_id = item.account_id
|
||
AND preload.inbound_id = item.inbound_id
|
||
JOIN fl_loading_plan plan ON plan.id = item.loading_plan_id;
|
||
|
||
/* Phase 3: PDA assignment, immutable task operations and review records. */
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_assignment (
|
||
pick_task_id BIGINT UNSIGNED NOT NULL,
|
||
assignee_id BIGINT UNSIGNED NOT NULL,
|
||
assignee_name VARCHAR(64) NOT NULL,
|
||
assigned_by BIGINT UNSIGNED NOT NULL,
|
||
assigned_by_name VARCHAR(64) NULL,
|
||
assignment_type VARCHAR(16) NOT NULL,
|
||
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||
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 (pick_task_id),
|
||
KEY idx_loading_pick_assignee (assignee_id, updated_at),
|
||
CONSTRAINT fk_loading_pick_assignment_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT chk_loading_pick_assignment_type CHECK (assignment_type IN ('CLAIMED', 'ASSIGNED'))
|
||
) ENGINE=InnoDB COMMENT='柜次PDA任务领取与作业员分配';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_review (
|
||
pick_task_id BIGINT UNSIGNED NOT NULL,
|
||
review_status VARCHAR(16) NOT NULL DEFAULT 'APPROVED',
|
||
reviewed_by BIGINT UNSIGNED NOT NULL,
|
||
reviewed_by_name VARCHAR(64) NULL,
|
||
remark VARCHAR(500) NULL,
|
||
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||
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 (pick_task_id),
|
||
KEY idx_loading_pick_reviewer (reviewed_by, created_at),
|
||
CONSTRAINT fk_loading_pick_review_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT chk_loading_pick_review_status CHECK (review_status = 'APPROVED')
|
||
) ENGINE=InnoDB COMMENT='柜次PDA任务完成复核';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_operation (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
pick_task_id BIGINT UNSIGNED NOT NULL,
|
||
operation_id VARCHAR(128) NOT NULL,
|
||
request_hash CHAR(64) NOT NULL,
|
||
action_type VARCHAR(16) 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),
|
||
UNIQUE KEY uk_loading_pick_operation_id (operation_id),
|
||
KEY idx_loading_pick_operation_task (pick_task_id, created_at),
|
||
CONSTRAINT fk_loading_pick_operation_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT chk_loading_pick_operation_action CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW'))
|
||
) ENGINE=InnoDB COMMENT='柜次PDA任务级幂等审计';
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_review_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_review_before_update BEFORE UPDATE ON fl_loading_pick_review
|
||
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA复核记录不可修改'; END$$
|
||
DELIMITER ;
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_review_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_review_before_delete BEFORE DELETE ON fl_loading_pick_review
|
||
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA复核记录不可删除'; END$$
|
||
DELIMITER ;
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_operation_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_operation_before_update BEFORE UPDATE ON fl_loading_pick_operation
|
||
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA任务操作历史不可修改'; END$$
|
||
DELIMITER ;
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_operation_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_operation_before_delete BEFORE DELETE ON fl_loading_pick_operation
|
||
FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA任务操作历史不可删除'; END$$
|
||
DELIMITER ;
|
||
|
||
USE baiyue_iam;
|
||
DROP PROCEDURE IF EXISTS assert_phase3_pda_permission_ids;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_pda_permission_ids()
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM iam_permission WHERE id BETWEEN 5102190 AND 5102193
|
||
AND permission_code NOT IN ('platform:firstleg:pda:view','platform:firstleg:pda:assign',
|
||
'platform:firstleg:pda:work','platform:firstleg:pda:review')) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA权限ID已被其他资源占用';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_pda_permission_ids();
|
||
DROP PROCEDURE IF EXISTS assert_phase3_pda_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
|
||
(5102190,0,'PLATFORM','MENU',5102094,'platform:firstleg:pda:view','PDA装柜拣货','/first-leg/pda-loading','GET',10,'ACTIVE',1,0),
|
||
(5102191,0,'PLATFORM','BUTTON',5102190,'platform:firstleg:pda:assign','分配PDA任务',NULL,'POST',20,'ACTIVE',1,0),
|
||
(5102192,0,'PLATFORM','BUTTON',5102190,'platform:firstleg:pda:work','执行PDA拣货',NULL,'POST',30,'ACTIVE',1,0),
|
||
(5102193,0,'PLATFORM','BUTTON',5102190,'platform:firstleg:pda:review','复核PDA任务',NULL,'POST',40,'ACTIVE',1,0)
|
||
ON DUPLICATE KEY UPDATE 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 r.id,p.id,0 FROM iam_role r JOIN iam_permission p ON p.id BETWEEN 5102190 AND 5102193
|
||
WHERE r.tenant_id=0 AND r.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR') AND p.tenant_id=0 AND p.deleted=0;
|
||
USE baiyue_firstleg;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_final_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_final_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema='baiyue_firstleg' AND table_name IN (
|
||
'fl_outbound_dispatch','fl_outbound_dispatch_history','fl_outbound_dispatch_operation',
|
||
'fl_customs_checklist_version','fl_customs_case','fl_customs_document','fl_customs_supplement_request',
|
||
'fl_customs_submission_batch','fl_customs_receipt','fl_customs_history','fl_customs_operation',
|
||
'fl_port_schedule','fl_port_schedule_history','fl_port_schedule_operation',
|
||
'fl_preload_loading_plan_link','fl_loading_pick_assignment','fl_loading_pick_review',
|
||
'fl_loading_pick_operation')) <> 18 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:三期出仓、报关或船期表未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_evidence'
|
||
AND column_name IN ('status','void_reason','voided_by','voided_by_name','voided_at')) <> 5
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_evidence'
|
||
AND index_name='uk_loading_evidence_type') <> 0
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_evidence'
|
||
AND index_name='uk_loading_evidence_seal') <> 0
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_evidence'
|
||
AND index_name='idx_loading_evidence_type' AND non_unique=1
|
||
AND ((seq_in_index=1 AND column_name='tenant_id')
|
||
OR (seq_in_index=2 AND column_name='inbound_id')
|
||
OR (seq_in_index=3 AND column_name='evidence_type')
|
||
OR (seq_in_index=4 AND column_name='status'))) <> 4
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_evidence'
|
||
AND index_name='idx_loading_evidence_seal' AND non_unique=1
|
||
AND ((seq_in_index=1 AND column_name='tenant_id')
|
||
OR (seq_in_index=2 AND column_name='seal_no')
|
||
OR (seq_in_index=3 AND column_name='status'))) <> 3
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_outbound_dispatch'
|
||
AND index_name='uk_outbound_dispatch_owner' AND non_unique=0) <> 3
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_customs_case'
|
||
AND index_name='uk_customs_case_scope' AND non_unique=0) <> 4
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_port_schedule'
|
||
AND index_name='uk_port_schedule_source' AND non_unique=0) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:三期关键唯一索引未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.referential_constraints
|
||
WHERE constraint_schema='baiyue_firstleg' AND constraint_name IN (
|
||
'fk_outbound_dispatch_inbound','fk_outbound_dispatch_container','fk_customs_case_checklist',
|
||
'fk_customs_document_case','fk_customs_supplement_case','fk_customs_batch_case',
|
||
'fk_customs_receipt_case','fk_customs_receipt_batch','fk_customs_history_case',
|
||
'fk_port_schedule_booking','fk_port_schedule_container','fk_port_schedule_history',
|
||
'fk_port_schedule_operation','fk_preload_loading_inbound','fk_preload_loading_preload',
|
||
'fk_preload_loading_plan','fk_loading_pick_assignment_task','fk_loading_pick_review_task',
|
||
'fk_loading_pick_operation_task')) <> 19 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:三期关键外键未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.triggers
|
||
WHERE trigger_schema='baiyue_firstleg' AND trigger_name IN (
|
||
'trg_port_schedule_before_insert','trg_port_schedule_before_update',
|
||
'trg_port_schedule_history_before_update','trg_port_schedule_history_before_delete',
|
||
'trg_loading_pick_review_before_update','trg_loading_pick_review_before_delete',
|
||
'trg_loading_pick_operation_before_update','trg_loading_pick_operation_before_delete')) <> 8 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:港务船期归属或历史保护触发器未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND app_code='PLATFORM'
|
||
AND id BETWEEN 5102160 AND 5102164 AND permission_code LIKE 'platform:firstleg:outbound-dispatch:%') <> 5
|
||
OR (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND app_code='PLATFORM'
|
||
AND id BETWEEN 5102170 AND 5102175 AND permission_code LIKE 'platform:firstleg:customs:%') <> 6
|
||
OR (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND app_code='PLATFORM'
|
||
AND id BETWEEN 5102180 AND 5102183 AND permission_code LIKE 'platform:firstleg:port-schedule:%') <> 4 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:三期出仓、报关或船期权限未完整写入';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission WHERE tenant_id=0 AND app_code='PLATFORM'
|
||
AND id BETWEEN 5102190 AND 5102193 AND permission_code LIKE 'platform:firstleg:pda:%') <> 4 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:PDA权限未完整写入';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT CONCAT(r.role_code,'|',p.permission_code))
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id=rp.role_id AND r.tenant_id=rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id=rp.permission_id AND p.tenant_id=rp.tenant_id
|
||
WHERE r.tenant_id=0 AND r.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
|
||
AND (p.permission_code LIKE 'platform:firstleg:outbound-dispatch:%'
|
||
OR p.permission_code LIKE 'platform:firstleg:customs:%'
|
||
OR p.permission_code LIKE 'platform:firstleg:port-schedule:%'
|
||
OR p.permission_code LIKE 'platform:firstleg:pda:%')) <> 38 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='统一迁移失败:三期出仓、报关或船期角色授权未完整写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_final_schema();
|
||
DROP PROCEDURE IF EXISTS assert_phase3_final_schema;
|
||
|
||
-- PDA 部分拣货释放策略:NULL/AUTO 按比例释放,MANUAL + [] 表示人工明确不释放。
|
||
USE baiyue_firstleg;
|
||
DROP PROCEDURE IF EXISTS phase3_pda_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_pda_add_column(
|
||
IN p_table_name VARCHAR(64), IN p_column_name VARCHAR(64), IN p_definition TEXT
|
||
)
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = DATABASE() AND table_name = p_table_name AND column_name = p_column_name
|
||
) THEN
|
||
SET @phase3_pda_column_ddl = CONCAT(
|
||
'ALTER TABLE `', p_table_name, '` ADD COLUMN `', p_column_name, '` ', p_definition
|
||
);
|
||
PREPARE phase3_pda_column_stmt FROM @phase3_pda_column_ddl;
|
||
EXECUTE phase3_pda_column_stmt;
|
||
DEALLOCATE PREPARE phase3_pda_column_stmt;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_pda_add_column(
|
||
'fl_loading_pick_item', 'release_mode',
|
||
"VARCHAR(10) NULL COMMENT 'AUTO按比例释放,MANUAL按人工选择释放' AFTER item_status"
|
||
);
|
||
CALL phase3_pda_add_column(
|
||
'fl_loading_pick_item', 'release_location_ids',
|
||
"JSON NULL COMMENT '人工选择释放库位ID;空数组表示明确释放0个' AFTER release_mode"
|
||
);
|
||
CALL phase3_pda_add_column(
|
||
'fl_inbound_pallet_allocation', 'reservation_id',
|
||
"BIGINT UNSIGNED NULL COMMENT '仓库预占资源唯一ID,A0共享场景用于区分托盘' AFTER location_code"
|
||
);
|
||
DROP PROCEDURE IF EXISTS phase3_pda_add_column;
|
||
-- PDA 多人协作:领取粒度由整柜任务下沉为单条拣货明细。
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_item_assignment (
|
||
pick_item_id BIGINT UNSIGNED NOT NULL,
|
||
assignee_id BIGINT UNSIGNED NOT NULL,
|
||
assignee_name VARCHAR(64) NOT NULL,
|
||
assigned_by BIGINT UNSIGNED NOT NULL,
|
||
assigned_by_name VARCHAR(64) NULL,
|
||
assignment_type VARCHAR(16) NOT NULL COMMENT 'CLAIMED/ASSIGNED',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||
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 (pick_item_id),
|
||
KEY idx_loading_pick_item_assignee (assignee_id, updated_at),
|
||
CONSTRAINT fk_loading_pick_item_assignment_item FOREIGN KEY (pick_item_id) REFERENCES fl_loading_pick_item (id),
|
||
CONSTRAINT chk_loading_pick_item_assignment_type CHECK (assignment_type IN ('CLAIMED', 'ASSIGNED'))
|
||
) ENGINE=InnoDB COMMENT='PDA拣货明细领取与作业员分配';
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_pda_replace_operation_check;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_pda_replace_operation_check()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.table_constraints
|
||
WHERE constraint_schema = DATABASE()
|
||
AND table_name = 'fl_loading_pick_operation'
|
||
AND constraint_name = 'chk_loading_pick_operation_action'
|
||
AND constraint_type = 'CHECK'
|
||
) THEN
|
||
ALTER TABLE fl_loading_pick_operation DROP CHECK chk_loading_pick_operation_action;
|
||
END IF;
|
||
ALTER TABLE fl_loading_pick_operation
|
||
ADD CONSTRAINT chk_loading_pick_operation_action
|
||
CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW'));
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_pda_replace_operation_check();
|
||
DROP PROCEDURE IF EXISTS phase3_pda_replace_operation_check;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_pda_item_assignment_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_pda_item_assignment_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_pick_item_assignment') <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_pick_item_assignment'
|
||
AND index_name='idx_loading_pick_item_assignee') <> 2
|
||
OR (SELECT COUNT(*) FROM information_schema.referential_constraints
|
||
WHERE constraint_schema='baiyue_firstleg'
|
||
AND constraint_name='fk_loading_pick_item_assignment_item') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA明细级多人分配表创建失败';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_pda_item_assignment_schema();
|
||
DROP PROCEDURE IF EXISTS assert_phase3_pda_item_assignment_schema;
|
||
|
||
-- 来源:firstleg/src/main/resources/db/phase3-carrier-schedule-query-schema.sql
|
||
SET NAMES utf8mb4;
|
||
USE baiyue_firstleg;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query (
|
||
id BIGINT UNSIGNED NOT NULL,
|
||
query_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,
|
||
container_scope_id BIGINT UNSIGNED GENERATED ALWAYS AS (COALESCE(container_booking_id,0)) STORED,
|
||
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,
|
||
official_url VARCHAR(500) NOT NULL,
|
||
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,
|
||
attempt_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||
next_allowed_at DATETIME(3) NOT NULL,
|
||
last_attempt_at DATETIME(3) NULL,
|
||
last_success_at DATETIME(3) NULL,
|
||
source_updated_at DATETIME(3) NULL,
|
||
expires_at DATETIME(3) NULL,
|
||
result_port_schedule_id BIGINT UNSIGNED NULL,
|
||
last_attempt_status VARCHAR(24) NULL,
|
||
manual_reason VARCHAR(1000) NULL,
|
||
last_error_code VARCHAR(64) NULL,
|
||
last_error_message VARCHAR(1000) NULL,
|
||
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_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,
|
||
query_id BIGINT UNSIGNED NOT NULL,
|
||
window_attempt_no INT UNSIGNED NOT NULL,
|
||
carrier_code VARCHAR(16) NOT NULL,
|
||
access_mode VARCHAR(32) NOT NULL,
|
||
official_url VARCHAR(500) NOT NULL,
|
||
attempt_status VARCHAR(24) NOT NULL,
|
||
coverage_gap TINYINT(1) NOT NULL DEFAULT 0,
|
||
coverage_gap_code VARCHAR(64) NULL,
|
||
manual_reason VARCHAR(1000) NULL,
|
||
error_code VARCHAR(64) NULL,
|
||
error_message VARCHAR(1000) 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_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,
|
||
query_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_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,
|
||
query_id BIGINT UNSIGNED NULL,
|
||
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_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;
|
||
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 BETWEEN 5102184 AND 5102188
|
||
AND permission_code <> CASE id
|
||
WHEN 5102184 THEN 'platform:firstleg:port-schedule-query:list'
|
||
WHEN 5102185 THEN 'platform:firstleg:port-schedule-query:view'
|
||
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 (permission_code LIKE 'platform:firstleg:port-schedule-query:%'
|
||
AND (tenant_id<>0 OR app_code<>'PLATFORM' OR id NOT BETWEEN 5102184 AND 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),
|
||
(5102185,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:view','查看船司任务',NULL,'GET',50,'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:%') <> 5 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:%') <> 10 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;
|
||
|
||
-- 来源:iam/src/main/resources/db/phase3-warehouse-roles.sql
|
||
-- 三期仓库常用系统角色:仓库操作员、仓库拣货员、仓库理货员。
|
||
-- 本脚本只组合已经存在的权限资源,需在三期 Firstleg/IAM 权限脚本之后执行,可重复部署。
|
||
USE baiyue_iam;
|
||
SET NAMES utf8mb4;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_warehouse_role_ids_available;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_warehouse_role_ids_available()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM iam_role
|
||
WHERE (id = 4000004 AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR role_code <> 'WAREHOUSE_OPERATOR'))
|
||
OR (id = 4000005 AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR role_code <> 'WAREHOUSE_PICKER'))
|
||
OR (id = 4000006 AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR role_code <> 'WAREHOUSE_TALLY'))
|
||
OR (tenant_id = 0 AND app_code = 'PLATFORM' AND role_code = 'WAREHOUSE_OPERATOR' AND id <> 4000004)
|
||
OR (tenant_id = 0 AND app_code = 'PLATFORM' AND role_code = 'WAREHOUSE_PICKER' AND id <> 4000005)
|
||
OR (tenant_id = 0 AND app_code = 'PLATFORM' AND role_code = 'WAREHOUSE_TALLY' AND id <> 4000006)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:4000004-4000006 或角色编码已被占用';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM iam_data_scope
|
||
WHERE (id = 6000004 AND role_id <> 4000004)
|
||
OR (id = 6000005 AND role_id <> 4000005)
|
||
OR (id = 6000006 AND role_id <> 4000006)
|
||
OR (role_id = 4000004 AND id <> 6000004)
|
||
OR (role_id = 4000005 AND id <> 6000005)
|
||
OR (role_id = 4000006 AND id <> 6000006)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:6000004-6000006 数据范围ID已被占用';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_warehouse_role_ids_available();
|
||
DROP PROCEDURE assert_phase3_warehouse_role_ids_available;
|
||
|
||
INSERT INTO iam_role (
|
||
id, tenant_id, app_code, role_code, role_name, role_type, status,
|
||
description, deleted, version
|
||
) VALUES
|
||
(4000004, 0, 'PLATFORM', 'WAREHOUSE_OPERATOR', '仓库操作员', 'SYSTEM', 'ACTIVE',
|
||
'负责查看入仓业务并在后台维护装柜证据', 0, 0),
|
||
(4000005, 0, 'PLATFORM', 'WAREHOUSE_PICKER', '仓库拣货员', 'SYSTEM', 'ACTIVE',
|
||
'负责使用PDA执行柜次拣货和现场拍照', 0, 0),
|
||
(4000006, 0, 'PLATFORM', 'WAREHOUSE_TALLY', '仓库理货员', 'SYSTEM', 'ACTIVE',
|
||
'负责入库测量、PDA复核和装柜证据维护', 0, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
role_name = VALUES(role_name), role_type = 'SYSTEM', status = 'ACTIVE',
|
||
description = VALUES(description), deleted = 0;
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_phase3_warehouse_role_permission;
|
||
CREATE TEMPORARY TABLE tmp_phase3_warehouse_role_permission (
|
||
role_code VARCHAR(64) NOT NULL,
|
||
permission_code VARCHAR(128) NOT NULL,
|
||
PRIMARY KEY (role_code, permission_code)
|
||
) ENGINE=MEMORY;
|
||
|
||
-- 操作员:查看入仓业务并维护后台装柜证据,不授予完成装柜、任务分派或出仓动作。
|
||
INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VALUES
|
||
('WAREHOUSE_OPERATOR', 'platform:order'),
|
||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:list'),
|
||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:view'),
|
||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:view'),
|
||
('WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:edit');
|
||
|
||
-- 拣货员:只允许PDA任务查询、作业和现场照片,不授予后台页面、任务分派或复核权限。
|
||
INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VALUES
|
||
('WAREHOUSE_PICKER', 'platform:firstleg:pda:view'),
|
||
('WAREHOUSE_PICKER', 'platform:firstleg:pda:work');
|
||
|
||
-- 理货员:负责到仓测量、装柜证据维护和PDA复核,不允许完成装柜或确认出仓。
|
||
INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VALUES
|
||
('WAREHOUSE_TALLY', 'platform:order'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:list'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:view'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:measure:view'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:measure:edit'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:measure:submit'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:weight:edit'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:weight:confirm'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:pda:view'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:pda:review'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:view'),
|
||
('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit');
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_warehouse_role_permissions_available;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_warehouse_role_permissions_available()
|
||
BEGIN
|
||
DECLARE operator_permission_count INT DEFAULT 0;
|
||
DECLARE picker_permission_count INT DEFAULT 0;
|
||
DECLARE tally_permission_count INT DEFAULT 0;
|
||
SELECT COALESCE(SUM(role_code = 'WAREHOUSE_OPERATOR'), 0),
|
||
COALESCE(SUM(role_code = 'WAREHOUSE_PICKER'), 0),
|
||
COALESCE(SUM(role_code = 'WAREHOUSE_TALLY'), 0)
|
||
INTO operator_permission_count, picker_permission_count, tally_permission_count
|
||
FROM tmp_phase3_warehouse_role_permission;
|
||
IF operator_permission_count <> 5
|
||
OR picker_permission_count <> 2
|
||
OR tally_permission_count <> 12 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:角色权限组合数量不符合预期';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM tmp_phase3_warehouse_role_permission expected
|
||
LEFT JOIN iam_permission permission
|
||
ON permission.tenant_id = 0
|
||
AND permission.app_code = 'PLATFORM'
|
||
AND permission.permission_code = expected.permission_code
|
||
AND permission.status = 'ACTIVE'
|
||
AND permission.deleted = 0
|
||
WHERE permission.id IS NULL
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:依赖的三期平台权限尚未完整部署';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_warehouse_role_permissions_available();
|
||
DROP PROCEDURE assert_phase3_warehouse_role_permissions_available;
|
||
|
||
-- 三个系统角色的权限集合由迁移维护。重复执行时移除历史版本或人工误授的越权项,
|
||
-- 避免最小权限角色因脚本升级只增不减。
|
||
DELETE role_permission
|
||
FROM iam_role_permission role_permission
|
||
JOIN iam_role role
|
||
ON role.id = role_permission.role_id
|
||
AND role.tenant_id = role_permission.tenant_id
|
||
JOIN iam_permission permission
|
||
ON permission.id = role_permission.permission_id
|
||
AND permission.tenant_id = role_permission.tenant_id
|
||
LEFT JOIN tmp_phase3_warehouse_role_permission expected
|
||
ON expected.role_code = role.role_code
|
||
AND expected.permission_code = permission.permission_code
|
||
WHERE role.tenant_id = 0
|
||
AND role.app_code = 'PLATFORM'
|
||
AND role.role_code IN ('WAREHOUSE_OPERATOR', 'WAREHOUSE_PICKER', 'WAREHOUSE_TALLY')
|
||
AND expected.permission_code IS NULL;
|
||
|
||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT role.id, permission.id, 0
|
||
FROM tmp_phase3_warehouse_role_permission expected
|
||
JOIN iam_role role
|
||
ON role.tenant_id = 0
|
||
AND role.app_code = 'PLATFORM'
|
||
AND role.role_code = expected.role_code
|
||
AND role.status = 'ACTIVE'
|
||
AND role.deleted = 0
|
||
JOIN iam_permission permission
|
||
ON permission.tenant_id = 0
|
||
AND permission.app_code = 'PLATFORM'
|
||
AND permission.permission_code = expected.permission_code
|
||
AND permission.status = 'ACTIVE'
|
||
AND permission.deleted = 0;
|
||
|
||
INSERT INTO iam_data_scope (id, role_id, tenant_id, scope_type, scope_expression) VALUES
|
||
(6000004, 4000004, 0, 'TENANT', NULL),
|
||
(6000005, 4000005, 0, 'TENANT', NULL),
|
||
(6000006, 4000006, 0, 'TENANT', NULL)
|
||
ON DUPLICATE KEY UPDATE
|
||
tenant_id = 0, scope_type = 'TENANT', scope_expression = NULL;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_warehouse_roles_ready;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_warehouse_roles_ready()
|
||
BEGIN
|
||
IF (SELECT COUNT(*)
|
||
FROM iam_role
|
||
WHERE tenant_id = 0
|
||
AND app_code = 'PLATFORM'
|
||
AND role_type = 'SYSTEM'
|
||
AND status = 'ACTIVE'
|
||
AND deleted = 0
|
||
AND role_code IN ('WAREHOUSE_OPERATOR', 'WAREHOUSE_PICKER', 'WAREHOUSE_TALLY')) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:系统角色未完整启用';
|
||
END IF;
|
||
IF (SELECT COUNT(*)
|
||
FROM tmp_phase3_warehouse_role_permission expected
|
||
JOIN iam_role role
|
||
ON role.tenant_id = 0
|
||
AND role.app_code = 'PLATFORM'
|
||
AND role.role_code = expected.role_code
|
||
JOIN iam_permission permission
|
||
ON permission.tenant_id = 0
|
||
AND permission.app_code = 'PLATFORM'
|
||
AND permission.permission_code = expected.permission_code
|
||
JOIN iam_role_permission grant_row
|
||
ON grant_row.role_id = role.id
|
||
AND grant_row.permission_id = permission.id
|
||
AND grant_row.tenant_id = 0) <> 19 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:最小权限授权未完整写入';
|
||
END IF;
|
||
IF (SELECT COUNT(*)
|
||
FROM iam_data_scope scope_row
|
||
JOIN iam_role role ON role.id = scope_row.role_id
|
||
WHERE role.tenant_id = 0
|
||
AND role.app_code = 'PLATFORM'
|
||
AND role.role_code IN ('WAREHOUSE_OPERATOR', 'WAREHOUSE_PICKER', 'WAREHOUSE_TALLY')
|
||
AND scope_row.tenant_id = 0
|
||
AND scope_row.scope_type = 'TENANT') <> 3 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '仓库角色初始化失败:数据范围未完整写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_warehouse_roles_ready();
|
||
DROP PROCEDURE assert_phase3_warehouse_roles_ready;
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_phase3_warehouse_role_permission;
|
||
|
||
-- 三期统一最终总断言:稳定箱号、单箱测量、墨家收件箱、客户尺寸和装柜权限。
|
||
USE baiyue_firstleg;
|
||
DROP PROCEDURE IF EXISTS assert_unified_final_schema;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_unified_final_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_carton_label') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:稳定箱号映射表未创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_measurement_item'
|
||
AND column_name IN ('box_code', 'machine_no', 'device_measured_at', 'updated_at')) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:单箱当前测量字段未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT CONCAT(table_name, '.', index_name))
|
||
FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND ((table_name = 'fl_inbound_carton_label'
|
||
AND index_name IN ('uk_carton_label_box_code', 'uk_carton_label_index'))
|
||
OR (table_name = 'fl_inbound_measurement_item'
|
||
AND index_name = 'uk_measure_box_code'))
|
||
AND non_unique = 0) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:稳定箱号或单箱测量唯一约束未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_mojia_measurement_receipt'
|
||
AND column_name IN ('id', 'tenant_id', 'subject_id', 'box_code', 'length_cm', 'width_cm',
|
||
'scanned_shipment_id', 'height_cm', 'weight_kg', 'machine_no', 'measured_at', 'image_file_id',
|
||
'status', 'match_failure_code', 'match_failure_reason', 'request_id',
|
||
'trace_id', 'created_at', 'updated_at')) <> 19 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:墨家未匹配测量收件箱字段未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_measurement_item'
|
||
AND column_name = 'scanned_shipment_id'
|
||
AND character_maximum_length = 64) <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:箱级测量实扫Shipment ID字段未创建';
|
||
END IF;
|
||
IF (SELECT character_maximum_length FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_measurement_item'
|
||
AND column_name = 'calculation_rule_version') <> 64 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:测量计算规则版本字段长度不是64';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户尺寸计算配置表未创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_customer_dimension_config'
|
||
AND index_name = 'uk_customer_dimension_config_tenant_box'
|
||
AND non_unique = 0
|
||
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
|
||
OR (seq_in_index = 2 AND column_name = 'box_type'))) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户四箱规尺寸配置唯一约束未创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_customer_dimension_config'
|
||
AND column_name IN ('id', 'tenant_id', 'box_type', 'inner_length_cm', 'inner_width_cm',
|
||
'inner_height_cm', 'edge_addition_cm', 'remark', 'initialization_key',
|
||
'created_by', 'created_at', 'updated_by', 'updated_at', 'deleted', 'version')) <> 15 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户四箱规尺寸配置字段不完整';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT tenant.id
|
||
FROM baiyue_iam.iam_tenant tenant
|
||
LEFT JOIN baiyue_firstleg.fl_customer_dimension_config config
|
||
ON config.tenant_id = tenant.id AND config.deleted = 0
|
||
WHERE tenant.tenant_type = 'MERCHANT' AND tenant.deleted = 0 AND tenant.id > 0
|
||
GROUP BY tenant.id
|
||
HAVING COUNT(config.id) <> 4
|
||
OR COUNT(DISTINCT CASE
|
||
WHEN config.box_type IN ('SMALL', 'MEDIUM', 'LARGE', 'EXTRA_LARGE')
|
||
THEN config.box_type END) <> 4
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:现有企业客户未完整持有固定四箱规尺寸配置';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE id = 5100010 AND tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND permission_code = 'platform:customer:list' AND permission_name = '企业客户'
|
||
AND status = 'ACTIVE' AND deleted = 0) <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户尺寸计算配置权限父节点企业客户不存在';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_auth' AND table_name = 'auth_account'
|
||
AND column_name = 'password_change_required') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户管理员强制改密字段未创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM' AND permission_type = 'BUTTON'
|
||
AND parent_id = 5100010 AND status = 'ACTIVE' AND deleted = 0
|
||
AND ((id = 5100016 AND permission_code = 'platform:customer:reset-password')
|
||
OR (id = 5100017 AND permission_code = 'platform:customer:delete')
|
||
OR (id = 5100018 AND permission_code = 'platform:customer:impersonate'))) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:平台客户操作权限未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT p.permission_code)
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id = rp.role_id AND r.tenant_id = rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id = rp.permission_id AND p.tenant_id = rp.tenant_id
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'PLATFORM_ADMIN'
|
||
AND p.permission_code IN ('platform:customer:reset-password',
|
||
'platform:customer:delete',
|
||
'platform:customer:impersonate')) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:平台管理员未获得客户操作权限';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM' AND permission_type = 'BUTTON'
|
||
AND parent_id = 5100010 AND status = 'ACTIVE' AND deleted = 0
|
||
AND ((id = 5102111 AND permission_code = 'firstleg:dimension-config:view')
|
||
OR (id = 5102112 AND permission_code = 'firstleg:dimension-config:edit'))) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:客户尺寸计算配置权限码、固定ID或父节点不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT p.permission_code)
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id = rp.role_id AND r.tenant_id = rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id = rp.permission_id AND p.tenant_id = rp.tenant_id
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'PLATFORM_ADMIN'
|
||
AND p.permission_code IN ('firstleg:dimension-config:view', 'firstleg:dimension-config:edit')) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:平台管理员未获得客户尺寸计算配置读写权限';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT p.permission_code)
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id = rp.role_id AND r.tenant_id = rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id = rp.permission_id AND p.tenant_id = rp.tenant_id
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'ORDER_OPERATOR'
|
||
AND p.permission_code = 'firstleg:dimension-config:view') <> 1
|
||
OR (SELECT COUNT(*)
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id = rp.role_id AND r.tenant_id = rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id = rp.permission_id AND p.tenant_id = rp.tenant_id
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'ORDER_OPERATOR'
|
||
AND p.permission_code = 'firstleg:dimension-config:edit') <> 0 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:订单运营角色的客户尺寸计算配置权限不是只读';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name IN ('loading_plan_id', 'container_no', 'task_status')) <> 3
|
||
OR (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count')) <> 0 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:智能装柜拣货任务表型不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
|
||
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count')) <> 5
|
||
OR (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
|
||
AND column_name IN ('loading_plan_id', 'container_no', 'task_status')) <> 0 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:入库汇总拣货任务表型不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND ((id = 5103040 AND permission_code = 'platform:download:list'
|
||
AND permission_name = '下载中心' AND route_path = '/downloads')
|
||
OR (id = 5103041 AND permission_code = 'platform:download:all' AND parent_id = 5103040))) <> 2
|
||
OR (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND ((id = 5103060 AND permission_code = 'platform:location:list' AND parent_id = 5103000)
|
||
OR (id = 5103061 AND permission_code = 'platform:location:create' AND parent_id = 5103060)
|
||
OR (id = 5103062 AND permission_code = 'platform:location:edit' AND parent_id = 5103060)
|
||
OR (id = 5103063 AND permission_code = 'platform:location:status' AND parent_id = 5103060)
|
||
OR (id = 5103064 AND permission_code = 'platform:location:delete' AND parent_id = 5103060))) <> 5 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:下载中心或库位权限语义未修复';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND id BETWEEN 5102121 AND 5102129
|
||
AND permission_code IN ('firstleg:inbound:split', 'firstleg:inbound:preload',
|
||
'firstleg:inbound:preload-view', 'platform:firstleg:inbound:loading:edit',
|
||
'platform:firstleg:inbound:loading:view', 'platform:firstleg:inbound:loading:complete',
|
||
'platform:firstleg:inbound:delivery:complete', 'platform:firstleg:inbound:picking:edit',
|
||
'platform:firstleg:inbound:picking:view')) <> 9 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:三期入库装柜权限 ID 未完成迁移';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT CONCAT(role.role_code, '|', permission.permission_code))
|
||
FROM baiyue_iam.iam_role_permission role_permission
|
||
JOIN baiyue_iam.iam_role role
|
||
ON role.id = role_permission.role_id AND role.tenant_id = role_permission.tenant_id
|
||
JOIN baiyue_iam.iam_permission permission
|
||
ON permission.id = role_permission.permission_id
|
||
AND permission.tenant_id = role_permission.tenant_id
|
||
WHERE role.tenant_id = 0
|
||
AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR')
|
||
AND permission.permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload',
|
||
'firstleg:inbound:preload-view', 'platform:firstleg:inbound:loading:edit',
|
||
'platform:firstleg:inbound:loading:view', 'platform:firstleg:inbound:loading:complete',
|
||
'platform:firstleg:inbound:delivery:complete', 'platform:firstleg:inbound:picking:edit',
|
||
'platform:firstleg:inbound:picking:view')) <> 18 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:三期入库装柜角色授权未完整写入';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND ((id = 5102091 AND permission_code = 'platform:firstleg:container:view' AND parent_id = 5102090)
|
||
OR (id = 5102092 AND permission_code = 'platform:firstleg:container:create' AND parent_id = 5102090)
|
||
OR (id = 5102093 AND permission_code = 'platform:firstleg:container:edit' AND parent_id = 5102090)
|
||
OR (id = 5102094 AND permission_code = 'platform:firstleg:smart-loading:list' AND parent_id = 5102000)
|
||
OR (id = 5102096 AND permission_code = 'platform:firstleg:smart-loading:create' AND parent_id = 5102094)
|
||
OR (id = 5102097 AND permission_code = 'platform:firstleg:smart-loading:assign' AND parent_id = 5102094)
|
||
OR (id = 5102098 AND permission_code = 'platform:firstleg:smart-loading:delete' AND parent_id = 5102094)
|
||
OR (id = 5102099 AND permission_code = 'platform:firstleg:smart-loading:push' AND parent_id = 5102094))) <> 8 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:智能装柜权限 ID 或父子关系不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_fee_item'
|
||
AND column_name IN ('id', 'tenant_id', 'trade_scope', 'fee_code', 'fee_type_name')) <> 5
|
||
OR (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_fee_item'
|
||
AND column_name = 'fee_code' AND is_nullable = 'NO') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:费用项目编码字段未完成兼容迁移';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_fee_item'
|
||
AND index_name = 'uk_fee_item_code' AND non_unique = 0) <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_fee_item'
|
||
AND index_name = 'uk_fee_item_code' AND non_unique = 0
|
||
AND seq_in_index = 1 AND column_name = 'fee_code') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:费用项目编码唯一索引未创建';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_firstleg.fl_fee_item WHERE tenant_id <> 0) <> 0
|
||
OR (SELECT COUNT(*) FROM baiyue_firstleg.fl_fee_item
|
||
WHERE tenant_id = 0 AND status = 'ACTIVE' AND deleted = 0
|
||
AND id BETWEEN 193000000001000001 AND 193000000001000110
|
||
AND fee_code = CONCAT('FEE_TYPE_', LPAD(id - 193000000001000000, 3, '0'))) <> 110
|
||
OR EXISTS (
|
||
SELECT 1
|
||
FROM baiyue_firstleg.fl_inbound_fee_detail detail
|
||
JOIN baiyue_firstleg.fl_fee_item item ON item.id = detail.fee_item_id
|
||
WHERE detail.tenant_id = 0 OR item.tenant_id <> 0
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:费用项目公共作用域、110条种子或客户明细引用不正确';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM' AND status = 'ACTIVE' AND deleted = 0
|
||
AND ((id = 5102070 AND permission_type = 'MENU' AND parent_id = 5103000
|
||
AND permission_code = 'firstleg:fee-item:view')
|
||
OR (id = 5102071 AND permission_type = 'BUTTON' AND parent_id = 5102070
|
||
AND permission_code = 'firstleg:fee-item:create')
|
||
OR (id = 5102072 AND permission_type = 'BUTTON' AND parent_id = 5102070
|
||
AND permission_code = 'firstleg:fee-item:edit')
|
||
OR (id = 5102073 AND permission_type = 'BUTTON' AND parent_id = 5102070
|
||
AND permission_code = 'firstleg:fee-item:delete'))) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:费用项目权限父子关系未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT p.permission_code)
|
||
FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_role r ON r.id = rp.role_id AND r.tenant_id = rp.tenant_id
|
||
JOIN baiyue_iam.iam_permission p ON p.id = rp.permission_id AND p.tenant_id = rp.tenant_id
|
||
WHERE r.tenant_id = 0 AND r.app_code = 'PLATFORM' AND r.role_code = 'PLATFORM_ADMIN'
|
||
AND p.permission_code IN ('firstleg:fee-item:view', 'firstleg:fee-item:create',
|
||
'firstleg:fee-item:edit', 'firstleg:fee-item:delete')) <> 4 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '统一迁移失败:平台管理员未获得费用项目完整授权';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_unified_final_schema();
|
||
DROP PROCEDURE IF EXISTS assert_unified_final_schema;
|
||
CALL assert_loading_pick_task_shapes(1);
|
||
DROP PROCEDURE IF EXISTS assert_loading_pick_task_shapes;
|
||
|
||
-- 二、三期对外统一使用 INBOUNDED;历史 WAIT_SPLIT 仅由查询适配为 INBOUNDED,统一迁移不改写主表或历史状态。
|
||
SELECT 'PASS' AS migration_status,
|
||
'统一增量全部执行完成;父子订单、履约分片、装柜门禁、订舱拖车、报关船期、船司查询、仓库角色、费用、公共资源及可靠审计结构校验通过' AS message;
|