# 学校组织模式与可选行政班设计 ## 背景与结论 产品需要同时服务两类学校: - 标准学校:学生属于行政班,教师从行政班组建教学班; - 珠江学校:不使用行政班,由教师直接维护教学班学生名单。 两类需求共用同一个产品、同一套代码库和同一套数据库。行政班不是系统的必备实体,而是某些学校启用的学生归属方式;教学班始终是实训、任务、成绩和学习进度的业务主体。 不建立长期客户分支,也不重建项目。短期交付若必须使用分支,分支中的差异必须回收为学校级配置或明确的扩展点。 ## 目标与非目标 ### 目标 1. 让每所学校独立选择是否启用行政班。 2. 珠江学校由教师创建教学班、添加和维护教学班学生。 3. 标准学校保持现有行政班和教学班逻辑,避免数据或操作回归。 4. 学生可以加入多个教学班;任务、成绩和进度只以教学班为准。 5. 复用现有 `school_class.class_type`、`created_by` 与 `teaching_class_student` 数据模型。 ### 非目标 - 本期不拆分为两套部署或两套数据库结构。 - 本期不将学校管理员的全校学生管理能力开放给珠江学校。 - 本期不引入通用流程引擎或任意层级组织树。 - 本期不改变外部教务系统同步的既有语义;后续可将其作为第三种名单来源扩展。 ## 方案选择 | 方案 | 结论 | 原因 | | --- | --- | --- | | 客户长期分支 | 不采用 | 版本升级、缺陷修复和安全补丁都需要反复合并,维护成本随客户数增长。 | | 独立项目 | 不采用 | 当前差异集中在组织结构与名单维护,不足以证明业务核心已经分化。 | | 学校级组织模式配置 | 采用 | 差异可被稳定规则表达,公共任务、成绩、实训等能力继续复用。 | ## 核心模型 ```text 学校 ├─ 学生身份(必属学校,以“学校 + 学号”去重) ├─ 行政班(可选,仅 ADMIN_CLASS_ENABLED 学校) └─ 教学班(实训业务主体) └─ 教学班学生成员(学生可加入多个教学班) └─ 实训任务、提交、成绩、学习进度 ``` ### 学校组织配置 新增一张一对一配置表 `school_product_config`,避免以后不断向 `school` 表加入互不相关的定制字段。 | 字段 | 类型 | 说明 | | --- | --- | --- | | `school_id` | VARCHAR(64), PK | 对应 `school.school_id` | | `organization_mode` | VARCHAR(32) | `ADMIN_CLASS_ENABLED` 或 `TEACHING_CLASS_ONLY` | | `student_roster_owner` | VARCHAR(32) | `SCHOOL_ADMIN`、`TEACHER`;预留 `EXTERNAL_SYNC` | | `create_time` / `update_time` | DATETIME | 审计时间 | 配置必须由平台超管维护,学校管理员和教师只读取当前学校配置。创建新学校时必须创建配置记录,默认值为: ```text organization_mode = ADMIN_CLASS_ENABLED student_roster_owner = SCHOOL_ADMIN ``` 珠江学校配置为: ```text organization_mode = TEACHING_CLASS_ONLY student_roster_owner = TEACHER ``` 不允许在学校已存在行政班或学生数据时直接切换组织模式;应先完成数据核查/迁移,或新建学校租户。 ### 珠江学校的数据规则 | 对象 | 规则 | | --- | --- | | 学生 `Userinfo` | `school_id` 必填;`school_class_id` 可为空;院系、专业在珠江模式下可为空。 | | 行政班 `SchoolClass` | 不允许创建 `class_type=ADMIN`;行政班列表、导入入口和相关菜单不展示。 | | 教学班 `SchoolClass` | 必须为 `class_type=TEACHING`,`created_by` 必填且等于创建教师。`school_major_id` 允许为空。 | | 教学班成员 `TeachingClassStudent` | `teaching_class_id`、`student_user_id` 必填;`admin_class_id` 改为可为空。保留 `(teaching_class_id, student_user_id)` 唯一约束。 | | 学生身份去重 | 导入及新增均以 `school_id + student_number`(现有学号/账号字段的业务含义需统一)查重,不能仅按姓名查重。 | `school_class_id` 只能继续作为历史兼容字段或行政班归属,不能再作为“学生当前教学班”的唯一来源。所有实训上下文应通过 `teaching_class_student` 查询教学班成员关系。 ## 业务流程 ### 珠江:教师维护名单 1. 教师进入“我的教学班”,创建教学班;系统写入 `class_type=TEACHING`、`created_by=当前教师`。 2. 教师在教学班内选择“添加学生”:单个新增、批量导入,或搜索并加入本校已有学生。 3. 系统用学校和学号查找学生: - 找到已有学生:仅新增当前教学班成员关系; - 未找到学生:创建本校学生身份后新增成员关系; - 已在当前教学班:返回重复明细,不创建重复记录。 4. 教师可编辑自己教学班成员的基本资料,并移出该教学班。 5. 移出教学班仅删除/失效成员关系,绝不删除 `Userinfo`、提交、成绩或其他教学班成员关系。 学生首次被任何教师导入后成为学校范围的学生身份。后续教师只能把已有学生加入自己的教学班;如允许编辑共享身份资料,必须记录操作者和修改时间,并以学号唯一性校验为准。 ### 标准学校:保持既有逻辑 1. 学校管理员维护学生及行政班。 2. 教师可从一个或多个行政班生成教学班,或导入名单。 3. 导入教学班时保留现有“学生属于指定行政班/专业范围”的校验。 4. `TeachingClassStudent.admin_class_id` 记录学生行政班快照。 所有涉及“行政班存在”或“成员必须来自行政班”的规则,都以当前学校的 `organization_mode` 为前提。不得让珠江模式经过标准模式的行政班校验。 ## 权限与界面 ### 珠江学校 | 角色 | 可见模块 | 关键限制 | | --- | --- | --- | | 平台超管 | 学校与学校配置 | 可查看学校数据;配置变更须通过平台端。 | | 学校管理员 | 学校基础信息、教师管理 | 不显示院系、专业、行政班和全校学生管理。 | | 教师 | 我的教学班、教学班学生、任务和成绩 | 只可操作 `created_by=本人` 的教学班及其成员。 | | 学生 | 已加入教学班的实训内容 | 仅访问自己有效教学班中的任务、进度和成绩。 | ### 前端行为 - 登录后返回或单独读取学校产品配置,并存入用户会话状态。 - 菜单、路由守卫、页面操作按钮均按角色和学校配置判断;隐藏菜单不是权限控制,后端必须执行相同校验。 - 珠江教师创建教学班页不展示“从行政班组建”,只提供“空教学班后添加学生”及“按学生名单创建教学班”。 - 珠江导入模板只包含学生必要身份字段,不包含院系、专业、行政班列。 - 标准学校继续使用现有模板与页面流程。 ## 后端改造边界 现有代码已具备 `SchoolClass.classType`、`SchoolClass.createdBy`、`TeachingClassStudent` 和教师教学班成员导入入口。改造应优先在既有服务/控制器上补充配置判断,而不是复制一套珠江控制器。 ### 配置读取 新增 `SchoolProductConfig`、Mapper 与 `SchoolProductConfigService`,对外提供: ```text getRequiredConfig(schoolId) requiresAdminClass(schoolId) isTeacherRosterManaged(schoolId) ``` 服务层必须获取登录用户所属学校后自行读取配置;不能信任前端传入的组织模式。 ### 教师教学班接口 可沿用现有教学班与成员导入接口,但应收敛为以下语义: ```text POST /api/.../teaching-classes GET /api/.../teaching-classes/mine GET /api/.../teaching-classes/{id}/students POST /api/.../teaching-classes/{id}/students POST /api/.../teaching-classes/{id}/students/import PATCH /api/.../teaching-classes/{id}/students/{studentId} DELETE /api/.../teaching-classes/{id}/students/{studentId} ``` 每个教学班接口先验证:班级存在、`class_type=TEACHING`、归属学校等于操作者学校、`created_by` 等于操作者。名单接口在珠江模式下不得调用行政班、专业或院系的必填校验。 ### 需要重点检查的现有逻辑 - 教学班成员导入与“按学生名单创建教学班”当前会调用行政班/专业范围校验;应按组织模式分支为标准校验与珠江校验。 - 教学班成员写入当前会传入学生的 `schoolClassId`;珠江模式传入 `NULL`。 - 学生任务、成绩、AI 评价和学习进度的“当前教学班”解析必须优先使用 `TeachingClassStudent`;若学生可并行处于多个教学班,调用方必须显式传入 `teachingClassId` 或让学生选择上下文,禁止任取第一条成员记录。 - 删除学生的接口必须在教学班上下文中仅移除成员关系;任何全局删除学生账号的行为不应向教师开放。 ## 数据迁移与上线 1. 新增 `school_product_config`,为所有已有学校写入标准学校默认配置。 2. 将 `teaching_class_student.admin_class_id` 改为可空;保留已有数据。 3. 确认 `school_class.school_major_id`、学生的院系/专业字段在数据库层允许为空;若不允许,仅为珠江场景放宽为空。 4. 为珠江创建配置,发布后先由一名教师做小范围名单导入验收。 5. 不删除行政班、专业、院系或历史 `school_class_id` 数据,不对标准学校做批量数据重写。 6. 配置切换设置为受控操作:出现行政班、学生归属或任务数据时,后端拒绝直接切换并返回可操作的迁移提示。 ## 审计与错误处理 记录教学班创建、学生身份创建、成员加入、成员移出和成员资料变更,至少包含学校、教学班、学生、操作者、时间、来源(手工/导入)。 典型错误: - 教师操作非本人教学班:403。 - 非珠江学校以珠江模板导入:返回“当前学校启用了行政班,请使用标准导入方式”。 - 珠江学校请求创建行政班:返回“当前学校未启用行政班”。 - 学号已存在但姓名冲突:返回冲突行,不自动覆盖既有学生身份。 - 学生已在当前教学班:返回重复明细,整批导入的处理策略统一为“重复行跳过并回显”;其他校验错误则整批拒绝。 ## 验收标准 1. 珠江教师可创建教学班、导入新学生、将已有学生加入另一教学班,且成员 `admin_class_id` 为空。 2. 珠江教师不需要创建或选择行政班、院系、专业即可完成名单维护。 3. 珠江学校管理员看不到且无法调用行政班、院系、专业、全校学生管理功能。 4. 珠江教师不能操作其他教师教学班或其成员。 5. 标准学校仍需经行政班规则组建教学班,原有行政班成员导入与任务分配测试通过。 6. 学生在多个教学班时,任务、成绩、AI 评价与学习进度都按明确的 `teachingClassId` 隔离。 7. 移出教学班不会删除学生身份、历史任务提交或其他教学班的成员资格。 8. 非法切换学校组织模式被后端拒绝;前端隐藏入口不能绕过后端授权。 ## 实施顺序 1. 编写数据库迁移与配置实体/服务单元测试。 2. 完成学校创建、查询与平台配置管理接口。 3. 调整教学班创建、学生新增、名单导入、成员移出服务的配置分支。 4. 调整学生当前教学班解析,使多教学班上下文明确。 5. 修改前端会话配置、菜单与教学班名单页面/导入模板。 6. 覆盖珠江模式、标准模式、跨教师越权、多教学班和历史回归测试。