Files
gongxue-base/docs/superpowers/specs/2026-07-05-gongxue-p0-design.md

303 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 恭学教育学生管理系统 P0 批次 — 设计规格
> 版本v1.0
> 日期2026-07-05
> 基于 PRD`../../Downloads/PRD-恭学教育学生管理系统.md`commit `59f75bb`
## 目标
按 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_typeINTERNAL/RENTAL/rental_id/status。
唯一约束:同教室 + 同 week_day + 时间段重叠 + status=active 拒绝写入(应用层检测 + 数据库层面依赖应用层保证SQLite 不支持排他约束)。
### 核心规则
1. INTERNAL 排课:按班级维度,展示科目+教师,关联 class_id
2. RENTAL 排课:仅标记教室占用,关联 rental_idclassroom_rentals
3. 冲突检测:新增/编辑时检查同教室同星期同时段是否已有 active 排课
4. 时段定义:预设 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 | 角色变更、权限变更 |
### 调用模式
```typescript
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',
});
```
---
## 模块 5RBAC 权限扩展
`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` 自动建表。
### 现有表变更
```sql
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 完成后并行。