2026-08-05 10:35:07 +08:00
-- 柏悦头程一期、二期数据库统一增量脚本
-- 固定发布入口:后续数据库变更只在本文件末尾按日期追加,不再新增部署入口脚本。
-- 适用: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' ;
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 '令牌版本,递增后可使历史令牌失效' ,
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 = '统一登录账号表:保存平台端和商户端共用的认证凭据及账号安全状态' ;
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 '租户内唯一订单号' ,
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 '买单或退税' ,
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 ),
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 '品牌' ,
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) ' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库固定使用 tenant_id=0' ,
warehouse_code VARCHAR ( 50 ) NOT NULL COMMENT '平台内唯一仓库编码' ,
2026-08-05 10:35:07 +08:00
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 )
2026-08-05 16:26:10 +08:00
) ENGINE = InnoDB COMMENT = '平台公共仓库主数据表:统一归属平台作用域 tenant_id=0' ;
2026-08-05 10:35:07 +08:00
CREATE TABLE IF NOT EXISTS wh_warehouse_address (
id BIGINT UNSIGNED NOT NULL COMMENT '仓库地址ID' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库地址固定使用 tenant_id=0' ,
2026-08-05 10:35:07 +08:00
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' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库服务能力固定使用 tenant_id=0' ,
2026-08-05 10:35:07 +08:00
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 )
2026-08-05 16:26:10 +08:00
) ENGINE = InnoDB COMMENT = '平台统一维护、供全部客户共享的往来单位主数据' ;
2026-08-05 10:35:07 +08:00
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 )
2026-08-05 16:26:10 +08:00
) ENGINE = InnoDB COMMENT = '平台共享往来单位稳定业务类型' ;
2026-08-05 10:35:07 +08:00
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 )
2026-08-05 16:26:10 +08:00
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' ;
2026-08-05 10:35:07 +08:00
INSERT INTO wh_partner_type ( partner_id , tenant_id , data_scope_id , partner_type )
VALUES
2026-08-05 16:26:10 +08:00
( 191000000000000001 , 0 , 0 , 'CUSTOMER' ),
( 191000000000000001 , 0 , 0 , 'SHIPPER' )
2026-08-05 10:35:07 +08:00
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' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共快递公司固定使用 tenant_id=0' ,
company_code VARCHAR ( 50 ) NOT NULL COMMENT '平台内唯一快递公司编码' ,
2026-08-05 10:35:07 +08:00
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 )
2026-08-05 16:26:10 +08:00
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '平台公共快递公司主数据,固定归属 tenant_id=0' ;
2026-08-05 10:35:07 +08:00
-- ============================================================================
-- 来源: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 ),
( 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 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:customer:edit' , 'platform:customer:status' , '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'
)
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 , 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'" );
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' );
2026-08-05 16:26:10 +08:00
-- 平台公共仓储主数据和平台往来单位统一归平台作用域 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 ;
2026-08-05 10:35:07 +08:00
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) ' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共托盘固定使用 tenant_id=0' ,
2026-08-05 10:35:07 +08:00
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID' ,
2026-08-05 16:26:10 +08:00
pallet_code VARCHAR ( 50 ) NOT NULL COMMENT '平台仓库内唯一托盘编码' ,
2026-08-05 10:35:07 +08:00
pallet_type VARCHAR ( 32 ) NOT NULL COMMENT '托盘类型' ,
pallet_name VARCHAR ( 100 ) NULL COMMENT '托盘名称' ,
capacity_cbm DECIMAL ( 18 , 6 ) NOT NULL COMMENT '托盘可用容积CBM' ,
2026-08-06 15:32:11 +08:00
weight_kg DECIMAL ( 18 , 3 ) NOT NULL DEFAULT 0 COMMENT '托盘重量kg' ,
color VARCHAR ( 32 ) NOT NULL DEFAULT '' COMMENT '托盘颜色' ,
2026-08-05 10:35:07 +08:00
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 ),
2026-08-06 15:32:11 +08:00
UNIQUE KEY uk_pallet_tenant_code ( tenant_id , pallet_code ),
2026-08-05 10:35:07 +08:00
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 = '二期托盘基础资料和物理托盘实例' ;
2026-08-06 15:32:11 +08:00
-- 存量库补齐托盘重量、颜色字段(可重复执行)。
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 ;
2026-08-05 10:35:07 +08:00
CREATE TABLE IF NOT EXISTS wh_location (
id BIGINT UNSIGNED NOT NULL COMMENT '库位ID(雪花ID) ' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共库位固定使用 tenant_id=0' ,
2026-08-05 10:35:07 +08:00
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID' ,
zone_code VARCHAR ( 50 ) NULL COMMENT '库区编码' ,
zone_name VARCHAR ( 100 ) NULL COMMENT '库区名称' ,
2026-08-05 16:26:10 +08:00
location_code VARCHAR ( 50 ) NOT NULL COMMENT '平台仓库内唯一库位编码' ,
2026-08-05 10:35:07 +08:00
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' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共库位预占固定使用 tenant_id=0' ,
reservation_no VARCHAR ( 40 ) NOT NULL COMMENT '平台内唯一预占业务号' ,
2026-08-05 10:35:07 +08:00
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' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共库位占用固定使用 tenant_id=0' ,
occupancy_no VARCHAR ( 40 ) NOT NULL COMMENT '平台内唯一占用业务号' ,
2026-08-05 10:35:07 +08:00
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 ;
2026-08-05 16:26:10 +08:00
-- 托盘、库位、预占和占用均为平台公共仓储资源,存量测试数据统一迁移到平台作用域 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 ;
2026-08-06 15:32:11 +08:00
-- 托盘编码在平台公共作用域全局唯一;包含逻辑删除记录,编码不复用。
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 ;
2026-08-05 16:26:10 +08:00
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 ;
2026-08-05 10:35:07 +08:00
-- ============================================================================
-- 来源: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 ;
-- ============================================================================
-- 来源: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 '买单或退税模式' ,
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 = '二期头程入库单聚合根和列表查询快照' ;
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 '第三方测量明细唯一号,平台手工明细可为空' ,
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 ( 32 ) NOT NULL COMMENT '体积计算和舍入规则版本' ,
source_image_file_id BIGINT UNSIGNED NULL COMMENT '测量设备图片文件ID' ,
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 '创建时间' ,
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 ),
CONSTRAINT fk_measure_item_batch FOREIGN KEY ( measurement_id ) REFERENCES fl_inbound_measurement ( 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' ,
rate_code VARCHAR ( 50 ) NOT NULL COMMENT '租户内价格编码' ,
rate_version INT UNSIGNED NOT NULL COMMENT '价格版本号' ,
origin_warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '起运仓逻辑ID' ,
origin_warehouse_name VARCHAR ( 128 ) NOT NULL COMMENT '起运仓名称快照' ,
business_type VARCHAR ( 32 ) NOT NULL COMMENT '业务或计费类型' ,
channel_code VARCHAR ( 64 ) NULL COMMENT '渠道编码' ,
carrier_id BIGINT UNSIGNED NULL COMMENT '船司逻辑ID' ,
carrier_name VARCHAR ( 128 ) NOT NULL COMMENT '船司名称快照' ,
destination_code VARCHAR ( 32 ) NOT NULL COMMENT 'FBA目的仓编码' ,
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 '固定附加费' ,
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_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_code , charge_unit , status , effective_from , effective_to )
) ENGINE = InnoDB COMMENT = '船司FBA目的仓价格版本' ;
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 NOT NULL COMMENT '客户计费规则ID' ,
charge_rule_version INT UNSIGNED NOT NULL COMMENT '客户规则版本快照' ,
carrier_rate_id BIGINT UNSIGNED NOT NULL COMMENT '船司FBA价格ID' ,
carrier_rate_version INT UNSIGNED NOT NULL COMMENT '船司价格版本快照' ,
rule_code VARCHAR ( 50 ) 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 '业务类型快照' ,
tax_mode VARCHAR ( 24 ) NULL COMMENT '包税模式快照' ,
rounding_scale TINYINT UNSIGNED NOT NULL COMMENT '计费量保留位数快照' ,
rounding_mode VARCHAR ( 24 ) NOT NULL COMMENT '舍入方式快照' ,
rule_effective_from DATETIME ( 3 ) NOT 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 NOT NULL COMMENT '船司ID快照' ,
carrier_name VARCHAR ( 128 ) NOT NULL COMMENT '船司名称快照' ,
destination_code VARCHAR ( 32 ) NOT NULL COMMENT 'FBA仓编码快照' ,
rate_effective_from DATETIME ( 3 ) NOT NULL COMMENT '价格生效开始快照' ,
rate_effective_to DATETIME ( 3 ) NULL COMMENT '价格生效结束快照' ,
charge_mode VARCHAR ( 16 ) NOT NULL COMMENT '计费口径:KG或CBM' ,
conversion_factor DECIMAL ( 18 , 6 ) NOT 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' ,
2026-08-05 16:26:10 +08:00
warehouse_resource_tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓储资源固定使用 0;tenant_id 仍是客户隔离键' ,
2026-08-05 10:35:07 +08:00
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' , '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' , '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' , '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' );
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) ' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '第三方未知/未解析主体哨兵值;不是平台客户记录或平台作用域,不得用于业务数据授权' ,
2026-08-05 10:35:07 +08:00
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 = '第三方测量开放接口原始请求、签名主体和处理结果留档' ;
2026-08-05 16:26:10 +08:00
-- 既有表也要同步未知主体 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 ;
2026-08-05 10:35:07 +08:00
-- ============================================================================
-- 来源: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' ,
2026-08-05 16:26:10 +08:00
"BIGINT UNSIGNED NULL COMMENT '平台公共仓储资源固定使用 0;tenant_id 仍是客户隔离键' AFTER warehouse_id" );
2026-08-05 10:35:07 +08:00
2026-08-05 16:26:10 +08:00
-- 仓储资源是平台公共主数据;存量分配快照统一迁移到平台作用域 0,再收口非空约束。
2026-08-05 10:35:07 +08:00
UPDATE fl_inbound_pallet_allocation
2026-08-05 16:26:10 +08:00
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
2026-08-05 10:35:07 +08:00
WHERE TABLE_SCHEMA = DATABASE () AND TABLE_NAME = 'fl_inbound_pallet_allocation'
2026-08-05 16:26:10 +08:00
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 仍是客户隔离键'" ,
2026-08-05 10:35:07 +08:00
'SELECT 1' );
PREPARE stmt FROM @ sql ; EXECUTE stmt ; DEALLOCATE PREPARE stmt ;
DROP PROCEDURE IF EXISTS phase2_fba_add_column ;
-- 旧索引可能缺少业务类型、船司和结束时间;用新索引名并存,避免破坏线上依赖。
SET @ idx_exists = ( SELECT COUNT ( * ) FROM information_schema . STATISTICS
WHERE TABLE_SCHEMA = DATABASE () AND TABLE_NAME = 'fl_carrier_fba_rate' AND INDEX_NAME = 'idx_fba_rate_unique_match' );
SET @ sql = IF ( @ idx_exists = 0 ,
'ALTER TABLE fl_carrier_fba_rate ADD INDEX idx_fba_rate_unique_match (tenant_id, origin_warehouse_id, business_type, channel_code, carrier_id, destination_code, charge_unit, status, effective_from, effective_to)' ,
'SELECT 1' );
PREPARE stmt FROM @ sql ; EXECUTE stmt ; DEALLOCATE PREPARE stmt ;
-- ============================================================================
-- 来源: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 ;
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 );
-- ============================================================================
-- 来源: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
( 5103040 , 0 , 'PLATFORM' , 'MENU' , 5103000 , 'platform:location:list' , '库位管理' ,
'/master-data/locations' , NULL , 40 , 'ACTIVE' , 1 , 0 ),
( 5103041 , 0 , 'PLATFORM' , 'BUTTON' , 5103040 , 'platform:location:create' , '新增库位' ,
NULL , NULL , 10 , 'ACTIVE' , 1 , 0 ),
( 5103042 , 0 , 'PLATFORM' , 'BUTTON' , 5103040 , 'platform:location:edit' , '编辑库位' ,
NULL , NULL , 20 , 'ACTIVE' , 1 , 0 ),
( 5103043 , 0 , 'PLATFORM' , 'BUTTON' , 5103040 , 'platform:location:status' , '启停库位' ,
NULL , NULL , 30 , 'ACTIVE' , 1 , 0 ),
( 5103044 , 0 , 'PLATFORM' , 'BUTTON' , 5103040 , '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 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 );
-- ============================================================================
-- 来源: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_firstleg' , 'fl_inbound_pallet_allocation' , 'warehouse_resource_tenant_id' );
CALL unified_assert_column ( 'baiyue_firstleg' , 'fl_inbound_measurement_item' , 'external_detail_no' );
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' , '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 <> 28 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:二期 IAM 权限资源未达到 28 项' ;
END IF ;
END $$
DELIMITER ;
CALL unified_assert_permission_count ();
DROP PROCEDURE IF EXISTS unified_assert_permission_count ;
2026-08-05 11:20:28 +08:00
-- ============================================================================
-- 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币种三字码' ,
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 NOT NULL COMMENT '结束天,含边界' ,
unit_price DECIMAL ( 18 , 6 ) NOT NULL COMMENT '仓储费标准' ,
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 , 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 >= start_day ),
CONSTRAINT chk_overseas_fee_tier_price CHECK ( unit_price > 0 )
) ENGINE = InnoDB COMMENT = '海外仓阶梯仓储费明细' ;
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 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 ( 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 ;
2026-08-05 16:26:10 +08:00
-- 海外仓阶梯费用结构校验已完成;最终 PASS 统一在脚本末尾输出。
2026-08-05 14:20:04 +08:00
-- ============================================================
-- 二期:费用项目主数据与入库单应收应付费用明细
-- ============================================================
USE baiyue_firstleg ;
CREATE TABLE IF NOT EXISTS fl_fee_item (
id BIGINT UNSIGNED NOT NULL COMMENT '费用项目ID' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '当前客户ID(客户隔离键);物理字段名沿用 tenant_id,当前不表示上级租户' ,
2026-08-05 14:20:04 +08:00
category VARCHAR ( 64 ) NOT NULL COMMENT '费用分类' ,
trade_scope VARCHAR ( 20 ) NOT NULL COMMENT 'DOMESTIC国内、INTERNATIONAL国际' ,
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 ),
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' ,
2026-08-05 16:26:10 +08:00
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '当前客户ID(客户隔离键);物理字段名沿用 tenant_id,当前不表示上级租户' ,
2026-08-05 14:20:04 +08:00
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 = '入库单应收应付费用明细' ;
2026-08-05 16:26:10 +08:00
-- 既有表同步字段注释: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 ;
2026-08-05 14:20:04 +08:00
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 );
2026-08-05 16:26:10 +08:00
-- 费用项目、入库费用明细及按钮权限结构校验延后至脚本末尾统一执行。
2026-08-05 14:20:04 +08:00
-- ============================================================
-- 可靠审计:本地 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 = '审计消息死信补偿表' ;
2026-08-05 16:26:10 +08:00
-- ============================================================
2026-08-06 11:49:51 +08:00
-- 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 );
2026-08-06 15:32:11 +08:00
-- 平台托盘初始化主数据;内部状态仅供预占/占用,不作为平台维护字段。
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 );
2026-08-06 11:49:51 +08:00
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 ;
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 ;
2026-08-05 16:26:10 +08:00
-- 全部增量完成后的最终结构强校验;只有通过此段才输出唯一 PASS
-- ============================================================
2026-08-05 20:32:43 +08:00
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" );
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` (`tenant_id`, `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 14:55:24 +08:00
-- ============================================================================
-- 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 NULL COMMENT 'File 服务逻辑文件ID' ,
file_name VARCHAR ( 255 ) NULL ,
seal_no VARCHAR ( 64 ) NULL ,
remark VARCHAR ( 1000 ) 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 ),
UNIQUE KEY uk_loading_evidence_type ( tenant_id , inbound_id , evidence_type ),
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_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 ;
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' , 5102030 ,
'firstleg:inbound:split' , '智能分柜' , NULL , NULL , 140 , 'ACTIVE' , 1 , 0 ),
( 5102092 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'firstleg:inbound:preload' , '编辑并确认预装清单' , NULL , NULL , 150 , 'ACTIVE' , 1 , 0 ),
( 5102093 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'firstleg:inbound:preload-view' , '查看预装清单' , NULL , NULL , 160 , 'ACTIVE' , 1 , 0 ),
( 5102094 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'platform:firstleg:inbound:loading:edit' , '维护装柜证据' , NULL , NULL , 170 , 'ACTIVE' , 1 , 0 ),
( 5102095 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'platform:firstleg:inbound:loading:view' , '查看装柜证据' , NULL , NULL , 180 , 'ACTIVE' , 1 , 0 ),
( 5102096 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'platform:firstleg:inbound:loading:complete' , '完成装柜' , NULL , NULL , 190 , 'ACTIVE' , 1 , 0 ),
( 5102097 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'platform:firstleg:inbound:delivery:complete' , '完成派送' , NULL , NULL , 200 , 'ACTIVE' , 1 , 0 ),
( 5102098 , 0 , 'PLATFORM' , 'BUTTON' , 5102030 ,
'platform:firstleg:inbound:picking:edit' , '执行拣货' , NULL , NULL , 210 , 'ACTIVE' , 1 , 0 ),
( 5102099 , 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 4000001 , id , 0
FROM iam_permission
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
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'
)
ON DUPLICATE KEY UPDATE tenant_id = VALUES ( tenant_id );
2026-08-05 16:26:10 +08:00
DROP PROCEDURE IF EXISTS unified_assert_final_extensions ;
DELIMITER $$
CREATE PROCEDURE unified_assert_final_extensions ()
BEGIN
2026-08-06 14:55:24 +08:00
IF ( SELECT COUNT ( * ) FROM information_schema . tables
WHERE table_schema = 'baiyue_firstleg'
AND table_name IN ( 'fl_preload_plan' , 'fl_split_operation' ,
'fl_loading_evidence' , 'fl_loading_operation' ,
'fl_loading_pick_task' )) <> 5 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_preload_plan'
AND column_name IN ( 'id' , 'tenant_id' , 'inbound_id' , 'status' , 'version' ,
'items_json' , 'remark' , 'created_by' , 'created_by_name' ,
'updated_by' , 'updated_by_name' , 'created_at' , 'updated_at' )) <> 13
OR ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_split_operation'
AND column_name IN ( 'id' , 'tenant_id' , 'inbound_id' , 'operator_id' , 'operation' ,
'idempotency_key' , 'status' , 'result_json' , 'created_at' )) <> 9
OR ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_evidence'
AND column_name IN ( 'id' , 'tenant_id' , 'inbound_id' , 'evidence_type' , 'file_id' ,
'file_name' , 'seal_no' , 'remark' , 'version' , 'created_by' ,
'created_by_name' , 'updated_by' , 'updated_by_name' ,
'created_at' , 'updated_at' )) <> 15
OR ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_operation'
AND column_name IN ( 'id' , 'tenant_id' , 'inbound_id' , 'operator_id' , 'operation' ,
'idempotency_key' , 'request_hash' , 'status' , 'result_json' ,
'created_at' , 'completed_at' )) <> 11
OR ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND column_name IN ( 'id' , 'tenant_id' , 'inbound_id' , 'status' , 'version' ,
'expected_count' , 'picked_count' , 'remark' , 'updated_by' ,
'updated_by_name' , 'created_at' , 'updated_at' )) <> 12 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:三期分柜、预装或装柜关键字段未完整创建' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg'
AND table_name IN ( 'fl_preload_plan' , 'fl_split_operation' , 'fl_loading_operation' )
AND column_name IN ( 'items_json' , 'result_json' ) AND data_type = 'json' ) <> 3
OR ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_order'
AND column_name = 'process_status' AND character_maximum_length >= 24 ) <> 1 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:三期 JSON 字段或入库状态字段类型不兼容' ;
END IF ;
IF ( SELECT COUNT ( DISTINCT CONCAT ( table_name , '.' , index_name ))
FROM information_schema . statistics
WHERE table_schema = 'baiyue_firstleg'
AND index_name IN ( 'uk_preload_tenant_inbound' , 'idx_preload_status' ,
'uk_split_operation' , 'idx_split_operation_time' ,
'uk_loading_evidence_type' , 'idx_loading_evidence_time' ,
'uk_loading_operation' , 'idx_loading_operation_time' ,
'uk_loading_pick_tenant_inbound' , 'idx_loading_pick_status' )) <> 10 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:三期分柜、预装或装柜索引未完整创建' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . table_constraints
WHERE constraint_schema = 'baiyue_firstleg' AND constraint_type = 'FOREIGN KEY'
AND table_name IN ( 'fl_preload_plan' , 'fl_split_operation' ,
'fl_loading_evidence' , 'fl_loading_operation' ,
'fl_loading_pick_task' )) <> 5 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 (
'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 = '统一迁移失败:三期分柜、预装或装柜权限未完整创建' ;
END IF ;
IF ( SELECT COUNT ( DISTINCT rp . permission_id )
FROM baiyue_iam . iam_role_permission rp
JOIN baiyue_iam . iam_permission p ON p . id = rp . permission_id
WHERE rp . role_id = 4000001 AND rp . tenant_id = 0
AND p . 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 = '统一迁移失败:平台系统管理员未获得完整三期操作权限' ;
END IF ;
2026-08-05 16:26:10 +08:00
IF ( SELECT COUNT ( * ) FROM information_schema . tables
WHERE table_schema = 'baiyue_firstleg'
AND table_name IN ( 'fl_fee_item' , 'fl_inbound_fee_detail' )) <> 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 IN ( 'fl_fee_item' , 'fl_inbound_fee_detail' )
AND column_name = 'tenant_id'
AND column_comment = '当前客户ID(客户隔离键);物理字段名沿用 tenant_id,当前不表示上级租户' ) <> 2 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:费用表 tenant_id 客户隔离语义未同步' ;
END IF ;
2026-08-05 20:32:43 +08:00
IF ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg'
AND table_name = 'fl_fee_item'
AND column_name = 'fee_code' ) <> 1
OR ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg'
AND table_name = 'fl_inbound_fee_detail'
AND column_name IN (
'source_type' , 'source_fee_id' , 'source_fee_version' , 'source_fee_no' ,
'customer_id' , 'customer_name' , 'invoice_title' ,
'chargeable_quantity' , 'charge_unit' , 'publication_status' ,
'published_by' , 'published_by_name' , 'published_at' , 'inbounded_at' ,
'settlement_included' )) <> 15 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 ) <> 2
OR ( SELECT COUNT ( * ) FROM information_schema . statistics
WHERE table_schema = 'baiyue_firstleg'
AND table_name = 'fl_inbound_fee_detail'
AND index_name = 'uk_inbound_fee_source' AND non_unique = 0 ) <> 3
OR ( SELECT COUNT ( * ) FROM information_schema . statistics
WHERE table_schema = 'baiyue_firstleg'
AND table_name = 'fl_inbound_fee_detail'
AND index_name = 'idx_inbound_fee_published' ) <> 4 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:已发布入库运费明细索引未完整创建' ;
END IF ;
2026-08-05 16:26:10 +08:00
IF ( SELECT COUNT ( DISTINCT CONCAT ( table_name , '.' , index_name ))
FROM information_schema . statistics
WHERE table_schema = 'baiyue_firstleg'
AND index_name IN ( 'idx_fee_item_query' , 'idx_fee_item_name' ,
'idx_inbound_fee_detail' , 'idx_inbound_fee_review' ,
'idx_inbound_fee_item' )) <> 5 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 status = 'ACTIVE' AND deleted = 0
AND permission_code IN (
'firstleg:fee-item:view' , 'firstleg:fee-item:create' ,
'firstleg:fee-item:edit' , 'firstleg:fee-item:delete' ,
'firstleg:inbound:fee-detail:view' , 'firstleg:inbound:fee-detail:edit' ,
'firstleg:inbound:fee-detail:delete' , 'firstleg:inbound:fee-detail:review' )) <> 8 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:费用项目或入库费用明细权限未完整创建' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . tables
WHERE table_schema IN ( 'baiyue_auth' , 'baiyue_iam' , 'baiyue_order' ,
'baiyue_firstleg' , 'baiyue_warehouse' )
AND table_name = 'sys_audit_outbox' ) <> 5 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:可靠审计 Outbox 未覆盖五个业务库' ;
END IF ;
IF ( SELECT COUNT ( DISTINCT CONCAT ( table_schema , '.' , index_name ))
FROM information_schema . statistics
WHERE table_schema IN ( 'baiyue_auth' , 'baiyue_iam' , 'baiyue_order' ,
'baiyue_firstleg' , 'baiyue_warehouse' )
AND table_name = 'sys_audit_outbox'
AND index_name IN ( 'PRIMARY' , 'idx_audit_outbox_publish' ,
'idx_audit_outbox_retention' )) <> 15 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:可靠审计 Outbox 主键或索引未完整创建' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema IN ( 'baiyue_auth' , 'baiyue_iam' , 'baiyue_order' ,
'baiyue_firstleg' , 'baiyue_warehouse' )
AND table_name = 'sys_audit_outbox'
AND column_name IN ( 'event_id' , 'payload_json' , 'status' ,
'next_retry_at' , 'lease_until' )) <> 25 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:可靠审计 Outbox 关键字段未完整创建' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . tables
WHERE table_schema = 'baiyue_audit'
AND table_name = 'sys_audit_dead_letter' ) <> 1 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:审计死信补偿表未创建' ;
END IF ;
IF ( SELECT COUNT ( DISTINCT index_name ) FROM information_schema . statistics
WHERE table_schema = 'baiyue_audit'
AND table_name = 'sys_audit_dead_letter'
AND index_name IN ( 'PRIMARY' , 'uk_audit_dead_event' , 'idx_audit_dead_retry' )) <> 3 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:审计死信补偿表主键或索引未完整创建' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_audit' AND table_name = 'sys_audit_log'
AND column_name = 'event_id' ) <> 1
OR ( SELECT COUNT ( DISTINCT index_name ) FROM information_schema . statistics
WHERE table_schema = 'baiyue_audit' AND table_name = 'sys_audit_log'
AND index_name = 'uk_audit_event_id' AND non_unique = 0 ) <> 1 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:审计消费幂等 event_id 唯一约束未就绪' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM information_schema . columns
WHERE table_schema = 'baiyue_firstleg'
AND table_name = 'fl_third_party_call_log'
AND column_name = 'tenant_id' AND column_default = '0'
AND column_comment = '第三方未知/未解析主体哨兵值;不是平台客户记录或平台作用域,不得用于业务数据授权' ) <> 1 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:第三方未知主体 tenant_id=0 哨兵语义未就绪' ;
END IF ;
IF ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_warehouse WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_warehouse_address WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_service_capability WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_express_company WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_partner
WHERE tenant_id <> 0 OR data_scope_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_partner_type
WHERE tenant_id <> 0 OR data_scope_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_pallet WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_location WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_location_reservation WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_warehouse . wh_location_occupancy WHERE tenant_id <> 0 ) > 0
OR ( SELECT COUNT ( * ) FROM baiyue_firstleg . fl_inbound_pallet_allocation
WHERE warehouse_resource_tenant_id <> 0 ) > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:平台公共仓储资源仍有 tenant_id<>0' ;
END IF ;
IF EXISTS (
SELECT 1 FROM baiyue_warehouse . wh_warehouse_address a
JOIN baiyue_warehouse . wh_warehouse w ON w . id = a . warehouse_id
WHERE a . tenant_id <> w . tenant_id
) OR EXISTS (
SELECT 1 FROM baiyue_warehouse . wh_service_capability c
JOIN baiyue_warehouse . wh_warehouse w ON w . id = c . warehouse_id
WHERE c . tenant_id <> w . tenant_id
) OR EXISTS (
SELECT 1 FROM baiyue_warehouse . wh_pallet p
JOIN baiyue_warehouse . wh_warehouse w ON w . id = p . warehouse_id
WHERE p . tenant_id <> w . tenant_id
) OR EXISTS (
SELECT 1 FROM baiyue_warehouse . wh_location l
JOIN baiyue_warehouse . wh_warehouse w ON w . id = l . warehouse_id
WHERE l . tenant_id <> w . tenant_id
) OR EXISTS (
SELECT 1 FROM baiyue_warehouse . wh_location_reservation r
JOIN baiyue_warehouse . wh_warehouse w ON w . id = r . warehouse_id
JOIN baiyue_warehouse . wh_pallet p ON p . id = r . pallet_id
JOIN baiyue_warehouse . 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 baiyue_warehouse . wh_location_occupancy o
JOIN baiyue_warehouse . wh_warehouse w ON w . id = o . warehouse_id
JOIN baiyue_warehouse . wh_pallet p ON p . id = o . pallet_id
JOIN baiyue_warehouse . wh_location l ON l . id = o . location_id
JOIN baiyue_warehouse . 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 ;
IF EXISTS ( SELECT 1 FROM baiyue_warehouse . wh_overseas_storage_fee_rule WHERE tenant_id = 0 ) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:客户费用规则缺少客户 tenant_id' ;
END IF ;
END $$
DELIMITER ;
CALL unified_assert_final_extensions ();
DROP PROCEDURE IF EXISTS unified_assert_final_extensions ;
-- tenant_id=0 在此表仅为未知/未解析第三方主体;数量可非零,但不得用于业务授权。
SELECT COUNT ( * ) AS unresolved_third_party_calls
FROM baiyue_firstleg . fl_third_party_call_log
WHERE tenant_id = 0 ;
SELECT 'PASS' AS migration_status ,
2026-08-06 15:32:11 +08:00
'统一增量全部执行完成;统一数据库脚本执行完成,三期出仓、费用、公共资源及可靠审计结构校验通过' AS message ;