refactor: syncAll creates Student + StudentDingMapping directly

This commit is contained in:
2026-07-09 17:05:54 +08:00
parent 73c1ea7a76
commit e87e5e2096

View File

@@ -4,14 +4,12 @@
* 提供:
* - OAuth2 access_token新版 API + 缓存)
* - BFS 遍历所有部门 + 用户(带限流)
* - 用户同步(自动建 User + Student + StudentDingMapping
* - 用户同步(自动建 Student + StudentDingMapping
*/
import { Injectable, Logger, ServiceUnavailableException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { Department } from '../entities/department.entity';
import { User } from '../entities/user.entity';
import { Student } from '../entities/student.entity';
import { Class } from '../entities/class.entity';
import { StudentDingMapping } from '../entities/student-ding-mapping.entity';
@@ -187,8 +185,6 @@ export class DingTalkService {
constructor(
@InjectRepository(Department)
private readonly deptRepo: Repository<Department>,
@InjectRepository(User)
private readonly userRepo: Repository<User>,
@InjectRepository(Student)
private readonly studentRepo: Repository<Student>,
@InjectRepository(StudentDingMapping)
@@ -603,73 +599,35 @@ export class DingTalkService {
// ═══════════════════════════════════════════
private async syncOneUser(du: {
userid: string;
name: string;
mobile: string;
userid: string; name: string; mobile: string;
}): Promise<void> {
// Look up by mapping first
let mapping = await this.studentDingMappingRepo.findOne({ where: { dingUserId: du.userid } });
let user: User | null = null;
let mapping = await this.studentDingMappingRepo.findOne({
where: { dingUserId: du.userid },
});
if (mapping) {
const student = await this.studentRepo.findOne({ where: { id: mapping.studentId } });
const student = await this.studentRepo.findOne({
where: { id: mapping.studentId },
});
if (student) {
student.name = du.name;
if (du.mobile) student.phone = du.mobile;
await this.studentRepo.save(student);
}
await this.studentDingMappingRepo.save(mapping);
return;
}
// No mapping → find or create
const username = du.mobile || `dd_${du.userid}`;
user = await this.userRepo.findOne({ where: { username } });
const student = this.studentRepo.create({
name: du.name,
phone: du.mobile || undefined,
status: 'active',
});
await this.studentRepo.save(student);
if (!user) {
const passwordHash = await bcrypt.hash('123456', 10);
user = this.userRepo.create({
username,
name: du.name,
passwordHash,
isActive: true,
});
await this.userRepo.save(user);
const student = this.studentRepo.create({
name: du.name,
phone: du.mobile || undefined,
userId: user.id,
status: 'active',
});
await this.studentRepo.save(student);
} else {
// Update existing user
user.name = du.name;
await this.userRepo.save(user);
// Ensure Student record exists (backfill for users synced before this logic)
const existingStudent = await this.studentRepo.findOne({ where: { userId: user.id } });
if (existingStudent && existingStudent.status !== 'active') {
// Student was manually marked as staff/graduated/withdrawn — do not overwrite
this.logger.debug(`User ${user.id} has non-active Student (${existingStudent.status}), skipping backfill`);
} else if (!existingStudent) {
const student = this.studentRepo.create({
name: du.name,
phone: du.mobile || undefined,
userId: user.id,
status: 'active',
});
await this.studentRepo.save(student);
}
}
// ponytail: find student by userId — method rewritten in Task 2
const student = await this.studentRepo.findOne({ where: { userId: user.id } });
if (!student) return;
mapping = this.studentDingMappingRepo.create({
dingUserId: du.userid,
studentId: student.id,
});
await this.studentDingMappingRepo.save(mapping);
}
// ═══════════════════════════════════════════