forked from wangziqi/gongxue-base
feat: add teacher list and profile API
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
|||||||
Delete,
|
Delete,
|
||||||
Body,
|
Body,
|
||||||
Param,
|
Param,
|
||||||
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
Request,
|
Request,
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
@@ -259,4 +260,43 @@ export class RbacController {
|
|||||||
async getTeacherWorkspace(@Request() req: any) {
|
async getTeacherWorkspace(@Request() req: any) {
|
||||||
return this.rbacService.getTeacherWorkspace(req.user?.id);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository, In } from 'typeorm';
|
import { Repository, In } from 'typeorm';
|
||||||
import * as bcrypt from 'bcryptjs';
|
import * as bcrypt from 'bcryptjs';
|
||||||
@@ -454,4 +454,70 @@ export class RbacService {
|
|||||||
myStudents,
|
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<string, unknown>;
|
||||||
|
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<string, unknown>;
|
||||||
|
const cls = ctRaw['__class__'] as Record<string, unknown> | 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user