✅ 代码层已完成:
tenant_id 字段已统一tenant_id⏳ 待执行:数据库迁移
为所有业务表添加 tenant_id 字段:
# PowerShell 执行
Get-Content "d:\DEMONET\tools\sql\add_tenant_id_to_business_tables.sql" | mysql --host=localhost --user=root --password=你的密码 --database=aidopdev
# 或直接用 MySQL 客户端
mysql -u root -p aidopdev < d:\DEMONET\tools\sql\add_tenant_id_to_business_tables.sql
影响的表(13张):
HolidayMaster 是主数据表,Service 层已实现租户过滤:
# PowerShell 执行
Get-Content "d:\DEMONET\tools\sql\add_tenant_id_to_holiday_master.sql" | mysql --host=localhost --user=root --password=你的密码 --database=aidopdev
# 或直接用 MySQL 客户端
mysql -u root -p aidopdev < d:\DEMONET\tools\sql\add_tenant_id_to_holiday_master.sql
如果数据库中已有业务数据,需要为它们设置租户ID:
-- 假设默认租户ID为 1,请根据实际情况调整
-- Order 模块
UPDATE crm_seorder SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE crm_seorderentry SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ado_contract_review SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ado_contract_review_flow SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE LinkagePlan SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ProductDesign SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ProductDesignBom SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ProductDesignRouting SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ShippingPlan SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE ShippingPlanDetail SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE AsnShipperMaster SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE AsnShipperDetail SET tenant_id = 1 WHERE tenant_id IS NULL;
-- Production 模块
UPDATE ScheduleExceptionMaster SET tenant_id = 1 WHERE tenant_id IS NULL;
UPDATE HolidayMaster SET tenant_id = 1 WHERE tenant_id IS NULL;
-- 验证所有表的 tenant_id 字段
SELECT
TABLE_NAME AS 表名,
COLUMN_NAME AS 字段名,
COLUMN_TYPE AS 字段类型,
IS_NULLABLE AS 是否可空
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'aidopdev'
AND COLUMN_NAME = 'tenant_id'
AND TABLE_NAME IN (
'ado_contract_review',
'ado_contract_review_flow',
'LinkagePlan',
'ProductDesign',
'ProductDesignBom',
'ProductDesignRouting',
'crm_seorder',
'crm_seorderentry',
'ShippingPlan',
'ShippingPlanDetail',
'AsnShipperMaster',
'AsnShipperDetail',
'ScheduleExceptionMaster',
'HolidayMaster'
)
ORDER BY TABLE_NAME;
-- 验证索引
SELECT
TABLE_NAME AS 表名,
INDEX_NAME AS 索引名,
COLUMN_NAME AS 字段名
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'aidopdev'
AND COLUMN_NAME = 'tenant_id'
AND INDEX_NAME = 'idx_tenant_id'
ORDER BY TABLE_NAME;
-- 创建测试数据(假设租户1和租户2)
INSERT INTO crm_seorder (Id, bill_no, tenant_id, ...) VALUES (..., 1, ...); -- 租户1
INSERT INTO crm_seorder (Id, bill_no, tenant_id, ...) VALUES (..., 2, ...); -- 租户2
-- 使用租户1登录,应该只能看到租户1的数据
-- 使用租户2登录,应该只能看到租户2的数据
-- 检查索引是否生效
EXPLAIN SELECT * FROM crm_seorder WHERE tenant_id = 1;
-- 应该显示使用了 idx_tenant_id 索引
tools/sql/add_tenant_id_to_business_tables.sql - 主迁移脚本tools/sql/add_tenant_id_to_holiday_master.sql - HolidayMaster 专用脚本tools/fix_tenant_id_column_name.ps1 - 字段名统一工具tools/add_tenant_id_to_entities.ps1 - Entity 批量添加工具tools/TENANT_MIGRATION_REPORT.md - 完整改造报告tools/TENANT_FIELD_NAME_UNIFICATION_REPORT.md - 字段名统一报告备份数据库
mysqldump -u root -p aidopdev > aidopdev_backup_$(date +%Y%m%d).sql
生产环境执行
回滚方案 如需回滚,可以删除 tenant_id 字段:
ALTER TABLE 表名 DROP COLUMN tenant_id;
DROP INDEX idx_tenant_id ON 表名;
执行完成后,您应该看到:
tenant_id 字段idx_tenant_id 索引ERROR: Duplicate column name 'tenant_id'
解决方案: 正常,说明该表已有字段,脚本会自动跳过
ERROR: Duplicate key name 'idx_tenant_id'
解决方案: 正常,说明索引已创建,脚本会自动跳过
ERROR: Access denied
解决方案: 确保使用有 ALTER TABLE 权限的数据库用户
生成时间:2026-04-22
字段标准:tenant_id(小写下划线)
执行顺序:迁移脚本 → 数据初始化 → 验证测试