5.7 KiB
5.7 KiB
钉钉组织导入 — 标记班级功能
日期: 2026-07-09 状态: 设计完成
概述
在钉钉组织架构导入抽屉(IntegrationConfig 页面)中,支持将钉钉部门标记为「班级」。导入时自动创建班级实体,部门下的教师加入班级教师关联,学生加入班级学生关联,勾选「班主任」角色的用户自动成为该班级主班主任。
前端改造
1. 树节点增强
部门节点右侧新增操作入口:
- 未标记班级的部门 → 显示
🏫 标为班级按钮 - 已标记班级的部门 → 显示
🏫 班级: XXXXX [已标记],颜色区分,点击可修改
用户节点无变化。
2. 标记班级 Modal
点击「标为班级」/「已标记」→ 弹出 Modal 表单:
| 字段 | 组件 | 说明 |
|---|---|---|
| 班级名称 | Input | 预填部门名称,可修改 |
| 班级编码 | Input | 必填,手动输入 |
| 班型 | Select | culture / professional / bootcamp / sprint |
| 开班日期 | DatePicker | 选填 |
| 结束日期 | DatePicker | 选填 |
| 最大人数 | InputNumber | 默认 0(不限制) |
| 备注 | TextArea | 选填 |
状态管理:classMarks: Record<number, ClassMarkForm>,key 为钉钉部门 ID。
3. 导入 payload 调整
点击「导入」时构建的 payload:
{
classes: [
{ deptId, name, code, classType, startDate?, endDate?, maxStudents?, notes? }
],
users: [
{
dingUserId: u.userid,
name: u.name,
mobile: u.mobile,
roleId: teacherChecks[u.userid]
? (teacherRoles[u.userid] || defaultTeacherRoleId)
: null,
dingDeptIds: u.deptIds
}
]
}
4. 状态清理
关闭抽屉时重置 classMarks。
后端改造
1. DTO 扩展
apps/server/src/sync/dto/import-users.dto.ts:
export class ImportUserItemDto {
dingUserId: string;
name: string;
mobile: string;
roleId: number | null;
dingDeptIds: number[]; // 新增
}
export class ImportClassItemDto {
deptId: number;
name: string;
code: string;
classType: string;
startDate?: string;
endDate?: string;
maxStudents?: number;
notes?: string;
}
export class ImportUsersDto {
classes?: ImportClassItemDto[]; // 新增,可选
users: ImportUserItemDto[];
}
2. 服务层逻辑
apps/server/src/sync/sync.service.ts — importDingTalkUsers 方法改造,全量事务:
1. 解析 classes[],创建 Class 实体,以 deptId → classId 建 Map
2. 遍历 users[]:
a. 检查 UserDingMapping 是否已存在 → 跳过
b. 创建 User + 角色分配 / Student(现有逻辑不变)
c. 记录 userId → dingDeptIds 映射
3. 遍历每个用户:
a. user.dingDeptIds 匹配 classes[].deptId → 找到归属班级
b. roleId = 班主任 → ClassTeacher(roleType=head_teacher),
第一个班主任设 Class.headTeacherId
c. roleId != null 且非班主任 → ClassTeacher(roleType 按角色名映射)
d. roleId = null → ClassStudent
e. 用户在多个部门 → 全部匹配到的班级都建立关联
返回结构:
{
teacherCount: number;
studentCount: number;
classCount: number; // 新增
skipped: number;
warnings: string[]; // 非致命警告
}
3. 教师角色判定
通过角色表查询:导入时勾选的用户分配指定角色后即为「老师」。所有老师在班级关联中统一写入 ClassTeacher(roleType = 'teacher'),不区分班主任/任课老师。班级对老师为多对多关系。
4. 错误场景
| 场景 | 行为 |
|---|---|
| 班级编码重复 | 事务回滚,返回 "班级编码 XX 已存在" |
| 部门标为班级但无人 | 班级正常创建(空班),warning |
| 用户在多个被标记的部门 | 同时加入所有匹配班级 |
| 无 classes 参数 | 兼容旧调用,仅导入师生,不创建班级 |
| 角色不存在 | 事务回滚,返回具体错误 |
dingDeptIds 数据来源
DingTalkService.fetchOrgTreeWithUsers 返回的 DingOrgTreeNodeWithUsers 中,每个 user 已包含钉钉 API 返回的 department 字段(用户所属部门 ID 列表)。前端 DingOrgTreeNodeExt interface 需新增 deptIds: number[] 字段。
测试策略
后端单测(apps/server/src/sync/sync.service.spec.ts)
| 用例 | 预期 |
|---|---|
| 单部门标班级 + 1班主任 + 1学生 | 班级创建,headTeacherId 正确,关联正确 |
| 单部门多班主任 | 第一个设 headTeacherId,其余进 ClassTeacher,warning |
| 用户在多个被标记部门 | 同时加入多个班级 |
| 班级编码重复 | 事务回滚,错误返回 |
| 空部门 | 班级创建,warning |
| 纯学生无老师 | 班级创建,headTeacherId=null,学生关联正确 |
| 无 classes 参数 | 向下兼容,行为不变 |
前端验证(手动 QA)
- 标记一个部门为班级 → 填表单 → 导入 → 验证班级列表、班主任、学生归属
- 标记多个部门 → 批量导入 → 每个班独立创建
- 点击已标记班级 → 修改表单 → 重新导入
- 无标记班级的普通导入 → 行为不受影响
影响范围
| 文件 | 改动 |
|---|---|
apps/admin/src/pages/IntegrationConfig/index.tsx |
树节点、Modal、import payload |
apps/server/src/sync/dto/import-users.dto.ts |
新增 ImportClassItemDto,扩展 ImportUserItemDto |
apps/server/src/sync/sync.service.ts |
importDingTalkUsers 改造 |
apps/server/src/sync/sync.service.spec.ts |
新增测试用例 |
apps/server/src/integration/dingtalk.service.ts |
org-tree-with-users 返回增加 deptIds |
实现阶段技能注入
编码 agent 需加载:
ui-ux-pro-max— 前端 UI/UX 设计(Ant Design 6 组件选择、交互细节)vercel-react-best-practices— React 性能优化(memo、useMemo、避免无意义重渲染)