gongxiangp2/docs/stage2-implementation-summary.md
chenpei ea9076df45 feat: 实现统一的下载错误处理系统并修复产品全案数据库Schema
- 创建统一的下载错误处理模块 (lib/download-errors.ts)
- 实现13种错误类型分类和中文错误消息
- 后端统一使用 createDownloadError 创建标准化错误响应
- 前端统一使用 parseDownloadError 解析错误并显示
- 修复正式系统数据库Schema,添加产品全案相关字段和表
- 执行数据库迁移:添加 task_no, task_status, task_fetched_at 字段
- 创建 material_assets, api_keys, api_call_logs 表
2026-01-05 17:52:42 +08:00

5.5 KiB
Raw Blame History

阶段二实施总结 - 部门外键关联

实施日期: 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 设置为 null
  • department 字段保留原值(向后兼容)

建议:

  • 运行迁移脚本前,先运行 migrate-departments-from-users.ts
  • 确保所有用户部门都在 Department 表中

3. 性能考虑

当前实现:

  • 更新用户部门时,需要查询 Department 表
  • 用户列表查询时,包含 departmentRef 关联查询

优化建议(未来):

  • 添加缓存机制
  • 批量查询优化

📝 下一步

  1. 在影子系统测试

    • 运行 Schema 更新:npx prisma db push
    • 运行迁移脚本:npx tsx scripts/migrate-department-to-id-safe.ts
    • 运行验证脚本:npx tsx scripts/verify-migration.ts 2
    • 按照测试指南进行功能测试
  2. 记录问题

    • 发现问题时,记录到 docs/shadow-testing-issues.md
    • 记录解决方案和修复步骤
  3. 验证通过后

    • 可以考虑实施阶段三(部门层级结构)
    • 或者先部署阶段一和阶段二到正式系统

文档版本: 1.0
创建日期: 2025-12-27
最后更新: 2025-12-27