fix: handle duplicate usernames and failed role lookups in importDingTalkUsers

This commit is contained in:
2026-07-09 11:07:25 +08:00
parent 3790b2dabb
commit 8ba3327074
2 changed files with 28 additions and 7 deletions

View File

@@ -116,13 +116,17 @@ describe('SyncService — new methods', () => {
); );
}); });
it('imports teacher when roleId is a number but role not found (logs warning, no roles assigned)', async () => { it('imports teacher when roleId is a number but role not found (creates student as fallback)', async () => {
roleRepo.findOne.mockResolvedValue(null); roleRepo.findOne.mockResolvedValue(null);
const mockUser = { id: 11 } as User; const mockUser = { id: 11 } as User;
userRepo.create.mockReturnValue(mockUser); userRepo.create.mockReturnValue(mockUser);
userRepo.save.mockResolvedValue(mockUser); userRepo.save.mockResolvedValue(mockUser);
const mockStudent = { id: 31 } as Student;
studentRepo.create.mockReturnValue(mockStudent);
studentRepo.save.mockResolvedValue(mockStudent);
mappingRepo.findOne.mockResolvedValue(null); mappingRepo.findOne.mockResolvedValue(null);
mappingRepo.create.mockReturnValue({} as UserDingMapping); mappingRepo.create.mockReturnValue({} as UserDingMapping);
mappingRepo.save.mockResolvedValue({} as UserDingMapping); mappingRepo.save.mockResolvedValue({} as UserDingMapping);
@@ -133,12 +137,21 @@ describe('SyncService — new methods', () => {
const result = await service.importDingTalkUsers(users); const result = await service.importDingTalkUsers(users);
expect(result.teacherCount).toBe(1); expect(result.teacherCount).toBe(0);
expect(result.studentCount).toBe(0); expect(result.studentCount).toBe(1);
expect(result.skipped).toBe(0); expect(result.skipped).toBe(0);
// User should be saved once (without roles assignment the second time) // User should be saved once (no second save for role assignment)
expect(userRepo.save).toHaveBeenCalledTimes(1); expect(userRepo.save).toHaveBeenCalledTimes(1);
// Student record should have been created as fallback
expect(studentRepo.create).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Li Si',
userId: 11,
status: 'active',
}),
);
expect(studentRepo.save).toHaveBeenCalled();
}); });
it('imports student when roleId is null', async () => { it('imports student when roleId is null', async () => {

View File

@@ -138,7 +138,7 @@ export class SyncService {
} }
try { try {
const username = u.mobile || `dd_${u.dingUserId}`; const username = `dd_${u.dingUserId}`;
const passwordHash = await bcrypt.hash('123456', 10); const passwordHash = await bcrypt.hash('123456', 10);
const user = this.userRepo.create({ const user = this.userRepo.create({
@@ -155,10 +155,18 @@ export class SyncService {
if (role) { if (role) {
user.roles = [role]; user.roles = [role];
await this.userRepo.save(user); await this.userRepo.save(user);
teacherCount++;
} else { } else {
this.logger.warn(`角色 id=${u.roleId} 不存在,用户 ${u.name} 未分配角色`); this.logger.warn(`角色 id=${u.roleId} 不存在,用户 ${u.name} 转为学生`);
const student = this.studentRepo.create({
name: u.name,
phone: u.mobile || undefined,
userId: user.id,
status: 'active',
});
await this.studentRepo.save(student);
studentCount++;
} }
teacherCount++;
} else { } else {
// 学生:创建 Student 记录 // 学生:创建 Student 记录
const student = this.studentRepo.create({ const student = this.studentRepo.create({