
座位划分面向班主任的真实排座场景,结合班级、考试成绩、性格测评和座位规则生成可用座位表。
本文基于 SeatAllocationViewSet 和座位工作台页面,把班级选择、考试联动、规则计算和导出任务转换为 Codex 项目代码生成任务。
文章目录
设计与需求
座位划分不能只按普通 CRUD 理解。它连接学生、教师、班级、考试或画像数据,页面操作会影响后续教学管理、班级治理和数据分析。Codex 接收这类任务时,需要拿到真实源码路径、字段约束、接口规则和验收边界,才能生成可落地的项目代码。
#mermaid-svg-kNBdjajGfmz0F4PZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kNBdjajGfmz0F4PZ .error-icon{fill:#552222;}#mermaid-svg-kNBdjajGfmz0F4PZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kNBdjajGfmz0F4PZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kNBdjajGfmz0F4PZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kNBdjajGfmz0F4PZ .marker.cross{stroke:#333333;}#mermaid-svg-kNBdjajGfmz0F4PZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kNBdjajGfmz0F4PZ p{margin:0;}#mermaid-svg-kNBdjajGfmz0F4PZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ .cluster-label text{fill:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ .cluster-label span{color:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ .cluster-label span p{background-color:transparent;}#mermaid-svg-kNBdjajGfmz0F4PZ .label text,#mermaid-svg-kNBdjajGfmz0F4PZ span{fill:#333;color:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ .node rect,#mermaid-svg-kNBdjajGfmz0F4PZ .node circle,#mermaid-svg-kNBdjajGfmz0F4PZ .node ellipse,#mermaid-svg-kNBdjajGfmz0F4PZ .node polygon,#mermaid-svg-kNBdjajGfmz0F4PZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kNBdjajGfmz0F4PZ .rough-node .label text,#mermaid-svg-kNBdjajGfmz0F4PZ .node .label text,#mermaid-svg-kNBdjajGfmz0F4PZ .image-shape .label,#mermaid-svg-kNBdjajGfmz0F4PZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-kNBdjajGfmz0F4PZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kNBdjajGfmz0F4PZ .rough-node .label,#mermaid-svg-kNBdjajGfmz0F4PZ .node .label,#mermaid-svg-kNBdjajGfmz0F4PZ .image-shape .label,#mermaid-svg-kNBdjajGfmz0F4PZ .icon-shape .label{text-align:center;}#mermaid-svg-kNBdjajGfmz0F4PZ .node.clickable{cursor:pointer;}#mermaid-svg-kNBdjajGfmz0F4PZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kNBdjajGfmz0F4PZ .arrowheadPath{fill:#333333;}#mermaid-svg-kNBdjajGfmz0F4PZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kNBdjajGfmz0F4PZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kNBdjajGfmz0F4PZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kNBdjajGfmz0F4PZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kNBdjajGfmz0F4PZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kNBdjajGfmz0F4PZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kNBdjajGfmz0F4PZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kNBdjajGfmz0F4PZ .cluster text{fill:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ .cluster span{color:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kNBdjajGfmz0F4PZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kNBdjajGfmz0F4PZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-kNBdjajGfmz0F4PZ .icon-shape,#mermaid-svg-kNBdjajGfmz0F4PZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kNBdjajGfmz0F4PZ .icon-shape p,#mermaid-svg-kNBdjajGfmz0F4PZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kNBdjajGfmz0F4PZ .icon-shape .label rect,#mermaid-svg-kNBdjajGfmz0F4PZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kNBdjajGfmz0F4PZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kNBdjajGfmz0F4PZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kNBdjajGfmz0F4PZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-kNBdjajGfmz0F4PZ .input>*{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .input span{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .input tspan{fill:#1D39C4!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .design>*{fill:#F6FFED!important;stroke:#52C41A!important;color:#237804!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .design span{fill:#F6FFED!important;stroke:#52C41A!important;color:#237804!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .design tspan{fill:#237804!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .process>*{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .process span{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .process tspan{fill:#AD4E00!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .check>*{fill:#F9F0FF!important;stroke:#722ED1!important;color:#531DAB!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .check span{fill:#F9F0FF!important;stroke:#722ED1!important;color:#531DAB!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .check tspan{fill:#531DAB!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .output>*{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .output span{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-kNBdjajGfmz0F4PZ .output tspan{fill:#006D75!important;} 教育管理需求
座位划分设计
页面结构
数据模型
接口规则
权限验收
Codex生成代码
| 需求层描述 | 设计层转换 | Codex 代码生成方向 |
|---|---|---|
| 班级选择 | select_classes_dict 提供树形班级 |
生成班级树、叶子节点校验和权限校验 |
| 考试联动 | get_class_exams 返回班级可选考试 |
生成考试下拉、默认考试和切换刷新 |
| 排座计算 | generate_seat_plan 返回 seat_plan、seat_matrix、students |
生成规则面板、座位网格和溢出提示 |
| 导出任务 | export_seat_plan_download_task 创建下载中心任务 |
生成导出按钮和任务创建提示 |
可以直接使用下面的Prompt进行模块功能的设计
text
请基于教育管理系统源码设计 座位划分 模块,不要编造代码中不存在的能力。
业务范围:按班级加载学生和考试成绩,设置行列、关闭座位、性别互补、性格互补、学业互补和特殊学生关注规则,生成座位表并创建下载中心导出任务。
后端路径:server_backend/modules/User/views_app/SeatAllocation.py、server_backend/modules/User/utils.py
前端路径:server_vue3/src/views/modules/User/SeatAllocation/index.vue、UserWorkbenchesApplication/index.vue
接口入口:/api/User/SeatAllocation/get_class_exams/、get_class_students/、generate_seat_plan/、export_seat_plan_download_task/
核心字段:该视图使用 DummyModel 作为 ViewSet 占位,核心数据来自 StudentManage、StudentAdmissionAssessment、TestingCenter.ExaminationStudentScore 和下载中心任务
扩展能力:数据联动、批量处理、导入导出
请输出模块设计方案,包含页面结构、数据模型、接口规则、权限验收、异常边界和 Codex 分阶段生成项目代码的任务拆分。扩展能力只允许使用源码中已经存在的能力。
后端设计
后端设计重点不是堆接口,而是把座位划分沉淀成可复用的数据底座。当前模块基于 CustomModelViewSet、CustomModelRefSerializer、CustomModelInFilter 和 DefaultRouter 注册接口,结合模型字段、只读字段、范围筛选和动作接口形成稳定契约。
| 后端设计项 | 设计重点 | Codex 生成方向 |
|---|---|---|
| 班级权限 | check_class_permission 校验用户班级范围 |
防止越权查看其他班级学生 |
| 学生特征 | build_student_features 聚合成绩和性格 |
输出学生表、考试序列、最近考试和排座依据 |
| 排座算法 | build_layout、build_seat_plan_payload 生成座位 |
支持关闭座位、容量截断和规则权重 |
| 导出任务 | build_seat_plan_export_excel_bytes + create_download_center_task |
返回 download_center_id 供下载中心处理 |
可以直接使用下面的Prompt进行后端代码的设计
text
请按 座位划分 的真实业务补齐或重构后端代码。
需要读取并遵守:
- server_backend/modules/User/views_app/SeatAllocation.py、server_backend/modules/User/utils.py
- server_backend/modules/User/models.py
- server_backend/modules/User/urls.py
生成要求:
- 按 SeatAllocation 模型字段设计序列化规则,不要新增无来源字段。
- 使用 CustomModelViewSet、CustomModelRefSerializer 和 CustomModelInFilter 保持项目风格。
- 补齐列表查询、详情、新增、编辑、删除或只读接口边界。
- 保留 read_only_fields、search_fields、range_fields 和权限校验逻辑。
- 扩展能力只实现:数据联动、批量处理、导入导出。
- 输出需要包含模型字段说明、接口清单、路由注册、筛选规则、权限规则和异常处理。
前端设计
前端设计重点不是把字段堆到页面上,而是让管理人员能高效完成座位划分的业务操作。当前模块使用 fs-page、fs-crud、createCrudOptions、接口封装和 Element Plus 组件组织列表、搜索、表单、抽屉或业务操作。
| 前端设计项 | 设计重点 | Codex 生成方向 |
|---|---|---|
| 控制面板 | 班级树、考试下拉、成绩表、规则滑块和行列输入 | 生成响应式状态和加载提示 |
| 座位网格 | seatRows、seatCols、disabledSeatLabels 控制布局 |
生成座位开关、空座、关闭座和学生卡片 |
| 自动排座 | 规则、行列或关闭座位变化后触发重新生成 | 生成防抖、loading 和异常提示 |
| 导出按钮 | 调用 export_seat_plan_download_task |
生成任务创建提示,不直接处理文件流 |
可以直接使用下面的Prompt进行前端代码的设计
text
请按 座位划分 的真实业务生成或补齐前端代码。
需要读取并遵守:
- server_vue3/src/views/modules/User/SeatAllocation/index.vue、UserWorkbenchesApplication/index.vue
- 页面内通过 request 调用 /api/User/SeatAllocation/*
生成要求:
- 使用项目现有 fs-crud、Element Plus、request 封装和 api.ts 风格。
- 生成列表查询、筛选区域、新增编辑表单、详情或抽屉交互、权限按钮和保存回显。
- 处理字段转换:班级树叶子节点触发考试加载,考试选择触发学生成绩加载,行列和关闭座位转换为 disabled_seat_labels,规则对象转换为后端 rules
- 扩展交互只实现:数据联动、批量处理、导入导出。
- 不要添加源码中未实现的扩展入口。
扩展功能
座位划分的扩展能力覆盖数据联动、整班批量排座和导出下载。它不是维护一条记录,而是把班级数据转换成可使用的排座方案。
| 扩展功能 | 主要用途 | 落地重点 |
|---|---|---|
| 数据联动 | 班级、考试、学生成绩、性格和座位布局联动 | 保持 class_name、exam_code、rules、disabled_seat_labels 一致 |
| 批量处理 | 整班学生按规则一次性生成座位表 | 处理容量、关闭座位、溢出和特殊学生 |
| 导入导出 | 生成 Excel 并创建下载中心任务 | 返回 download_center_id,由下载中心下载 |
数据联动
座位划分的数据联动体现在 selectedClass、selectedExam、studentScoreTable、rules、seatRows、seatCols、disabledSeatLabels、seat_plan。前端选择、搜索或抽屉打开后,会生成接口参数 class_name、exam_code、rows、cols、rules、disabled_seat_labels,后端按学生、班级、日期或类型筛选,再把结果回填到列表、表单或业务面板中。
#mermaid-svg-pYDteUUwhbi3b5dB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pYDteUUwhbi3b5dB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pYDteUUwhbi3b5dB .error-icon{fill:#552222;}#mermaid-svg-pYDteUUwhbi3b5dB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pYDteUUwhbi3b5dB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pYDteUUwhbi3b5dB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pYDteUUwhbi3b5dB .marker.cross{stroke:#333333;}#mermaid-svg-pYDteUUwhbi3b5dB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pYDteUUwhbi3b5dB p{margin:0;}#mermaid-svg-pYDteUUwhbi3b5dB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pYDteUUwhbi3b5dB .cluster-label text{fill:#333;}#mermaid-svg-pYDteUUwhbi3b5dB .cluster-label span{color:#333;}#mermaid-svg-pYDteUUwhbi3b5dB .cluster-label span p{background-color:transparent;}#mermaid-svg-pYDteUUwhbi3b5dB .label text,#mermaid-svg-pYDteUUwhbi3b5dB span{fill:#333;color:#333;}#mermaid-svg-pYDteUUwhbi3b5dB .node rect,#mermaid-svg-pYDteUUwhbi3b5dB .node circle,#mermaid-svg-pYDteUUwhbi3b5dB .node ellipse,#mermaid-svg-pYDteUUwhbi3b5dB .node polygon,#mermaid-svg-pYDteUUwhbi3b5dB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pYDteUUwhbi3b5dB .rough-node .label text,#mermaid-svg-pYDteUUwhbi3b5dB .node .label text,#mermaid-svg-pYDteUUwhbi3b5dB .image-shape .label,#mermaid-svg-pYDteUUwhbi3b5dB .icon-shape .label{text-anchor:middle;}#mermaid-svg-pYDteUUwhbi3b5dB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pYDteUUwhbi3b5dB .rough-node .label,#mermaid-svg-pYDteUUwhbi3b5dB .node .label,#mermaid-svg-pYDteUUwhbi3b5dB .image-shape .label,#mermaid-svg-pYDteUUwhbi3b5dB .icon-shape .label{text-align:center;}#mermaid-svg-pYDteUUwhbi3b5dB .node.clickable{cursor:pointer;}#mermaid-svg-pYDteUUwhbi3b5dB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pYDteUUwhbi3b5dB .arrowheadPath{fill:#333333;}#mermaid-svg-pYDteUUwhbi3b5dB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pYDteUUwhbi3b5dB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pYDteUUwhbi3b5dB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pYDteUUwhbi3b5dB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pYDteUUwhbi3b5dB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pYDteUUwhbi3b5dB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pYDteUUwhbi3b5dB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pYDteUUwhbi3b5dB .cluster text{fill:#333;}#mermaid-svg-pYDteUUwhbi3b5dB .cluster span{color:#333;}#mermaid-svg-pYDteUUwhbi3b5dB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pYDteUUwhbi3b5dB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pYDteUUwhbi3b5dB rect.text{fill:none;stroke-width:0;}#mermaid-svg-pYDteUUwhbi3b5dB .icon-shape,#mermaid-svg-pYDteUUwhbi3b5dB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pYDteUUwhbi3b5dB .icon-shape p,#mermaid-svg-pYDteUUwhbi3b5dB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pYDteUUwhbi3b5dB .icon-shape .label rect,#mermaid-svg-pYDteUUwhbi3b5dB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pYDteUUwhbi3b5dB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pYDteUUwhbi3b5dB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pYDteUUwhbi3b5dB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-pYDteUUwhbi3b5dB .input>*{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-pYDteUUwhbi3b5dB .input span{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-pYDteUUwhbi3b5dB .input tspan{fill:#1D39C4!important;}#mermaid-svg-pYDteUUwhbi3b5dB .process>*{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-pYDteUUwhbi3b5dB .process span{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-pYDteUUwhbi3b5dB .process tspan{fill:#AD4E00!important;}#mermaid-svg-pYDteUUwhbi3b5dB .output>*{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-pYDteUUwhbi3b5dB .output span{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-pYDteUUwhbi3b5dB .output tspan{fill:#006D75!important;} 结果阶段
处理阶段
输入阶段
筛选条件
表单选择
抽屉打开
生成参数
请求接口
字段转换
刷新列表
表单回显
状态更新
交给 Codex 生成时,要明确联动边界:只处理源码中存在的字段和接口,不把其他学生画像、考试分析或 AI 评估能力塞进本模块。保存动作必须保证 刷新考试列表、成绩表、座位网格、容量统计和导出任务。
可以直接使用下面的Prompt进行数据联动设计
text
请为 座位划分 设计数据联动实现。
联动字段:selectedClass、selectedExam、studentScoreTable、rules、seatRows、seatCols、disabledSeatLabels、seat_plan
接口参数:class_name、exam_code、rows、cols、rules、disabled_seat_labels
结果回填:刷新考试列表、成绩表、座位网格、容量统计和导出任务
源码边界:只使用当前模块已经存在的模型字段、api.ts 请求和页面组件,不新增未实现的业务能力。
请输出联动流程、字段映射表、异常处理、保存回显规则和需要修改的文件路径。
批量处理
座位划分的批量处理不是简单多选删除,而是把一组学生或班级数据按业务规则一次性生成结果。源码中的 generate_seat_plan 按整班学生生成 seat_plan、seat_matrix、arranged_count、overflow_count 需要统计成功、跳过、失败和更新数量,便于页面提示和后续验收。
#mermaid-svg-UXdy9F4apSHsn4te{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UXdy9F4apSHsn4te .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UXdy9F4apSHsn4te .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UXdy9F4apSHsn4te .error-icon{fill:#552222;}#mermaid-svg-UXdy9F4apSHsn4te .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UXdy9F4apSHsn4te .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UXdy9F4apSHsn4te .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UXdy9F4apSHsn4te .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UXdy9F4apSHsn4te .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UXdy9F4apSHsn4te .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UXdy9F4apSHsn4te .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UXdy9F4apSHsn4te .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UXdy9F4apSHsn4te .marker.cross{stroke:#333333;}#mermaid-svg-UXdy9F4apSHsn4te svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UXdy9F4apSHsn4te p{margin:0;}#mermaid-svg-UXdy9F4apSHsn4te .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UXdy9F4apSHsn4te .cluster-label text{fill:#333;}#mermaid-svg-UXdy9F4apSHsn4te .cluster-label span{color:#333;}#mermaid-svg-UXdy9F4apSHsn4te .cluster-label span p{background-color:transparent;}#mermaid-svg-UXdy9F4apSHsn4te .label text,#mermaid-svg-UXdy9F4apSHsn4te span{fill:#333;color:#333;}#mermaid-svg-UXdy9F4apSHsn4te .node rect,#mermaid-svg-UXdy9F4apSHsn4te .node circle,#mermaid-svg-UXdy9F4apSHsn4te .node ellipse,#mermaid-svg-UXdy9F4apSHsn4te .node polygon,#mermaid-svg-UXdy9F4apSHsn4te .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UXdy9F4apSHsn4te .rough-node .label text,#mermaid-svg-UXdy9F4apSHsn4te .node .label text,#mermaid-svg-UXdy9F4apSHsn4te .image-shape .label,#mermaid-svg-UXdy9F4apSHsn4te .icon-shape .label{text-anchor:middle;}#mermaid-svg-UXdy9F4apSHsn4te .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UXdy9F4apSHsn4te .rough-node .label,#mermaid-svg-UXdy9F4apSHsn4te .node .label,#mermaid-svg-UXdy9F4apSHsn4te .image-shape .label,#mermaid-svg-UXdy9F4apSHsn4te .icon-shape .label{text-align:center;}#mermaid-svg-UXdy9F4apSHsn4te .node.clickable{cursor:pointer;}#mermaid-svg-UXdy9F4apSHsn4te .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UXdy9F4apSHsn4te .arrowheadPath{fill:#333333;}#mermaid-svg-UXdy9F4apSHsn4te .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UXdy9F4apSHsn4te .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UXdy9F4apSHsn4te .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UXdy9F4apSHsn4te .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UXdy9F4apSHsn4te .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UXdy9F4apSHsn4te .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UXdy9F4apSHsn4te .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UXdy9F4apSHsn4te .cluster text{fill:#333;}#mermaid-svg-UXdy9F4apSHsn4te .cluster span{color:#333;}#mermaid-svg-UXdy9F4apSHsn4te div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UXdy9F4apSHsn4te .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UXdy9F4apSHsn4te rect.text{fill:none;stroke-width:0;}#mermaid-svg-UXdy9F4apSHsn4te .icon-shape,#mermaid-svg-UXdy9F4apSHsn4te .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UXdy9F4apSHsn4te .icon-shape p,#mermaid-svg-UXdy9F4apSHsn4te .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UXdy9F4apSHsn4te .icon-shape .label rect,#mermaid-svg-UXdy9F4apSHsn4te .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UXdy9F4apSHsn4te .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UXdy9F4apSHsn4te .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UXdy9F4apSHsn4te :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-UXdy9F4apSHsn4te .input>*{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .input span{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .input tspan{fill:#1D39C4!important;}#mermaid-svg-UXdy9F4apSHsn4te .process>*{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .process span{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .process tspan{fill:#AD4E00!important;}#mermaid-svg-UXdy9F4apSHsn4te .check>*{fill:#F9F0FF!important;stroke:#722ED1!important;color:#531DAB!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .check span{fill:#F9F0FF!important;stroke:#722ED1!important;color:#531DAB!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .check tspan{fill:#531DAB!important;}#mermaid-svg-UXdy9F4apSHsn4te .output>*{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .output span{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-UXdy9F4apSHsn4te .output tspan{fill:#006D75!important;} 批量输入
规则校验
事务处理
统计结果
页面反馈
交给 Codex 生成时,要要求接口返回结构化汇总,不要只返回一句成功。重复账号、未分班学生、无座位容量、空数据等情况都需要明确处理。
可以直接使用下面的Prompt进行批量处理功能设计
text
请为 座位划分 设计批量处理能力。
真实批量操作:generate_seat_plan 按整班学生生成 seat_plan、seat_matrix、arranged_count、overflow_count
要求:校验输入、统计成功数量、统计跳过数量、统计失败数量、返回结构化 data,并在前端显示明确提示。
不要新增源码中未实现的批量流程。
导入导出
座位划分的导出能力围绕 export_seat_plan_download_task 生成座位表 Excel 并写入下载中心 展开。后端生成 Excel 字节流并创建下载中心任务,前端只接收 download_center_id 和提示信息,避免在业务页面直接处理文件流。
#mermaid-svg-jZON9C4vnqunckIC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jZON9C4vnqunckIC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jZON9C4vnqunckIC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jZON9C4vnqunckIC .error-icon{fill:#552222;}#mermaid-svg-jZON9C4vnqunckIC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jZON9C4vnqunckIC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jZON9C4vnqunckIC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jZON9C4vnqunckIC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jZON9C4vnqunckIC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jZON9C4vnqunckIC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jZON9C4vnqunckIC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jZON9C4vnqunckIC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jZON9C4vnqunckIC .marker.cross{stroke:#333333;}#mermaid-svg-jZON9C4vnqunckIC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jZON9C4vnqunckIC p{margin:0;}#mermaid-svg-jZON9C4vnqunckIC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jZON9C4vnqunckIC .cluster-label text{fill:#333;}#mermaid-svg-jZON9C4vnqunckIC .cluster-label span{color:#333;}#mermaid-svg-jZON9C4vnqunckIC .cluster-label span p{background-color:transparent;}#mermaid-svg-jZON9C4vnqunckIC .label text,#mermaid-svg-jZON9C4vnqunckIC span{fill:#333;color:#333;}#mermaid-svg-jZON9C4vnqunckIC .node rect,#mermaid-svg-jZON9C4vnqunckIC .node circle,#mermaid-svg-jZON9C4vnqunckIC .node ellipse,#mermaid-svg-jZON9C4vnqunckIC .node polygon,#mermaid-svg-jZON9C4vnqunckIC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jZON9C4vnqunckIC .rough-node .label text,#mermaid-svg-jZON9C4vnqunckIC .node .label text,#mermaid-svg-jZON9C4vnqunckIC .image-shape .label,#mermaid-svg-jZON9C4vnqunckIC .icon-shape .label{text-anchor:middle;}#mermaid-svg-jZON9C4vnqunckIC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jZON9C4vnqunckIC .rough-node .label,#mermaid-svg-jZON9C4vnqunckIC .node .label,#mermaid-svg-jZON9C4vnqunckIC .image-shape .label,#mermaid-svg-jZON9C4vnqunckIC .icon-shape .label{text-align:center;}#mermaid-svg-jZON9C4vnqunckIC .node.clickable{cursor:pointer;}#mermaid-svg-jZON9C4vnqunckIC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jZON9C4vnqunckIC .arrowheadPath{fill:#333333;}#mermaid-svg-jZON9C4vnqunckIC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jZON9C4vnqunckIC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jZON9C4vnqunckIC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jZON9C4vnqunckIC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jZON9C4vnqunckIC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jZON9C4vnqunckIC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jZON9C4vnqunckIC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jZON9C4vnqunckIC .cluster text{fill:#333;}#mermaid-svg-jZON9C4vnqunckIC .cluster span{color:#333;}#mermaid-svg-jZON9C4vnqunckIC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jZON9C4vnqunckIC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jZON9C4vnqunckIC rect.text{fill:none;stroke-width:0;}#mermaid-svg-jZON9C4vnqunckIC .icon-shape,#mermaid-svg-jZON9C4vnqunckIC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jZON9C4vnqunckIC .icon-shape p,#mermaid-svg-jZON9C4vnqunckIC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jZON9C4vnqunckIC .icon-shape .label rect,#mermaid-svg-jZON9C4vnqunckIC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jZON9C4vnqunckIC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jZON9C4vnqunckIC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jZON9C4vnqunckIC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-jZON9C4vnqunckIC .input>*{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-jZON9C4vnqunckIC .input span{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-jZON9C4vnqunckIC .input tspan{fill:#1D39C4!important;}#mermaid-svg-jZON9C4vnqunckIC .process>*{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-jZON9C4vnqunckIC .process span{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-jZON9C4vnqunckIC .process tspan{fill:#AD4E00!important;}#mermaid-svg-jZON9C4vnqunckIC .output>*{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-jZON9C4vnqunckIC .output span{fill:#E6FFFB!important;stroke:#13C2C2!important;color:#006D75!important;stroke-width:1.2px!important;}#mermaid-svg-jZON9C4vnqunckIC .output tspan{fill:#006D75!important;} 导出参数
生成表格
创建任务
下载中心
Codex 生成时要保留下载中心任务模式,不能把导出结果硬塞到列表接口。文件名、任务名、描述信息需要带上班级、考试或模块标识,便于用户在下载中心识别。
可以直接使用下面的Prompt进行导入导出功能设计
text
请为 座位划分 设计导出能力。
真实导出逻辑:export_seat_plan_download_task 生成座位表 Excel 并写入下载中心
后端要求:生成 Excel 内容,调用 create_download_center_task,返回 download_center_id。
前端要求:按钮触发导出接口,展示任务创建提示,不直接处理二进制文件。
不要添加源码中未实现的导入流程。
Codex开发标准
使用 Codex 开发座位划分时,不能直接让模型自由写代码。PDD 定义业务边界和验收口径,SOP 约束目录、文件职责和开发顺序,接口权限规则保证生成代码能进入现有系统。
#mermaid-svg-d5mmoRH3K352p3lv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-d5mmoRH3K352p3lv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d5mmoRH3K352p3lv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d5mmoRH3K352p3lv .error-icon{fill:#552222;}#mermaid-svg-d5mmoRH3K352p3lv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d5mmoRH3K352p3lv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d5mmoRH3K352p3lv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d5mmoRH3K352p3lv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d5mmoRH3K352p3lv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d5mmoRH3K352p3lv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d5mmoRH3K352p3lv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d5mmoRH3K352p3lv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d5mmoRH3K352p3lv .marker.cross{stroke:#333333;}#mermaid-svg-d5mmoRH3K352p3lv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d5mmoRH3K352p3lv p{margin:0;}#mermaid-svg-d5mmoRH3K352p3lv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-d5mmoRH3K352p3lv .cluster-label text{fill:#333;}#mermaid-svg-d5mmoRH3K352p3lv .cluster-label span{color:#333;}#mermaid-svg-d5mmoRH3K352p3lv .cluster-label span p{background-color:transparent;}#mermaid-svg-d5mmoRH3K352p3lv .label text,#mermaid-svg-d5mmoRH3K352p3lv span{fill:#333;color:#333;}#mermaid-svg-d5mmoRH3K352p3lv .node rect,#mermaid-svg-d5mmoRH3K352p3lv .node circle,#mermaid-svg-d5mmoRH3K352p3lv .node ellipse,#mermaid-svg-d5mmoRH3K352p3lv .node polygon,#mermaid-svg-d5mmoRH3K352p3lv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d5mmoRH3K352p3lv .rough-node .label text,#mermaid-svg-d5mmoRH3K352p3lv .node .label text,#mermaid-svg-d5mmoRH3K352p3lv .image-shape .label,#mermaid-svg-d5mmoRH3K352p3lv .icon-shape .label{text-anchor:middle;}#mermaid-svg-d5mmoRH3K352p3lv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d5mmoRH3K352p3lv .rough-node .label,#mermaid-svg-d5mmoRH3K352p3lv .node .label,#mermaid-svg-d5mmoRH3K352p3lv .image-shape .label,#mermaid-svg-d5mmoRH3K352p3lv .icon-shape .label{text-align:center;}#mermaid-svg-d5mmoRH3K352p3lv .node.clickable{cursor:pointer;}#mermaid-svg-d5mmoRH3K352p3lv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d5mmoRH3K352p3lv .arrowheadPath{fill:#333333;}#mermaid-svg-d5mmoRH3K352p3lv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d5mmoRH3K352p3lv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d5mmoRH3K352p3lv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d5mmoRH3K352p3lv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d5mmoRH3K352p3lv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d5mmoRH3K352p3lv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d5mmoRH3K352p3lv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d5mmoRH3K352p3lv .cluster text{fill:#333;}#mermaid-svg-d5mmoRH3K352p3lv .cluster span{color:#333;}#mermaid-svg-d5mmoRH3K352p3lv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-d5mmoRH3K352p3lv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d5mmoRH3K352p3lv rect.text{fill:none;stroke-width:0;}#mermaid-svg-d5mmoRH3K352p3lv .icon-shape,#mermaid-svg-d5mmoRH3K352p3lv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d5mmoRH3K352p3lv .icon-shape p,#mermaid-svg-d5mmoRH3K352p3lv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d5mmoRH3K352p3lv .icon-shape .label rect,#mermaid-svg-d5mmoRH3K352p3lv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d5mmoRH3K352p3lv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d5mmoRH3K352p3lv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d5mmoRH3K352p3lv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-d5mmoRH3K352p3lv .input>*{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .input span{fill:#EEF4FF!important;stroke:#5B8FF9!important;color:#1D39C4!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .input tspan{fill:#1D39C4!important;}#mermaid-svg-d5mmoRH3K352p3lv .design>*{fill:#F6FFED!important;stroke:#52C41A!important;color:#237804!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .design span{fill:#F6FFED!important;stroke:#52C41A!important;color:#237804!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .design tspan{fill:#237804!important;}#mermaid-svg-d5mmoRH3K352p3lv .dev>*{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .dev span{fill:#FFF7E6!important;stroke:#FA8C16!important;color:#AD4E00!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .dev tspan{fill:#AD4E00!important;}#mermaid-svg-d5mmoRH3K352p3lv .check>*{fill:#F9F0FF!important;stroke:#722ED1!important;color:#531DAB!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .check span{fill:#F9F0FF!important;stroke:#722ED1!important;color:#531DAB!important;stroke-width:1.2px!important;}#mermaid-svg-d5mmoRH3K352p3lv .check tspan{fill:#531DAB!important;} 验收交付
Codex开发
模块设计
输入约束
需求边界
PDD设计
SOP目录
接口权限
后端设计
前端设计
扩展功能
读取上下文
生成后端
生成前端
补齐扩展
功能自检
PDD验收
问题修复
模块交付
SOP 标准
SOP 用来约束代码目录、文件职责和开发顺序。座位划分的目录结构需要贴合现有 User 模块,不应为单篇文章另起一套风格。
text
server_backend/modules/User/
views_app/SeatAllocation.py
utils.py
urls.py
server_vue3/src/views/modules/User/SeatAllocation/
index.vue
server_vue3/src/views/modules/User/UserWorkbenchesApplication/
index.vue
docs/modules/座位划分/
pdd.md
api.md
test-cases.md
codex-sop.md
| 开发阶段 | Codex 执行目标 | 输出结果 |
|---|---|---|
| 模块设计 | 读取模型、视图、路由和前端页面 | pdd.md 明确业务边界 |
| 目录规划 | 对齐后端 views_app、前端模块目录和接口封装 | codex-sop.md 明确文件职责 |
| 后端实现 | 生成模型序列化、视图接口、筛选和权限校验 | 后端接口可被前端稳定调用 |
| 前端实现 | 生成列表、表单、抽屉、按钮和接口调用 | 页面操作闭环可运行 |
| 功能补齐 | 按真实扩展能力补齐联动、批量、文件或生成逻辑 | 扩展能力与源码一致 |
| 验收修复 | 根据 PDD 和测试用例修复偏差 | test-cases.md 与代码一致 |
可以直接使用下面的Prompt进行SOP撰写
text
请为 座位划分 编写 Codex 开发 SOP。
要求:
- 先输出目录结构,不要直接写代码。
- 先生成 docs/modules/座位划分/pdd.md、api.md、test-cases.md 和 codex-sop.md。
- 目录必须贴合现有路径:server_backend/modules/User/views_app/SeatAllocation.py、server_backend/modules/User/utils.py、server_vue3/src/views/modules/User/SeatAllocation/index.vue、UserWorkbenchesApplication/index.vue。
- SOP 需要约束模型字段、接口视图、路由注册、前端 crud 配置、接口封装、权限按钮、保存回显和扩展能力。
- 扩展能力只包含:数据联动、批量处理、导入导出。
- 文档完成后,再按 SOP 分阶段生成项目代码。
PDD 标准
PDD 是座位划分的设计与验收文档,用于判断 Codex 输出是否符合真实业务。验收不能只看页面能否打开,还要检查字段、接口、权限、筛选、联动和扩展能力是否匹配源码。
| 验收维度 | 验收重点 | 通过标准 |
|---|---|---|
| 业务目标 | 按班级加载学生和考试成绩,设置行列、关闭座位、性别互补、性格互补、学业互补和特殊学生关注规则,生成座位表并创建下载中心导出任务。 | 页面和接口能支撑模块业务闭环 |
| 页面结构 | server_vue3/src/views/modules/User/SeatAllocation/index.vue、UserWorkbenchesApplication/index.vue | 列表、表单、抽屉或操作按钮与源码一致 |
| 数据模型 | 该视图使用 DummyModel 作为 ViewSet 占位,核心数据来自 StudentManage、StudentAdmissionAssessment、TestingCenter.ExaminationStudentScore 和下载中心任务 | 字段类型、只读字段、唯一约束不偏离模型 |
| 接口规则 | /api/User/SeatAllocation/get_class_exams/、get_class_students/、generate_seat_plan/、export_seat_plan_download_task/ | 列表、保存、删除、动作接口请求参数正确 |
| 权限控制 | 登录用户、班级范围或系统权限 | 非授权数据不可越权访问 |
| 测试用例 | 正常、异常、空数据、回显和删除 | test-cases.md 覆盖主要路径 |
| 数据联动 | 班级、考试、规则和座位布局联动 | 切换班级或考试后成绩与座位同步刷新 |
| 批量处理 | 整班排座 | 容量不足、关闭座位和空班级处理正确 |
| 导入导出 | 座位表导出任务 | 下载中心任务创建成功并返回 ID |
可以直接使用下面的Prompt进行PDD 验收
text
请根据 docs/modules/座位划分/pdd.md 对 座位划分 进行验收。
验收范围:业务目标、页面结构、数据模型、接口规则、权限控制、测试用例、数据联动、批量处理、导入导出。
请输出验收结果表,字段包含:验收项、通过状态、发现问题、涉及文件、修复建议。不要只给结论;必须指出未通过项的具体文件位置和建议改法。
总结
座位划分把班级学生、考试成绩、性格测评和座位规则汇总为可执行的班级排座方案,是班主任日常治理中高频且有明确业务闭环的模块。
Codex 开发该模块时,应由 PDD 定义排座规则、接口参数和导出边界,由 SOP 约束后端算法与前端工作台结构,再用 Prompt 分阶段实现联动、批量排座和下载任务。