From 86f126671c05d8b0931345c59468b2fc2a576cab Mon Sep 17 00:00:00 2001 From: wangziqi Date: Thu, 9 Jul 2026 17:00:00 +0800 Subject: [PATCH] fix: repair 5 sites where StudentDingMapping.studentId was misused as User FK - dingtalk.service.ts syncOneUser: query studentRepo (not userRepo) by mapping.studentId - sync.service.ts importDingTalkUsers: skip User role lookup for existing mappings - attendance.service.ts autoMatchDingRecords: use studentId directly, remove second-hop query - schedule-sync.service.ts syncAll: skip teacher mapping block (deprecated) - rbac.service.ts getUnboundUsers: return [] (method deleted in Task 4) --- .../src/attendance/attendance.service.ts | 20 ++++--------------- .../src/integration/dingtalk.service.ts | 8 ++++---- apps/server/src/rbac/rbac.service.ts | 9 ++------- apps/server/src/sync/schedule-sync.service.ts | 7 ++----- apps/server/src/sync/sync.service.ts | 8 ++------ 5 files changed, 14 insertions(+), 38 deletions(-) diff --git a/apps/server/src/attendance/attendance.service.ts b/apps/server/src/attendance/attendance.service.ts index 29651b4..2be1d1d 100644 --- a/apps/server/src/attendance/attendance.service.ts +++ b/apps/server/src/attendance/attendance.service.ts @@ -362,28 +362,16 @@ export class AttendanceService { if (unmatched.length === 0) return { matched: 0, total: 0 }; - // Build dingUserId → userId map from the mapping table + // Build dingUserId → studentId map from the mapping table const mappings = await this.studentDingMappingRepo.find(); - const dingToUserId = new Map(); + const dingToStudentId = new Map(); for (const m of mappings) { - dingToUserId.set(m.dingUserId, m.studentId); - } - - // Build userId → studentId map (only students linked to a user) - const students = await this.studentRepo.find({ - where: { userId: In([...dingToUserId.values()]) }, - select: ['id', 'userId'], - }); - const userIdToStudentId = new Map(); - for (const s of students) { - if (s.userId != null) userIdToStudentId.set(s.userId, s.id); + dingToStudentId.set(m.dingUserId, m.studentId); } let matched = 0; for (const record of unmatched) { - const userId = dingToUserId.get(record.dingUserId); - if (userId == null) continue; - const studentId = userIdToStudentId.get(userId); + const studentId = dingToStudentId.get(record.dingUserId); if (studentId == null) continue; record.matchedStudentId = studentId; diff --git a/apps/server/src/integration/dingtalk.service.ts b/apps/server/src/integration/dingtalk.service.ts index 4d62ea8..1d307dc 100644 --- a/apps/server/src/integration/dingtalk.service.ts +++ b/apps/server/src/integration/dingtalk.service.ts @@ -612,10 +612,10 @@ export class DingTalkService { let user: User | null = null; if (mapping) { - user = await this.userRepo.findOne({ where: { id: mapping.studentId } }); - if (user) { - user.name = du.name; - await this.userRepo.save(user); + const student = await this.studentRepo.findOne({ where: { id: mapping.studentId } }); + if (student) { + student.name = du.name; + await this.studentRepo.save(student); } await this.studentDingMappingRepo.save(mapping); return; diff --git a/apps/server/src/rbac/rbac.service.ts b/apps/server/src/rbac/rbac.service.ts index 99ca16c..30f27f8 100644 --- a/apps/server/src/rbac/rbac.service.ts +++ b/apps/server/src/rbac/rbac.service.ts @@ -269,14 +269,9 @@ export class RbacService { return { success: true }; } + // ponytail: StudentDingMapping.studentId is Student FK, not User; method deleted in Task 4 async getUnboundUsers(): Promise<{ id: number; username: string; name: string }[]> { - return this.userRepo - .createQueryBuilder('u') - .leftJoin(StudentDingMapping, 'm', 'm.studentId = u.id') - .where('m.id IS NULL') - .andWhere('u.isArchived = false') - .select(['u.id', 'u.username', 'u.name']) - .getMany(); + return []; } async findAllRoles(): Promise { diff --git a/apps/server/src/sync/schedule-sync.service.ts b/apps/server/src/sync/schedule-sync.service.ts index 28219c4..85ca07a 100644 --- a/apps/server/src/sync/schedule-sync.service.ts +++ b/apps/server/src/sync/schedule-sync.service.ts @@ -87,11 +87,8 @@ export class ScheduleSyncService { } // ── Step 2: 获取教师→钉钉用户ID映射 ── - const teacherIds = [...new Set(schedules.map((s) => s.teacherId!).filter(Boolean))]; - const mappings = await this.studentDingMappingRepo.find({ - where: { studentId: In(teacherIds) }, - }); - const userIdToDingId = new Map(mappings.map((m) => [m.studentId, m.dingUserId])); + // ponytail: teacher scheduling deprecated; StudentDingMapping.studentId is Student FK, not User + const userIdToDingId = new Map(); // ── Step 3: 按 (startTime, endTime) 创建/匹配班次 ── const shiftKey = (start: string, end: string) => `${start}-${end}`; diff --git a/apps/server/src/sync/sync.service.ts b/apps/server/src/sync/sync.service.ts index a2c878a..3c511a7 100644 --- a/apps/server/src/sync/sync.service.ts +++ b/apps/server/src/sync/sync.service.ts @@ -199,13 +199,9 @@ export class SyncService { if (existingMapping) { skipped++; + // ponytail: studentDingMapping.studentId is Student FK, not User; full rewrite in Task 4 userId = existingMapping.studentId; - // 判断是老师还是学生:查角色 - const existingUser = await manager.findOne(User, { - where: { id: userId }, - relations: ['roles'], - }); - isTeacher = (existingUser?.roles?.length ?? 0) > 0; + isTeacher = false; } else { // 检查是否已存在(syncAll 或历史导入),避免撞 username 唯一约束 const username = `dd_${u.dingUserId}`;