feat: P2-12 Teacher profile/workspace + P2-13 Multi-class comparison

P2-12 Teacher Management:
- Add profile JSON field (subjects, joinedAt, qualifications) to User entity
- Add GET/PUT /rbac/users/:id/profile endpoints with UpdateProfileDto
- Add GET /rbac/teacher-workspace endpoint returning assignedClasses,
  todaySchedules, and myStudents
- Create /teacher-workspace page with tabs (My Classes, Today's Schedule,
  My Students)
- Add route with PermissionRoute and menu item in MainLayout

P2-13 Student Archive Multi-class:
- Add GET /students/:id/compare-classes endpoint returning side-by-side
  enrollment data with per-class attendance statistics
- Students module now includes ClassStudent and AttendanceRecord entities
- No 2-enrollment cap existed in the codebase; student enrollments
  already return all records via class-student table
This commit is contained in:
2026-07-05 20:42:27 +08:00
parent 80ce911b88
commit 5df70a8af0
11 changed files with 438 additions and 6 deletions

View File

@@ -17,6 +17,7 @@ import {
CreateUserDto,
UpdateUserDto,
ResetPasswordDto,
UpdateProfileDto,
} from './dto/rbac.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RequirePermission } from '../auth/decorators/permission.decorator';
@@ -216,4 +217,46 @@ export class RbacController {
throw new BadRequestException(e.message);
}
}
// ---- 用户资料 ----
@Get('users/:id/profile')
@RequirePermission('user:view')
getUserProfile(@Param('id') id: string) {
return this.rbacService.getUserProfile(+id);
}
@Put('users/:id/profile')
@RequirePermission('user:edit')
async updateUserProfile(
@Param('id') id: string,
@Body() dto: UpdateProfileDto,
@Request() req: any,
) {
const { ipAddress, userAgent } = extractRequestInfo(req);
try {
const result = await this.rbacService.updateUserProfile(+id, dto);
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '账号',
action: '更新资料',
targetId: +id,
targetType: 'user',
ipAddress,
userAgent,
});
return result;
} catch (e: any) {
throw new BadRequestException(e.message);
}
}
// ---- 教师工作台 ----
@Get('teacher-workspace')
@RequirePermission('class:view')
async getTeacherWorkspace(@Request() req: any) {
return this.rbacService.getTeacherWorkspace(req.user?.id);
}
}