12 KiB
恭学教育学生管理系统 P0 批次 — 设计规格
版本:v1.0
日期:2026-07-05
基于 PRD:../../Downloads/PRD-恭学教育学生管理系统.md(commit59f75bb)
目标
按 PRD 优先级分批实施,本批次覆盖 P0(阻塞上线)的全部 5 项 + 部分 P1(上线前完成),打通班级→排课→宿舍增强→操作日志→RBAC→考勤前端→数据面板的完整链路。
架构
Monorepo (Turborepo),后端 NestJS 11 + TypeORM 0.3 + SQLite/MySQL,前端 React 19 + Vite + Ant Design 6 + ECharts。新增 classes、schedules 两个独立 NestJS 模块,在现有 rooms/occupancies/bills 模块上增量增强,考勤前端新建页面。遵循现有模块结构:每个业务模块独立目录,含 entity/dto/service/controller/module。
技术栈
| 层级 | 技术 |
|---|---|
| 架构 | Monorepo (Turborepo) |
| 前端 | React 19 + Vite + Ant Design 6 + ECharts |
| 后端 | NestJS 11 + TypeORM 0.3 + JWT + Passport |
| 数据库 | 开发 SQLite / 生产 MySQL 8 |
| 测试 | Jest (后端) + Playwright (前端 E2E) |
全局约束
- 所有涉及大量数据的列表/表单页面必须有详细的筛选方案
- 敏感信息(手机号、身份证)需脱敏展示,查看时二次确认+记录操作日志
- 遵循现有 NestJS 模块结构,每个业务模块独立目录
- 前端页面放在
apps/admin/src/pages/下,每个模块独立目录 - 表名使用复数形式(与现有
students、rooms、classrooms、bills一致) - 冲突检测使用数据库唯一约束 + 应用层双重保障
模块 1:班级管理(Class)
实体
classes:班级主表,含 name/code/class_type/status/日期/教师/人数上限,通过 department_id 关联校区。
class_student:班级-学员关联,UNIQUE(class_id, student_id),含 enroll_id/join_date/leave_date/status。
class_teacher:班级-教师关联,UNIQUE(class_id, user_id, role_type),含 role_type(任课老师/班主任/生活老师/学服老师)+ subject。
Class 自身的 head_teacher_id/life_teacher_id/academic_teacher_id 与 class_teacher 表保持同步——创建/编辑班级时,若传了 head_teacher_id 等字段,同时写入 class_teacher 表对应记录。
API
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /classes |
班级列表(筛选:department_id/status/class_type/keyword) |
| POST | /classes |
创建班级(含初始教师/学员分配) |
| GET | /classes/:id |
班级详情(教师列表+学员统计) |
| PUT | /classes/:id |
编辑班级 |
| DELETE | /classes/:id |
删除班级(CASCADE 删除关联) |
| GET | /classes/:id/students |
班级学员列表 |
| POST | /classes/:id/students |
批量添加学员 { studentIds: number[] } |
| DELETE | /classes/:id/students/:studentId |
移除学员 |
| GET | /classes/:id/teachers |
班级教师列表 |
| POST | /classes/:id/teachers |
添加教师 { userId, roleType, subject? } |
| DELETE | /classes/:id/teachers/:userId |
移除教师 |
前端
列表页 Classes/index.tsx:筛选栏(校区下拉/班型下拉/状态下拉/搜索输入)+ 表格(名称/编码/校区/班型/日期/学员数/班主任/状态Tag/操作)+ 分页。
详情页 Classes/Detail.tsx:3 Tab — 基本信息(可编辑表单)、花名册(学员表格+批量添加/移除)、教师(教师表格+添加/移除)。
模块 2:排课管理(ClassSchedule)
实体
class_schedule:排课记录,含 class_id/classroom_id/week_day/start_time/end_time/start_date/end_date/subject/teacher_id/schedule_type(INTERNAL/RENTAL)/rental_id/status。
唯一约束:同教室 + 同 week_day + 时间段重叠 + status=active 拒绝写入(应用层检测 + 数据库层面依赖应用层保证,SQLite 不支持排他约束)。
核心规则
- INTERNAL 排课:按班级维度,展示科目+教师,关联 class_id
- RENTAL 排课:仅标记教室占用,关联 rental_id(classroom_rentals)
- 冲突检测:新增/编辑时检查同教室同星期同时段是否已有 active 排课
- 时段定义:预设 PRD 作息表(早自习 7:30-8:40、上午 9:00-12:00、下午 14:00-17:00、晚自习 18:30-21:00),管理员可在系统设置中调整
API
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /class-schedules |
排课列表(筛选:classroom_id/class_id/week_day/date_range) |
| POST | /class-schedules |
创建排课(含冲突检测) |
| PUT | /class-schedules/:id |
编辑排课(含冲突检测) |
| DELETE | /class-schedules/:id |
删除排课 |
| GET | /class-schedules/weekly |
周视图数据(按教室+week_day聚合) |
| GET | /class-schedules/classroom/:id/occupancy |
教室占用时间线(合并内部+外部) |
前端
周视图 Schedules/index.tsx:顶部周切换 + 教室/班级筛选,主体 7列×N行矩阵(列=周一~周日,行=教室),每格显示科目/教师/时间。点击格子弹出排课表单 Modal(班级/科目/教师/教室/星期/时段/日期范围)。冲突时 Modal 内红色提示。
模块 3:宿舍/入住/账单增强
数据变更
rooms:新增 rental_category(VARCHAR(10),long/short)、monthly_rate(DECIMAL(10,2))。
occupancies:新增 rental_type(VARCHAR(10),long/short)、tenant_id(FK → tenants.id)。
students:新增 tenant_id(FK → tenants.id),替代原 organization 文本字段。迁移时根据 organization 匹配 tenants.name。
账单逻辑
POST /bills/generate 内部:遍历宿舍的 occupancy 记录,rental_type === 'long' 的长租学生走固定月费独立生成(不参与分摊),short 学生走原人天数加权分摊。
前端
Rooms 表单增加 rental_category Select + monthly_rate InputNumber。Occupancies 表单增加 rental_type Select + tenant_id Select。
模块 4:操作日志全量接入
覆盖范围
在以下 controller 的每个写操作中统一调用 OperationLogsService.log():
| 模块 | 审计操作 |
|---|---|
| Classes | 创建/编辑/删除班级、添加/移除学员、添加/移除教师 |
| ClassSchedules | 创建/编辑/删除排课 |
| Students | 新增/编辑/删除学生、导入/导出、查看敏感信息 |
| Occupancies | 新增/编辑/退住 |
| Expenses | 新增/编辑/删除费用 |
| Bills | 生成账单、确认账单、标记已付、导出 |
| Deposits | 收取/退还押金 |
| Attendance | 补录/编辑考勤、匹配钉钉数据 |
| RBAC | 角色变更、权限变更 |
调用模式
this.operationLogsService.log({
userId: req.user.id,
username: req.user.username,
module: 'CLASS',
action: 'CREATE',
targetId: result.id,
targetType: 'class',
detail: { name: result.name },
ipAddress: req.ip,
status: 'success',
});
模块 5:RBAC 权限扩展
permission.json 新增以下节点:
| 权限节点 | super_admin | staff | class_teacher | student |
|---|---|---|---|---|
| CLASS:READ | ✅ | ✅ | ✅ | - |
| CLASS:ADD | ✅ | ✅ | - | - |
| CLASS:UPDATE | ✅ | ✅ | - | - |
| CLASS:DELETE | ✅ | - | - | - |
| SCHEDULE:READ | ✅ | ✅ | ✅ | - |
| SCHEDULE:ADD | ✅ | ✅ | - | - |
| SCHEDULE:UPDATE | ✅ | ✅ | - | - |
| SCHEDULE:DELETE | ✅ | - | - | - |
| ATTENDANCE:READ | ✅ | ✅ | ✅ | ✅ |
| ATTENDANCE:ADD | ✅ | ✅ | - | - |
| ATTENDANCE:UPDATE | ✅ | ✅ | - | - |
模块 6:考勤管理前端
页面
列表页 Attendance/index.tsx:筛选栏(班级/日期范围/时段/状态/来源)+ 表格(姓名/班级/日期/时段/状态Tag/来源/打卡时间/备注/操作)+ 批量补录按钮 + 切换到日历视图按钮。
日历视图:切换模式,行=学生、列=日期+时段,格=状态色块(绿=出勤、黄=迟到、红=缺勤、蓝=请假)。
API
增强现有 /attendance-records 筛选参数,新增:
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /attendance-records/batch |
批量补录 |
| GET | /attendance-records/summary |
考勤汇总统计(按班级/日期范围) |
| GET | /attendance-records/calendar |
日历视图数据 |
| GET | /ding-attendance-raw |
钉钉原始数据列表(筛选match_status) |
| POST | /ding-attendance-raw/:id/match |
手动匹配钉钉数据到学生 |
模块 7:数据面板增强
在现有 Dashboard 的基础上:
- 第二行指标卡:教室总数/占用率、今日出勤率、本月收入总额、教室占用率
- 新增图表:考勤趋势折线图(近30天)、近6月收入趋势图
- 权限控制:不同角色看到不同指标——班主任只看本班考勤,超管看全局
API
GET /dashboard 返回数据增加字段:classroomCount/classroomOccupancyRate/todayAttendanceRate/monthlyIncome/attendanceTrend/incomeTrend。
数据库迁移
新增表
classes、class_student、class_teacher、class_schedule — TypeORM synchronize: true 自动建表。
现有表变更
ALTER TABLE rooms ADD COLUMN rental_category VARCHAR(10) DEFAULT 'short';
ALTER TABLE rooms ADD COLUMN monthly_rate DECIMAL(10,2) DEFAULT 0;
ALTER TABLE occupancies ADD COLUMN rental_type VARCHAR(10) DEFAULT 'short';
ALTER TABLE occupancies ADD COLUMN tenant_id INTEGER REFERENCES tenants(id);
ALTER TABLE students ADD COLUMN tenant_id INTEGER REFERENCES tenants(id);
数据迁移
students.organization → students.tenant_id:遍历 students 表,根据 organization 文本匹配 tenants.name,匹配到的写入 tenant_id,未匹配的置 NULL。原 organization 列保留不删,待后续迭代清理。
文件结构
apps/server/src/
├── entities/
│ ├── class.entity.ts 🆕
│ ├── class-student.entity.ts 🆕
│ ├── class-teacher.entity.ts 🆕
│ ├── class-schedule.entity.ts 🆕
│ ├── room.entity.ts ✏️
│ ├── occupancy.entity.ts ✏️
│ ├── student.entity.ts ✏️
│ └── index.ts ✏️
├── classes/ 🆕
│ ├── classes.module.ts
│ ├── classes.controller.ts
│ ├── classes.service.ts
│ └── dto/
├── schedules/ 🆕
│ ├── schedules.module.ts
│ ├── schedules.controller.ts
│ ├── schedules.service.ts
│ └── dto/
├── rooms/ ✏️ dto
├── occupancies/ ✏️ dto + service
├── bills/ ✏️ service
├── operation-logs/ ✏️ 注入各模块
├── rbac/ ✏️ permission.json
├── dashboard/ ✏️ service + controller
└── app.module.ts ✏️ 注册新模块
apps/admin/src/pages/
├── Classes/ 🆕
│ ├── index.tsx
│ └── Detail.tsx
├── Schedules/ 🆕
│ └── index.tsx
├── Attendance/ 🆕
│ └── index.tsx
├── Dashboard/ ✏️ index.tsx
├── Rooms/ ✏️ index.tsx
└── Occupancies/ ✏️ index.tsx
开发顺序
Phase 1 班级管理(无上游依赖)
│
Phase 2 排课管理(依赖班级+教室)
│
Phase 3 宿舍/入住/账单增强(独立,可与1/2并行)
│
Phase 4 操作日志全量接入(依赖1/2/3的controller就绪)
│
Phase 5 RBAC权限扩展(依赖1/2模块存在)
│
Phase 6 考勤管理前端(后端API已有,独立开发)
│
Phase 7 数据面板增强(依赖各方面数据)
Phase 1-3 可部分并行,Phase 4-5 需等 1-3 完成,Phase 6-7 可在 1-5 完成后并行。