264 lines
8.2 KiB
TypeScript
264 lines
8.2 KiB
TypeScript
import { Injectable, Logger, Optional } from '@nestjs/common';
|
|
import type { UpdateProfileDto } from './dto/rbac.dto';
|
|
import { RbacSeedService } from './rbac-seed.service';
|
|
import { RbacUserService } from './rbac-user.service';
|
|
import { getChinaDateParts } from './rbac-presets';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository, In } from 'typeorm';
|
|
import {
|
|
Permission,
|
|
Role,
|
|
User,
|
|
Class,
|
|
ClassStudent,
|
|
ClassTeacher,
|
|
ClassSchedule,
|
|
Student,
|
|
AttendanceSession,
|
|
} from '../entities';
|
|
|
|
@Injectable()
|
|
export class RbacService {
|
|
private readonly logger = new Logger(RbacService.name);
|
|
|
|
constructor(
|
|
@InjectRepository(Permission) private permRepo: Repository<Permission>,
|
|
@InjectRepository(Role) private roleRepo: Repository<Role>,
|
|
@InjectRepository(User) private userRepo: Repository<User>,
|
|
@InjectRepository(Class) private classRepo: Repository<Class>,
|
|
@InjectRepository(ClassStudent) private classStudentRepo: Repository<ClassStudent>,
|
|
@InjectRepository(ClassTeacher) private classTeacherRepo: Repository<ClassTeacher>,
|
|
@InjectRepository(ClassSchedule) private classScheduleRepo: Repository<ClassSchedule>,
|
|
@InjectRepository(Student) private studentRepo: Repository<Student>,
|
|
@InjectRepository(AttendanceSession)
|
|
private attendanceSessionRepo: Repository<AttendanceSession>,
|
|
@Optional() private seedService?: RbacSeedService,
|
|
@Optional() private userService?: RbacUserService,
|
|
) {}
|
|
|
|
private get seedOps(): RbacSeedService {
|
|
if (!this.seedService) {
|
|
this.seedService = new RbacSeedService(this.permRepo, this.roleRepo, this.userRepo);
|
|
}
|
|
return this.seedService;
|
|
}
|
|
|
|
private get userOps(): RbacUserService {
|
|
if (!this.userService) {
|
|
this.userService = new RbacUserService(
|
|
this.permRepo,
|
|
this.roleRepo,
|
|
this.userRepo,
|
|
this.classRepo,
|
|
this.classStudentRepo,
|
|
this.classTeacherRepo,
|
|
this.classScheduleRepo,
|
|
this.studentRepo,
|
|
this.attendanceSessionRepo,
|
|
);
|
|
}
|
|
return this.userService;
|
|
}
|
|
|
|
async findAllRoles(): Promise<Role[]> {
|
|
return this.roleRepo.find({
|
|
relations: ['permissions'],
|
|
order: { id: 'ASC' },
|
|
});
|
|
}
|
|
|
|
async findRoleById(id: number): Promise<Role> {
|
|
return this.roleRepo.findOneOrFail({ where: { id }, relations: ['permissions'] });
|
|
}
|
|
|
|
async createRole(dto: {
|
|
name: string;
|
|
description?: string;
|
|
permissionIds?: number[];
|
|
}): Promise<Role> {
|
|
const role = this.roleRepo.create({ name: dto.name, description: dto.description });
|
|
if (dto.permissionIds && dto.permissionIds.length > 0) {
|
|
role.permissions = await this.userOps.resolvePermissions(dto.permissionIds);
|
|
}
|
|
return this.roleRepo.save(role);
|
|
}
|
|
|
|
async updateRole(
|
|
id: number,
|
|
dto: { name?: string; description?: string; permissionIds?: number[] },
|
|
): Promise<Role> {
|
|
const role = await this.roleRepo.findOneOrFail({
|
|
where: { id },
|
|
relations: ['permissions'],
|
|
});
|
|
if (dto.name !== undefined && !role.isSystem) role.name = dto.name;
|
|
if (dto.description !== undefined) role.description = dto.description;
|
|
if (dto.permissionIds !== undefined) {
|
|
role.permissions =
|
|
dto.permissionIds.length > 0 ? await this.userOps.resolvePermissions(dto.permissionIds) : [];
|
|
}
|
|
return this.roleRepo.save(role);
|
|
}
|
|
|
|
async deleteRole(id: number): Promise<{ message: string }> {
|
|
const role = await this.roleRepo.findOneOrFail({ where: { id } });
|
|
if (role.isSystem) throw new Error('系统角色不可停用');
|
|
if (role.status === 0) return { message: '角色已停用' };
|
|
role.status = 0;
|
|
await this.roleRepo.save(role);
|
|
return { message: '角色已停用' };
|
|
}
|
|
|
|
async findAllPermissions(): Promise<Permission[]> {
|
|
return this.permRepo.find({ order: { group: 'ASC', code: 'ASC' } });
|
|
}
|
|
|
|
async getPermissionTree(): Promise<{ group: string; permissions: Permission[] }[]> {
|
|
const all = await this.findAllPermissions();
|
|
const map = new Map<string, Permission[]>();
|
|
for (const p of all) {
|
|
if (!map.has(p.group)) map.set(p.group, []);
|
|
map.get(p.group)!.push(p);
|
|
}
|
|
return Array.from(map.entries()).map(([group, permissions]) => ({ group, permissions }));
|
|
}
|
|
|
|
async getUserPermissions(userId: number): Promise<string[]> {
|
|
const user = await this.userRepo.findOne({
|
|
where: { id: userId },
|
|
relations: ['roles', 'roles.permissions'],
|
|
});
|
|
if (!user || !user.roles) return [];
|
|
const codes = new Set<string>();
|
|
for (const role of user.roles) {
|
|
if (role.status !== 1) continue;
|
|
for (const perm of role.permissions) {
|
|
codes.add(perm.code);
|
|
}
|
|
}
|
|
return Array.from(codes);
|
|
}
|
|
|
|
|
|
async getTeacherWorkspace(userId: number) {
|
|
// Find all classes where this user is a teacher
|
|
const teacherAssignments = await this.classTeacherRepo.find({
|
|
where: { userId },
|
|
relations: ['class'],
|
|
});
|
|
|
|
const classIds = [...new Set(teacherAssignments.map((t) => t.classId))];
|
|
|
|
if (classIds.length === 0) {
|
|
return { assignedClasses: [], todaySchedules: [], myStudents: [] };
|
|
}
|
|
|
|
const assignedClasses = teacherAssignments.map((t) => ({
|
|
classId: t.classId,
|
|
className: t.class?.name || '',
|
|
classCode: t.class?.code || '',
|
|
roleType: t.roleType,
|
|
subject: t.subject,
|
|
}));
|
|
|
|
// Get today's China business date and day of week (1=Monday, 7=Sunday)
|
|
const { date: todayStr, weekDay: adjustedWeekDay } = getChinaDateParts();
|
|
|
|
const todaySchedules = await this.classScheduleRepo
|
|
.createQueryBuilder('cs')
|
|
.where('cs.classId IN (:...classIds)', { classIds })
|
|
.andWhere('cs.weekDay = :weekDay', { weekDay: adjustedWeekDay })
|
|
.andWhere('cs.startDate <= :today', { today: todayStr })
|
|
.andWhere('cs.endDate >= :today', { today: todayStr })
|
|
.andWhere('cs.status = :status', { status: 'active' })
|
|
.orderBy('cs.startTime', 'ASC')
|
|
.getMany();
|
|
|
|
const classStudents = await this.classStudentRepo.find({
|
|
where: { classId: In(classIds), status: 'active' },
|
|
relations: ['student', 'class'],
|
|
});
|
|
|
|
const myStudents = classStudents.map((cs) => ({
|
|
studentId: cs.studentId,
|
|
studentName: cs.student?.name || '',
|
|
studentNo: cs.student?.studentNo || '',
|
|
className: cs.class?.name || '',
|
|
classId: cs.classId,
|
|
joinDate: cs.joinDate,
|
|
}));
|
|
|
|
return {
|
|
assignedClasses,
|
|
todaySchedules: todaySchedules.map((s) => ({
|
|
id: s.id,
|
|
classId: s.classId,
|
|
classroomId: s.classroomId,
|
|
teacherId: s.teacherId,
|
|
weekDay: s.weekDay,
|
|
startTime: s.startTime,
|
|
endTime: s.endTime,
|
|
subject: s.subject,
|
|
scheduleType: s.scheduleType,
|
|
})),
|
|
myStudents,
|
|
};
|
|
}
|
|
async seedData(): Promise<void> {
|
|
return this.seedOps.seedData();
|
|
}
|
|
|
|
async findAllUsers(isArchived = false) {
|
|
return this.userOps.findAllUsers(isArchived);
|
|
}
|
|
|
|
async createUser(dto: { username: string; password: string; name: string; roleIds?: number[] }) {
|
|
return this.userOps.createUser(dto);
|
|
}
|
|
|
|
async updateUser(id: number, dto: { username?: string; name?: string; roleIds?: number[] }) {
|
|
return this.userOps.updateUser(id, dto);
|
|
}
|
|
|
|
async resetPassword(id: number, newPassword: string) {
|
|
return this.userOps.resetPassword(id, newPassword);
|
|
}
|
|
|
|
async archiveUser(id: number) {
|
|
return this.userOps.archiveUser(id);
|
|
}
|
|
|
|
async restoreUser(id: number) {
|
|
return this.userOps.restoreUser(id);
|
|
}
|
|
|
|
async purgeUser(id: number, currentUserId: number) {
|
|
return this.userOps.purgeUser(id, currentUserId);
|
|
}
|
|
|
|
async markAsStaff(userId: number) {
|
|
return this.userOps.markAsStaff(userId);
|
|
}
|
|
|
|
async markAsStudent(userId: number) {
|
|
return this.userOps.markAsStudent(userId);
|
|
}
|
|
|
|
async getUserProfile(id: number) {
|
|
return this.userOps.getUserProfile(id);
|
|
}
|
|
|
|
async updateUserProfile(id: number, dto: UpdateProfileDto) {
|
|
return this.userOps.updateUserProfile(id, dto);
|
|
}
|
|
|
|
async getTeachers(query?: { search?: string; page?: number; pageSize?: number }) {
|
|
return this.userOps.getTeachers(query);
|
|
}
|
|
|
|
async updateTeacherProfile(id: number, dto: UpdateProfileDto) {
|
|
return this.userOps.updateTeacherProfile(id, dto);
|
|
}
|
|
|
|
}
|