fix: add DTO validation, transactional imports, role-not-found handling, null role guard
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { SyncLog, SyncState, UserDingMapping } from '../entities';
|
||||
import type { SyncPlatform, SyncType, SyncStatus } from '../entities/sync-log.entity';
|
||||
import { DingTalkService } from '../integration/dingtalk.service';
|
||||
@@ -39,6 +39,7 @@ export class SyncService {
|
||||
private readonly weComService: WeComService,
|
||||
private readonly attendanceImportService: AttendanceImportService,
|
||||
private readonly scheduleSyncService: ScheduleSyncService,
|
||||
private readonly dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
// ── Scheduled sync disabled — use manual trigger via UI ──
|
||||
@@ -122,13 +123,14 @@ export class SyncService {
|
||||
teacherCount: number;
|
||||
studentCount: number;
|
||||
skipped: number;
|
||||
warnings: string[];
|
||||
}> {
|
||||
let teacherCount = 0;
|
||||
let studentCount = 0;
|
||||
let skipped = 0;
|
||||
const warnings: string[] = [];
|
||||
|
||||
for (const u of users) {
|
||||
// 检查是否已存在映射
|
||||
const existing = await this.mappingRepo.findOne({
|
||||
where: { dingUserId: u.dingUserId },
|
||||
});
|
||||
@@ -138,65 +140,62 @@ export class SyncService {
|
||||
}
|
||||
|
||||
try {
|
||||
const username = `dd_${u.dingUserId}`;
|
||||
const passwordHash = await bcrypt.hash('123456', 10);
|
||||
await this.dataSource.transaction(async (manager) => {
|
||||
const username = `dd_${u.dingUserId}`;
|
||||
const passwordHash = await bcrypt.hash('123456', 10);
|
||||
|
||||
const user = this.userRepo.create({
|
||||
username,
|
||||
name: u.name,
|
||||
passwordHash,
|
||||
isActive: true,
|
||||
});
|
||||
await this.userRepo.save(user);
|
||||
const user = manager.create(User, {
|
||||
username,
|
||||
name: u.name,
|
||||
passwordHash,
|
||||
isActive: true,
|
||||
});
|
||||
await manager.save(user);
|
||||
|
||||
if (u.roleId != null) {
|
||||
// 老师:分配角色
|
||||
const role = await this.roleRepo.findOne({ where: { id: u.roleId } });
|
||||
if (role) {
|
||||
user.roles = [role];
|
||||
await this.userRepo.save(user);
|
||||
teacherCount++;
|
||||
if (u.roleId != null) {
|
||||
const role = await manager.findOne(Role, { where: { id: u.roleId } });
|
||||
if (role) {
|
||||
user.roles = [role];
|
||||
await manager.save(user);
|
||||
teacherCount++;
|
||||
} else {
|
||||
const msg = `角色 id=${u.roleId} 不存在,跳过用户 ${u.name}(${u.dingUserId})`;
|
||||
this.logger.warn(msg);
|
||||
warnings.push(msg);
|
||||
skipped++;
|
||||
throw new Error('SKIP_USER');
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(`角色 id=${u.roleId} 不存在,用户 ${u.name} 转为学生`);
|
||||
const student = this.studentRepo.create({
|
||||
const student = manager.create(Student, {
|
||||
name: u.name,
|
||||
phone: u.mobile || undefined,
|
||||
userId: user.id,
|
||||
status: 'active',
|
||||
});
|
||||
await this.studentRepo.save(student);
|
||||
await manager.save(student);
|
||||
studentCount++;
|
||||
}
|
||||
} else {
|
||||
// 学生:创建 Student 记录
|
||||
const student = this.studentRepo.create({
|
||||
name: u.name,
|
||||
phone: u.mobile || undefined,
|
||||
userId: user.id,
|
||||
status: 'active',
|
||||
});
|
||||
await this.studentRepo.save(student);
|
||||
studentCount++;
|
||||
}
|
||||
|
||||
// 创建映射
|
||||
const mapping = this.mappingRepo.create({
|
||||
dingUserId: u.dingUserId,
|
||||
userId: user.id,
|
||||
dingName: u.name,
|
||||
dingMobile: u.mobile,
|
||||
const mapping = manager.create(UserDingMapping, {
|
||||
dingUserId: u.dingUserId,
|
||||
userId: user.id,
|
||||
dingName: u.name,
|
||||
dingMobile: u.mobile,
|
||||
});
|
||||
await manager.save(mapping);
|
||||
});
|
||||
await this.mappingRepo.save(mapping);
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
this.logger.error(`导入用户 ${u.name}(${u.dingUserId}) 失败: ${msg}`);
|
||||
if (msg !== 'SKIP_USER') {
|
||||
this.logger.error(`导入用户 ${u.name}(${u.dingUserId}) 失败: ${msg}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`钉钉用户导入完成: ${teacherCount} 位老师, ${studentCount} 位学生, ${skipped} 跳过`,
|
||||
);
|
||||
return { teacherCount, studentCount, skipped };
|
||||
return { teacherCount, studentCount, skipped, warnings };
|
||||
}
|
||||
|
||||
// ── 排班同步 ──
|
||||
|
||||
Reference in New Issue
Block a user