From 9e0d9ff39c27fd6984d087bb1a2bc125fddccfbc Mon Sep 17 00:00:00 2001 From: wangziqi Date: Mon, 6 Jul 2026 15:33:12 +0800 Subject: [PATCH] feat: add teacher list and profile API --- apps/server/src/rbac/rbac.controller.ts | 40 +++++++++++++++ apps/server/src/rbac/rbac.service.ts | 68 ++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/apps/server/src/rbac/rbac.controller.ts b/apps/server/src/rbac/rbac.controller.ts index 3a45be2..ebd6715 100644 --- a/apps/server/src/rbac/rbac.controller.ts +++ b/apps/server/src/rbac/rbac.controller.ts @@ -6,6 +6,7 @@ import { Delete, Body, Param, + Query, UseGuards, Request, BadRequestException, @@ -259,4 +260,43 @@ export class RbacController { async getTeacherWorkspace(@Request() req: any) { return this.rbacService.getTeacherWorkspace(req.user?.id); } + + // ---- 教师管理 ---- + + @Get('teachers') + @RequirePermission('user:view') + async getTeachers( + @Query('search') search?: string, + @Query('page') page?: string, + @Query('pageSize') pageSize?: string, + ) { + return this.rbacService.getTeachers({ + search, + page: page ? +page : undefined, + pageSize: pageSize ? +pageSize : undefined, + }); + } + + @Put('teachers/:id/profile') + @RequirePermission('user:edit') + async updateTeacherProfile( + @Param('id') id: string, + @Body() profile: UpdateProfileDto, + @Request() req: { user?: { id: number; username: string } }, + ) { + const { ipAddress, userAgent } = extractRequestInfo(req); + const result = await this.rbacService.updateTeacherProfile(+id, profile); + await this.logService.log({ + userId: req.user?.id, + username: req.user?.username, + module: '教师管理', + action: '编辑档案', + targetId: +id, + targetType: 'user', + detail: '更新教师档案', + ipAddress, + userAgent, + }); + return result; + } } diff --git a/apps/server/src/rbac/rbac.service.ts b/apps/server/src/rbac/rbac.service.ts index a942790..d864239 100644 --- a/apps/server/src/rbac/rbac.service.ts +++ b/apps/server/src/rbac/rbac.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, In } from 'typeorm'; import * as bcrypt from 'bcryptjs'; @@ -454,4 +454,70 @@ export class RbacService { myStudents, }; } + + async getTeachers(query?: { search?: string; page?: number; pageSize?: number }) { + const qb = this.userRepo + .createQueryBuilder('u') + .leftJoin('u.roles', 'role') + .leftJoin(ClassTeacher, 'ct', 'ct.userId = u.id') + .leftJoin('ct.class', 'c') + .select([ + 'u.id', 'u.username', 'u.name', 'u.isActive', 'u.profile', 'u.lastLoginAt', + 'role.code', 'role.name', + 'ct.roleType', 'ct.subject', 'ct.id', + 'c.id', 'c.name', + ]) + .where('role.code IN (:...roles)', { roles: ['teacher', 'class_teacher', 'dormitory_supervisor', 'super_admin'] }); + + if (query?.search) { + qb.andWhere('(u.name LIKE :s OR u.username LIKE :s)', { s: `%${query.search}%` }); + } + + const total = await qb.getCount(); + const users = await qb + .orderBy('u.name', 'ASC') + .skip(((query?.page || 1) - 1) * (query?.pageSize || 20)) + .take(query?.pageSize || 20) + .getMany(); + + const list = users.map((u) => { + // TypeORM injects __ct__ and __class__ via leftJoin on non-entity relations + const raw = u as unknown as Record; + const classAssignments: Array<{ roleType?: string; subject?: string; className: string | null }> = []; + const rawCt = raw['__ct__']; + if (Array.isArray(rawCt)) { + for (const ct of rawCt) { + const ctRaw = ct as Record; + const cls = ctRaw['__class__'] as Record | undefined; + classAssignments.push({ + roleType: typeof ctRaw['roleType'] === 'string' ? ctRaw['roleType'] : undefined, + subject: typeof ctRaw['subject'] === 'string' ? ctRaw['subject'] : undefined, + className: cls && typeof cls['name'] === 'string' ? cls['name'] : null, + }); + } + } + return { + id: u.id, + username: u.username, + name: u.name, + isActive: u.isActive, + profile: u.profile, + lastLoginAt: u.lastLoginAt, + roles: u.roles || [], + classAssignments, + }; + }); + + return { list, total }; + } + + async updateTeacherProfile( + id: number, + profile: { subjects?: string[]; joinedAt?: string; qualifications?: string }, + ) { + const user = await this.userRepo.findOne({ where: { id } }); + if (!user) throw new NotFoundException('用户不存在'); + user.profile = { ...user.profile, ...profile }; + return this.userRepo.save(user); + } }