forked from wangziqi/gongxue-base
refactor: syncAll creates Student + StudentDingMapping directly
This commit is contained in:
@@ -4,14 +4,12 @@
|
|||||||
* 提供:
|
* 提供:
|
||||||
* - OAuth2 access_token(新版 API + 缓存)
|
* - OAuth2 access_token(新版 API + 缓存)
|
||||||
* - BFS 遍历所有部门 + 用户(带限流)
|
* - BFS 遍历所有部门 + 用户(带限流)
|
||||||
* - 用户同步(自动建 User + Student + StudentDingMapping)
|
* - 用户同步(自动建 Student + StudentDingMapping)
|
||||||
*/
|
*/
|
||||||
import { Injectable, Logger, ServiceUnavailableException } from '@nestjs/common';
|
import { Injectable, Logger, ServiceUnavailableException } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import * as bcrypt from 'bcryptjs';
|
|
||||||
import { Department } from '../entities/department.entity';
|
import { Department } from '../entities/department.entity';
|
||||||
import { User } from '../entities/user.entity';
|
|
||||||
import { Student } from '../entities/student.entity';
|
import { Student } from '../entities/student.entity';
|
||||||
import { Class } from '../entities/class.entity';
|
import { Class } from '../entities/class.entity';
|
||||||
import { StudentDingMapping } from '../entities/student-ding-mapping.entity';
|
import { StudentDingMapping } from '../entities/student-ding-mapping.entity';
|
||||||
@@ -187,8 +185,6 @@ export class DingTalkService {
|
|||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Department)
|
@InjectRepository(Department)
|
||||||
private readonly deptRepo: Repository<Department>,
|
private readonly deptRepo: Repository<Department>,
|
||||||
@InjectRepository(User)
|
|
||||||
private readonly userRepo: Repository<User>,
|
|
||||||
@InjectRepository(Student)
|
@InjectRepository(Student)
|
||||||
private readonly studentRepo: Repository<Student>,
|
private readonly studentRepo: Repository<Student>,
|
||||||
@InjectRepository(StudentDingMapping)
|
@InjectRepository(StudentDingMapping)
|
||||||
@@ -603,73 +599,35 @@ export class DingTalkService {
|
|||||||
// ═══════════════════════════════════════════
|
// ═══════════════════════════════════════════
|
||||||
|
|
||||||
private async syncOneUser(du: {
|
private async syncOneUser(du: {
|
||||||
userid: string;
|
userid: string; name: string; mobile: string;
|
||||||
name: string;
|
|
||||||
mobile: string;
|
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
// Look up by mapping first
|
let mapping = await this.studentDingMappingRepo.findOne({
|
||||||
let mapping = await this.studentDingMappingRepo.findOne({ where: { dingUserId: du.userid } });
|
where: { dingUserId: du.userid },
|
||||||
let user: User | null = null;
|
});
|
||||||
|
|
||||||
if (mapping) {
|
if (mapping) {
|
||||||
const student = await this.studentRepo.findOne({ where: { id: mapping.studentId } });
|
const student = await this.studentRepo.findOne({
|
||||||
|
where: { id: mapping.studentId },
|
||||||
|
});
|
||||||
if (student) {
|
if (student) {
|
||||||
student.name = du.name;
|
student.name = du.name;
|
||||||
|
if (du.mobile) student.phone = du.mobile;
|
||||||
await this.studentRepo.save(student);
|
await this.studentRepo.save(student);
|
||||||
}
|
}
|
||||||
await this.studentDingMappingRepo.save(mapping);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No mapping → find or create
|
const student = this.studentRepo.create({
|
||||||
const username = du.mobile || `dd_${du.userid}`;
|
name: du.name,
|
||||||
user = await this.userRepo.findOne({ where: { username } });
|
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({
|
mapping = this.studentDingMappingRepo.create({
|
||||||
dingUserId: du.userid,
|
dingUserId: du.userid,
|
||||||
studentId: student.id,
|
studentId: student.id,
|
||||||
});
|
});
|
||||||
|
await this.studentDingMappingRepo.save(mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════
|
// ═══════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user