TENANT_MIGRATION_EXECUTION_GUIDE.md 6.0 KB

多租户改造执行指南

🎯 当前状态

代码层已完成:

  • Entity 层:14个实体的 tenant_id 字段已统一
  • Service 层:关键服务的租户过滤已实现
  • SQL 查询:字段名已统一为 tenant_id

待执行:数据库迁移

📋 执行步骤

步骤 1:执行主迁移脚本(必需)

为所有业务表添加 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张):

  • ado_contract_review
  • ado_contract_review_flow
  • LinkagePlan
  • ProductDesign
  • ProductDesignBom
  • ProductDesignRouting
  • crm_seorder(已有字段,脚本会跳过)
  • crm_seorderentry
  • ShippingPlan
  • ShippingPlanDetail
  • AsnShipperMaster
  • AsnShipperDetail
  • ScheduleExceptionMaster

步骤 2:添加 HolidayMaster 表字段(推荐)

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

步骤 3:为现有数据设置租户ID(如有历史数据)

如果数据库中已有业务数据,需要为它们设置租户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;

步骤 4:验证迁移结果

-- 验证所有表的 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. 数据隔离测试

-- 创建测试数据(假设租户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的数据

3. 性能测试

-- 检查索引是否生效
EXPLAIN SELECT * FROM crm_seorder WHERE tenant_id = 1;
-- 应该显示使用了 idx_tenant_id 索引

📁 相关文件

SQL 脚本

  • tools/sql/add_tenant_id_to_business_tables.sql - 主迁移脚本
  • tools/sql/add_tenant_id_to_holiday_master.sql - HolidayMaster 专用脚本

PowerShell 工具

  • 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 - 字段名统一报告

⚠️ 注意事项

  1. 备份数据库

    mysqldump -u root -p aidopdev > aidopdev_backup_$(date +%Y%m%d).sql
    
  2. 生产环境执行

    • 建议在低峰期执行
    • 先在测试环境验证
    • 通知相关人员
  3. 回滚方案 如需回滚,可以删除 tenant_id 字段:

    ALTER TABLE 表名 DROP COLUMN tenant_id;
    DROP INDEX idx_tenant_id ON 表名;
    

🎉 完成标志

执行完成后,您应该看到:

  • ✅ 所有业务表都有 tenant_id 字段
  • ✅ 所有表都有 idx_tenant_id 索引
  • ✅ 现有数据(如有)已设置租户ID
  • ✅ 功能测试通过,数据正确隔离

📞 问题排查

问题1:字段已存在

ERROR: Duplicate column name 'tenant_id'

解决方案: 正常,说明该表已有字段,脚本会自动跳过

问题2:索引已存在

ERROR: Duplicate key name 'idx_tenant_id'

解决方案: 正常,说明索引已创建,脚本会自动跳过

问题3:权限不足

ERROR: Access denied

解决方案: 确保使用有 ALTER TABLE 权限的数据库用户


生成时间:2026-04-22
字段标准:tenant_id(小写下划线)
执行顺序:迁移脚本 → 数据初始化 → 验证测试