- 创建统一的下载错误处理模块 (lib/download-errors.ts) - 实现13种错误类型分类和中文错误消息 - 后端统一使用 createDownloadError 创建标准化错误响应 - 前端统一使用 parseDownloadError 解析错误并显示 - 修复正式系统数据库Schema,添加产品全案相关字段和表 - 执行数据库迁移:添加 task_no, task_status, task_fetched_at 字段 - 创建 material_assets, api_keys, api_call_logs 表
5.5 KiB
5.5 KiB
阶段二实施总结 - 部门外键关联
实施日期: 2025-12-27
状态: 代码已完成,待测试
✅ 已完成的工作
1. Schema 更新
文件: prisma/schema.prisma
User 模型变更:
- ✅ 添加
departmentId字段(可选,外键) - ✅ 添加
departmentRef关联(Department模型) - ✅ 保留
department字段(向后兼容) - ✅ 添加
departmentId索引
Department 模型变更:
- ✅ 添加
users关联(User[])
影响评估:
- ✅ 低风险: 只添加字段,不删除字段
- ✅ 向后兼容: 所有现有代码继续工作
- ✅ 可选字段:
departmentId可为空,不影响现有数据
2. 代码更新(双写双读)
2.1 更新用户部门 API (app/api/admin/users/update-department/route.ts)
变更内容:
- ✅ 双写:同时更新
department(旧字段)和departmentId(新字段) - ✅ 根据部门名称查找部门ID
- ✅ 如果部门不存在,清除
departmentId
代码逻辑:
// 如果提供了部门名称,查找对应的部门ID
if (department && department.trim() !== '') {
const dept = await prisma.department.findUnique({
where: { name: department.trim() }
});
if (dept) {
updateData.departmentId = dept.id; // 新字段
} else {
updateData.departmentId = null; // 部门不存在
}
}
2.2 钉钉完善资料 API (app/api/dingtalk/complete-profile/route.ts)
变更内容:
- ✅ 双写:同时更新
department和departmentId - ✅ 根据部门名称查找部门ID
- ✅ 返回数据包含
departmentRef关联信息
2.3 用户列表 API (app/api/admin/users/route.ts)
变更内容:
- ✅ 返回
departmentId字段 - ✅ 返回
departmentRef关联信息(包含部门ID和名称) - ✅ 保留
department字段(向后兼容)
2.4 部门权限辅助函数 (lib/department-permission.ts)
新增函数:
- ✅
getUserDepartmentName(): 获取用户部门名称(双读)- 优先使用
departmentRef.name - 回退到
department字段
- 优先使用
现有函数:
- ✅ 保持不变,继续使用
department字符串参数 - ✅ 向后兼容,不影响现有功能
3. 迁移脚本
文件: scripts/migrate-department-to-id-safe.ts
功能:
- ✅ 确保所有用户部门都在 Department 表中
- ✅ 为每个用户设置
departmentId(基于department名称匹配) - ✅ 验证数据一致性
- ✅ 幂等性:可以重复执行
4. 验证脚本
文件: scripts/verify-migration.ts
阶段二验证内容:
- ✅ 检查
departmentId字段是否存在 - ✅ 验证所有有部门名称的用户都有
departmentId - ✅ 验证
departmentId指向的部门存在 - ✅ 验证
department和departmentRef.name一致
🔄 双写双读策略
双写策略
所有更新用户部门的操作都同时更新两个字段:
// 示例:更新用户部门
const updateData = {
department: departmentName, // 旧字段(向后兼容)
departmentId: departmentId, // 新字段(逐步迁移)
};
双读策略
优先使用新字段,回退到旧字段:
// 示例:获取用户部门
const departmentName = user.departmentRef?.name || user.department || null;
📋 待测试项
功能测试
- 更新用户部门(管理员操作)
- 钉钉用户完善资料(选择部门)
- 用户列表API返回新字段
- 数据迁移脚本执行
- 数据一致性验证
兼容性测试
- 旧代码(只更新
department)仍然工作 - Session 中的
department字段正常 - 权限检查逻辑正常(使用
department字符串)
数据验证
- 所有有部门名称的用户都有
departmentId departmentId指向的部门存在department和departmentRef.name一致
🚨 注意事项
1. Session 中的部门信息
当前实现:
- Session 中仍然使用
department字符串字段 - 权限检查函数使用
department字符串参数 - 不需要修改,因为权限检查函数已经支持字符串参数
未来优化(可选):
- 可以在 Session 中添加
departmentId字段 - 更新权限检查函数,优先使用
departmentId
2. 部门不存在的情况
处理逻辑:
- 如果用户有部门名称,但 Department 表中不存在该部门
departmentId设置为nulldepartment字段保留原值(向后兼容)
建议:
- 运行迁移脚本前,先运行
migrate-departments-from-users.ts - 确保所有用户部门都在 Department 表中
3. 性能考虑
当前实现:
- 更新用户部门时,需要查询 Department 表
- 用户列表查询时,包含
departmentRef关联查询
优化建议(未来):
- 添加缓存机制
- 批量查询优化
📝 下一步
-
在影子系统测试
- 运行 Schema 更新:
npx prisma db push - 运行迁移脚本:
npx tsx scripts/migrate-department-to-id-safe.ts - 运行验证脚本:
npx tsx scripts/verify-migration.ts 2 - 按照测试指南进行功能测试
- 运行 Schema 更新:
-
记录问题
- 发现问题时,记录到
docs/shadow-testing-issues.md - 记录解决方案和修复步骤
- 发现问题时,记录到
-
验证通过后
- 可以考虑实施阶段三(部门层级结构)
- 或者先部署阶段一和阶段二到正式系统
文档版本: 1.0
创建日期: 2025-12-27
最后更新: 2025-12-27