Skip to content

工程实践4-软件产品设计实践指导书

文档版本:v1.0.1 | 分支:lujun-feature-001 | 最后更新:2026-05-14 | 维护者:《工程实践4-软件产品设计》 | 状态:正式发布


0. 本方案解决的工程实践痛点

《工程实践 4-软件产品设计》 的教学场景中,学生同步开发同一个 Web 项目,长期面临以下四类典型痛点。本平台针对每一项痛点提供系统性解决方案:

痛点一:多学生合作开发,代码与流程难以协调

问题:45 人提交代码到同一仓库,分支冲突频发,谁改了什么无从追溯,代码质量参差不齐,教师无法高效审查。

解决方案

  • 强制分支保护(main 禁直推)+ PR 合并流程(F-D-002)
  • GitHub Actions 自动运行 Ruff 代码风格检查与 pytest 单元测试,不通过不合并(F-D-004)
  • pr-agent AI 自动代码审查,针对每个 PR 生成审查意见,教师只需复核(F-D-007)
  • Issue 关闭必须关联 PR 编号,确保每项任务均有完整的开发记录(F-T-007)

痛点二:学生开发效果无法实时预览,反馈滞后

问题:学生提交代码后无法直观看到运行效果,只能等教师部署后才知道结果,调试周期长、错误隐蔽。

解决方案

  • 平台提供自动化脚本 Web 执行界面(F-D-006),一键触发部署预览
  • GitHub 快捷链接面板(F-D-008)直接跳转到仓库、Actions 日志、PR 页面,减少跳转摩擦
  • 学生通过得分看板(F-S-040)和学习时间线(F-S-041)随时掌握自己的开发进度与评分状态
  • 教师通过学生开发日志导出(F-T-010)获取 9 维度报告,精准识别落后学生

痛点三:AI 评价缺乏统一标准,评语质量不一

问题:教师依赖主观判断给出评语,缺乏统一标准;ai 评价无法沉淀为可复用的规则;学生收到的反馈因人而异,改进方向不明确。

解决方案

  • 教师在平台维护 pr-agent 审查提示词模板(F-D-007),统一 AI 评价口径
  • 平台支持 Dry-Run 验证提示词效果,正式使用前可对历史 PR 预览审查结果
  • 评语与改进建议模块(F-S-031)面向学生展示 AI 审查结果,并附可操作的改进建议
  • 需求文档编辑器(F-T-006)内置 Copilot 辅助,帮助教师生成标准化需求描述,从源头减少歧义

痛点四:Web 系统统一数据库管理混乱,各自为政

问题:学生各自修改数据库表结构,导致字段冲突、版本不一致、他人代码跑不起来;缺乏统一管控手段。

解决方案

  • 采用「统一开发数据库 + Alembic 迁移版本管理」模式(详见第 13 节)
  • 学生无 DDL 权限,所有表结构变更必须提 Issue → 教师审批 → 生成迁移文件 → PR 合并 → 统一执行 reflex db upgrade
  • Git 记录所有迁移历史,可回滚、可追踪、可审计
  • 任意学生执行 git pull + reflex db upgrade 即可与最新库结构同步

1. 文档目标

本文档定义《工程实践》课程管理平台的需求范围与实现约束。

  • 实现框架:Reflex(https://reflex.dev)
  • 开发语言:Python(前后端统一)
  • 当前范围:优先定义“学生功能”

说明:教师端、助教端、管理员端将在后续章节补充,本章先完成学生端需求冻结。

2. 产品定位

《工程实践》课程管理平台用于支撑工程实践1-4全过程教学,围绕“任务发布—学习过程—提交反馈—成绩追踪”形成闭环。

平台第一阶段聚焦学生侧核心体验:

  1. 看得到:课程信息、章节任务、通知日历清晰可查。
  2. 做得完:作业/实验提交流程顺畅,支持多次迭代。
  3. 跟得上:成绩、评语、进度可追踪。
  4. 不掉线:支持移动端访问,基础网络波动可恢复。

2.1 工程实践1-4内容规划(软件工程专业)

针对软件工程专业学生,建议将《工程实践1-4》设计为从基础编码训练 → Web 全栈开发 → 软件测试 → 软件产品综合开发的递进式课程链条。这样可以避免课程内容割裂,也能让学生在工程实践 4 中真正具备“可交付产品开发”的综合能力。

课程 课程定位 核心内容 编程语言 软件工程方法/工具 课程支撑系统/平台 教学目标
工程实践1 编码训练 C 语言基础、数据结构入门、Git、GitHub、Linux 基本操作、学术诚信与反 AI 代写检测 C GCCGit、代码相似度检测、提交行为分析、AI 辅助检查与评分 学生采用 VS CodeSSH + vi 编程;课程可采用 GitHub Classroom 开课与作业分发,也可直接使用 GitHub;建议每名学生一个长期分支;学生提交后自动触发 AI 检查与评分,且 AI 检查记录带有触发人编码机制,便于审计追踪 让学生建立代码规范、命令行操作、版本管理、基于 GitHub 的课程协作方式,以及“可使用 AI 但不得直接代写提交”的学术规范意识
工程实践2 Web 程序设计 数据驱动网站开发、页面与状态管理、前后端一体化开发、部署基础、分支预览与在线调试 PythonJava ReflexVaadinMySQLDockerGitHub Flow GitHub Codespaces 作为主要开发平台,并用于触发分支预览;教师侧配置 Coolify 实现 CI/CD;课程流程采用 GitHub Flow,不使用 Gitflow 优先采用 Reflex、Vaadin 这类现代全栈/全后端 Web 技术,替代 ServletJSP 这类较陈旧的教学技术路线,并让学生熟悉轻量化 Web 协作与持续部署
工程实践3 软件测试 前端测试、后端接口测试、图形/GUI 测试、自动化测试、测试报告、TDD 驱动开发;以前一届工程实践项目代码为测试对象开展训练 PythonJavaScript pytestPlaywright、接口测试工具、GUI 自动化测试、TDD、CI 工作流 以“测试上一届代码”为主要训练场景,重点训练学生围绕既有系统补测试、补回归、补自动化脚本,并重点熟悉 TDD 的分析—编写测试—最小实现—重构循环 让学生掌握“写代码之前先定义验证标准”的工程习惯,把测试从补充环节变成开发主流程
工程实践4 软件产品开发 面向真实产品的需求分析、数据库设计、协作开发、测试、部署上线、运维与交付;需求范围以本文所定义的平台需求、流程规范、数据库设计、测试方案、部署方案为核心 C/C++JavaJavaScriptPython 嵌入式、Spring Boot + VueAI + PythonCoolifyGitHub、CI/CD、代码审查与质量控制流程 课程支撑方式以本文档为总规范:参考本文中的需求定义、GitHub 协作流程、数据库规范、测试方案、部署方案与文档要求,组织学生完成一个可交付的软件产品 训练学生完成从需求、设计、编码、测试、部署到交付的完整软件产品生命周期

建议的递进关系

  • 工程实践1 解决“会不会写代码、会不会用 Git、会不会在 Linux 环境下开发”的问题;
  • 工程实践2 解决“会不会做一个完整 Web 系统”的问题;
  • 工程实践3 解决“会不会验证软件质量、会不会自动化测试”的问题;
  • 工程实践4 解决“会不会把一个项目按真实产品方式交付出来”的问题。

工程实践1 的额外教学要求

  • 课程开设阶段建议统一使用 GitHub Classroom 创建班级、发布作业仓库、收集学生提交与查看提交记录;
  • 对作业评阅应加入反 AI 检测机制,重点检查:提交时间异常集中、提交风格前后突变、代码相似度异常、学生无法解释自己提交代码的逻辑等情况;
  • 教学口径应明确为:允许学生把 AI 作为辅助学习工具,但不允许直接复制 AI 生成结果作为本人独立作业提交。

教学建议:工程实践 4 不应再退回到“只做单个页面或单个实验”的层面,而应强调产品意识、团队协作、测试质量、部署交付、版本流程五个关键词。

3. 技术开发方案选型说明

本系统为小班课程教学管理平台,功能以数据展示、GitHub 集成、配置管理为主,用户规模小、业务逻辑简单。Reflex 全栈 Python 架构开发速度快、部署简单、维护成本低,完全满足需求;SpringBoot + Vue 前后端分离架构过重,技术复杂度高,开发与维护成本远超实际需求,因此 Reflex 是更合理、更务实的技术选型。

3. 技术约束(Reflex)

3.1 架构约束

  • 使用 Reflex 的全栈模式(UI + 状态 + 事件处理 + 数据访问统一 Python)。
  • 前端页面通过 Reflex 组件构建,交互通过事件(event handlers)驱动。
  • 页面状态必须通过 State 管理,禁止将敏感数据硬编码到静态前端内容。

3.2 认证与安全约束

  • 学生身份认证可采用本地账号体系或第三方认证集成(如 Google/OAuth 类方案)。
  • 涉及私有信息(成绩、评语、个人资料)的读取与写入必须在后端事件中进行权限校验。
  • 敏感 token / 会话信息仅存储于后端可控变量或安全会话机制中。

3.3 数据层约束

  • 数据模型基于 Reflex rx.Model / SQLModel 体系。
  • 数据库迁移遵循 Alembic 流程(初始化、迁移生成、迁移执行)。
  • 生产环境必须使用可持久化数据库(非仅本地临时文件)。

4. 角色与边界

4.1 角色

  • 学生(本文档重点)
  • 教师(同时承担系统管理员职能)
  • 助教(通过 GitHub 特殊账号授权,Triage 权限,不单独作为平台角色)

4.2 学生端边界

  • 学生仅可访问本人课程数据、本人提交记录、本人成绩与反馈。
  • 学生不可查看其他学生的成绩、私有提交内容和教师后台配置。

5. 功能需求

5.1 学生账户与身份

F-S-001 登录/退出

  • 学生可通过学号/邮箱 + 密码登录。
  • 登录后可主动退出并清除会话。
  • 连续登录失败达到阈值后触发临时限制与提示。

F-S-002 个人资料

  • 学生可查看并编辑个人基础信息(姓名、班级、联系方式)。
  • 学生可修改密码。

F-S-003 GitHub 账号绑定

  • 学生登录后可在个人资料页填写自己的 GitHub 用户名,系统将自动通过 GitHub API 校验账号是否存在。
  • 校验通过后呈现绑定状态(已绑定 / 未绑定 / 账号不存在);每个学生只允许绑定一个账号。
  • 绑定申请提交后需教师审核确认(防止冒用他人账号)。
  • 已绑定帐号希望修改时需向教师申请解除旧绑定。

5.2 课程与章节学习

F-S-010 课程主页

  • 展示学生已选课程(工程实践1-4)与当前学习进度。
  • 每门课程展示:章节数、已完成任务数、截止提醒。

F-S-011 章节内容浏览

  • 学生可按章节查看学习资料、任务说明、评分标准。
  • 支持章节内附件下载(文档、模板、参考代码包)。

F-S-012 公告与通知

  • 展示课程公告、作业截止提醒、成绩发布通知。
  • 未读通知需有明显标识。

5.3 作业/实验提交

F-S-020 作业提交

  • 学生可在截止前提交作业(文本、附件或两者结合)。
  • 支持常见格式:pdfdocxzippyccpp(最终以后端配置为准)。
  • 展示提交时间、文件大小、版本号。

F-S-021 多次提交与版本记录

  • 在教师允许的前提下,学生可重复提交,系统保留历史版本。
  • 最新版本默认为评阅版本,历史版本可回溯查看。

F-S-022 截止规则

  • 截止前可正常提交。
  • 截止后按课程策略处理:禁止提交或标记迟交。
  • 迟交状态需在学生端明确显示。

5.4 成绩与反馈

F-S-030 成绩实时统计

  • 学生登录后可实时查看本人当前综合得分,得分来源包括:考勤、在线考试、代码提交、PR 审查。
  • 各维度得分实时更新(将相关事件嵌入 Reflex 状态更新链),无需手动刷新。
  • 展示各维度得分晠细:
  • 出勤得分:课堂点名縯计,自动按到课率计算
  • 考试得分:各次在线考试各题得分列表
  • 代码提交:各任务代码评阅得分
  • PR 贡献:参与代码审查次数与质量
  • 展示评分时间、评分人(教师/助教);待评分项标注状态提示。

F-S-031 评语与改进建议

  • 学生可查看教师评语、扣分项说明、改进建议。
  • 对允许二次提交的任务,学生可基于反馈再次提交。

F-S-032 成绩单 Excel 导出

  • 学生可一键下载本人全期成绩单,格式符合课程要求的标准 Excel 模板(.xlsx)。
  • 导出内容包含:学号、姓名、各任务得分、总评成绩、提交时间、批改时间。
  • 导出文件中禁止包含其他同学数据(个人成绩单只包含本人信息)。
  • 文件名默认格式:学号_姓名_成绩单_课程名称.xlsx
  • 教师端将在教师功能中支持按班级批量导出全班成绩单。

5.5 学习进度与统计

F-S-040 得分看板与可视化

  • 登录后首屏展示总得分以及各维度分项得分的图形列表:
  • 雷达图:出勤 / 考试 / 代码 / PR 四维度得分比较
  • 柱状图:各次考试得分历史趋势
  • 线形图:全期总得分随时间变化曲线
  • 所有图表基于 Reflex 内置图表组件(或集成 recharts)渲染,无需外部工具。
  • 展示任务完成率、已提交/未提交数量、即将到期任务;提供按课程(工程实践1-4)筛选。

F-S-041 时间线

  • 按时间展示“任务发布—提交—批改—反馈”关键节点。

5.6 交互与可用性

F-S-050 响应式界面

  • 学生端需适配桌面与移动端常见分辨率。

F-S-051 异常提示

  • 上传失败、网络中断、权限不足等场景提供可理解错误提示。
  • 支持失败后重试。

5.7 课堂互动

F-S-052 课堂点名

  • 教师发起点名后,学生在限定时间窗口(默认 60 秒)内通过 App 或浏览器确认到场。
  • 支持地理围栏辅助验证(可选配置,防止代签)。
  • 点名记录自动写入出勤表,学生可查看本人历史出勤状态。
  • 迟到(超时签到)与缺勤分级记录。

F-S-053 课堂在线考试

  • 支持教师发布限时考试题目(单选、多选、填空、简答)。
  • 学生可在手机浏览器答题,无需安装额外 App(Reflex 响应式界面自适应)。
  • 作答过程实时保存草稿,防止意外断线丢失进度。
  • 提交后显示成绩(客观题立即出分,主观题标记待批)。
  • 考试期间禁止查看他人答案;截止时间到自动提交。

5.8 开发运维与项目配置

F-D-001 仓库初始化

  • 平台源码托管于 GitHub 私有仓库。
  • 仓库初始化时配置 .gitignore(Python 模板)与开源协议。

F-D-002 分支保护

  • main 分支启用保护规则:PR 审查必须通过后方可合并。
  • 禁止直接 Force Push 至主分支。
  • 至少需要 1 名成员 Approve。

F-D-003 Secrets 管理

  • 生产环境敏感配置(数据库连接串、部署令牌、会话密钥)一律存入 GitHub Secrets。
  • 禁止将 Secrets 硬编码或提交至代码仓库。

F-D-004 CI 自动化

  • 每次 PR 自动触发 GitHub Actions:代码 lint(Ruff)+ 单元测试(pytest)。
  • CI 通过为合并前置条件,CI 失败时不可合并。

F-D-005 协作者权限管理

  • 仓库成员按角色分配权限:Admin / Write / Triage / Read。
  • 使用 GitHub Team 统一管理课程组成员权限,禁止个人直接授权。

F-D-006 自动化脚本 Web 执行界面

  • 平台提供 Web 配置向导页面,支持一键调用服务器端脚本(如 gh CLI 初始化仓库、设置分支保护、注入 Secrets 等)。
  • 执行过程实时推送脚本输出到页面(基于 Reflex 事件流 yield),每行输出逐步追加显示。
  • 捕获脚本非零退出码与 stderr,在页面以红色标记错误行,提供可复制的完整错误日志。
  • 以进度条或步骤列表展示当前执行进度(如《Step 3/8》已完成)。
  • 执行完成后显示汇总状态(成功 / 失败),并提示可重试的失败步骤。

F-D-007 代码审查与 PR 提示词模版管理

  • 平台支持配置自动代码审查:通过 GitHub Actions 集成 pr-agent(或等效工具),每次 PR 自动触发 AI 审查。
  • 系统内置三套提示词模版:通用代码审查、工程实践规范检查、安全全面审查。
  • 用户可在高级设置页面编辑任意模版内容,并保存为自定义模版(带名称 + 描述)。
  • 自定义模版可设为默认,下次 PR 自动审查时使用该模版。
  • 支持对模版进行复制、重命名、删除操作;提供模版忽略规则(如忽略特定路径或文件类型)。
  • 模版配置存储在仓库的 .github/review-prompts/ 目录下,纳入 Git 版本控制。

修改代码审核 AI 提示词

  • 教师可在提示词编辑器中独立维护「代码审核提示词」(Code Review Prompt),用于评估代码质量、注释规范、命名风格、潜在 Bug 等。
  • 编辑器提供语法高亮和占位符提示({diff}{pr_title}{pr_description} 等 pr-agent 内置变量)。
  • 修改后可点击「保存并应用」,平台自动将新提示词写入 .github/review-prompts/code-review.prompt.md 并提交到仓库。

修改 PR 的 AI 提示词

  • 教师可在提示词编辑器中独立维护「PR 描述/总结提示词」(PR Summary Prompt),用于自动生成 PR 摘要、变更说明和影响范围分析。
  • 修改后平台自动写入 .github/review-prompts/pr-summary.prompt.md

配置完成后验证

  • 平台提供「验证配置」按钮:发起一次针对目标仓库中最近一条 PR(或测试 PR)的 Dry-Run AI 审查,将审查结果输出到平台内置预览区(不提交到 GitHub)。
  • 验证结果中展示:AI 调用状态(成功/超时/错误)、实际使用的提示词版本、返回审查评论片段预览。
  • 若验证失败(如 API Key 无效、仓库权限不足),平台高亮显示具体错误原因,并提供修复建议链接。

验证结束后删除验证痕迹

  • 验证过程产生的临时数据(Dry-Run 输出、临时测试 PR、Draft 评论草稿)在确认后一键清除。
  • 「清除验证痕迹」操作包含:删除 Dry-Run 生成的临时 GitHub Draft Comments(若有)、清空平台预览区缓存、清除本轮验证日志。
  • 操作日志保留「验证时间、操作人、清除时间」三条审计记录,但审查内容本身不持久化。

F-D-008 GitHub 快捷链接面板

  • 平台在开发运维页面提供一组 GitHub 关键快捷链接,支持一键跳转到常用页:
  • 仓库主页
  • Pull Requests 列表
  • Issues 列表
  • Actions / CI 运行记录
  • 分支管理
  • Settings → Secrets
  • Settings → Branches(分支保护)
  • 链接基于已配置的仓库 URL 自动生成,无需手动填写。
  • 支持自定义标签名称和显示顺序;可隐藏不需要的链接。
  • 页面布局中常驻显示(侧边栏或卡片区),无需切换页面即可快速跳转。

5.9 教师基础功能

F-T-001 学生—GitHub 账号对应表

此表是整个系统的核心基础数据。所有基于 GitHub 活动(提交、PR、审查)的得分和统计均依赖此表将真实身份与 GitHub 账号映射。

  • 教师可在后台查看全班学生与对应 GitHub 账号的完整对应表。
  • 支持批量导入(CSV 格式:学号,姓名,GitHub 用户名),自动校验 GitHub 用户名的合法性。
  • 支持单条手动新增、编辑、删除对应关系。
  • 对应关系变更后,相关学生的历史成绩数据自动重算并更新。
  • 表格支持按班级、课程筛选,支持导出当前表格为 CSV。
  • 一个学生只允许绑定一个 GitHub 账号;远程账号共用时系统提示冲突。

F-T-002 VS Code 扩展配置与 Copilot 提示词管理

  • 教师可在管理页面配置课程必装/推荐 VS Code 扩展列表,系统自动生成 .vscode/extensions.json
  • 支持按扩展类型分组:必装(recommendations)/ 建议(备注)/ 禁止(unwantedRecommendations)。
  • 每条扩展可输入扩展 ID、显示名称、安装理由;系统提供常用扩展预置模版(Python开发、Copilot、Reflex 开发等)。
  • 教师可配置课程全局 Copilot 指令文件(.github/copilot-instructions.md),系统提供基础模版,支持在线编辑保存。
  • 按不同课程模块配置对应的 .github/instructions/*.instructions.md 文件;模版内容可在页面直接编辑。
  • 配置完成后可一键提交到仓库,自动触发 CI 检查扩展配置格式合法性。

F-T-003 GitHub 账号实名核查(含 AI 自动审查)

  • 系统通过 GitHub API(GET /users/{username})自动获取每个学生绑定账号的 name 字段。
  • 在学生-GitHub 账号对应表中展示核查状态列:已登记姓名 / 未登记姓名 / 姓名不符。
  • 教师可一键触发全班批量核查,也可对单个学生手动重新核查。
  • 对于未登记真实姓名的账号,系统显示警告并可一键发送提醒通知,附上学生 GitHub 账号登记姓名的操作说明链接。
  • 核查结果在对应表中标注,记录最近核查时间。

AI 自动审查子功能(F-T-003-AI):

判断依据:GitHub 实名(name 字段)通常应为汉语姓名。AI 通过规则+模型启发式判断实名是否可能是真实中文姓名,结果仅供教师参考,最终由教师人工确认。

  • 系统对每条 name 字段执行 AI 启发式规则检查,输出三种结论:
  • 疑似真名:含 CJK 字符(Unicode \u4e00\u9fff),长度 2–4 字,无特殊符号。
  • ⚠️ 待人工审查:纯英文/拼音/数字组合(如 Zhang Sanstudent001),或长度异常(1 字或 ≥8 字)。
  • 未填写name 字段为空或 null。
  • 教师端提供"AI 实名审查队列"看板,集中展示所有⚠️和❌条目,每条显示:
  • GitHub 用户名、当前实名值、AI 判断结论、置信度(高/中/低)、AI 给出的判断理由摘要。
  • 教师对每条审查结果可执行:通过(确认为真实姓名)/ 标记异常(记录为可疑账号)/ 发提醒(通知该学生更新 GitHub 实名)。
  • 批量操作:可一键向所有"未填写"学生发送提醒;可批量通过所有"高置信度疑似真名"条目。
  • AI 审查结果与人工确认状态分开记录:即使 AI 标记为⚠️,教师通过确认后系统记录"人工已核实通过"。

F-T-004 GitHub 账号绑定状态看板

  • 教师端展示全班学生绑定状态汇总:已绑定人数 / 未绑定人数 / 待审核人数。
  • 未绑定学生以显著颜色(红色)高亮显示,列表可按未绑定先排序。
  • 待审核请求支持一键批量通过(批量确认学生自行绑定的账号)。
  • 提供“向未绑定学生发送提醒”按鈕,直接发送首页公告或短信通知。

F-T-005 学生名单导入

  • 教师从教务管理系统导出学生名单(CSV 格式:学号,姓名,班级,课程),导入到 OAEPP。
  • 导入时自动校验字段完整性、学号唯一性、班级与课程合法性;错误行高亮显示,支持修正后重新导入。
  • 导入成功后自动创建学生账号(默认密码为学号),并发送激活邀请频道(邮件或首页公告)。
  • 支持增量导入(重复学号跳过,新增学生追加)和全量覆盖两种模式。
  • 导入日志可查,记录每次导入时间、批次、导入人、记录数。

F-T-006 需求文档编辑器

  • OAEPP 提供内置 Markdown 编辑器,预置功能需求模版(`## 模块名

F-xxx 功能名

  • 功能描述`)。
  • 集成 GitHub Copilot ,支持基于简说自动补全功能属性、验收标准、安全属性;提示词遵守 .github/copilot-instructions.md 模板。
  • 支持从外部导入已有 .md 文件,自动解析并效验功能需求格式合规性。
  • 文档渲染预览实时更新,完成后可一键提交到 GitHub 仓库。
  • 审阅模式:学生可查阅需求内容,在评论区提出清晰化意见;教师确认后封存文档。

F-T-007 Issue 关闭必填 PR 编号

重要:Issue 是需求的最小可追踪单元。关闭 Issue 时不关联 PR 则无法验证需求已实现,因此平台将其作为强制工作流规则执行。

最佳实践:学生在 PR 描述中写 Closes #Issue-ID,GitHub 在 PR 合并时自动关闭对应 Issue,完全匹配 "提交 PR → 评审 → 合并 → 关闭 Issue → 任务完成" 的教学闭环,可追溯、易管理、合规。

  • 教师在「开发运维」页面可开启「关闭 Issue 必须关联 PR」规则开关(默认开启)。
  • 规则生效后,任何角色(学生、教师、助教)在 OAEPP 内操作关闭 Issue 时,系统强制弹出对话框,要求填写关联的 PR 编号(仅接受同仓库内已存在的 PR 编号,实时调用 GitHub API 校验)。
  • 推荐方式:学生提交 PR 时在描述中加入 Closes #N,合并后 GitHub 自动关闭 Issue,平台识别此自动关闭事件并标记为「PR 自动关联关闭」,无需额外填写,满足规则要求。
  • 若关联 PR 尚未合并(Merged),平台显示警告:「该 PR 尚未合并,确认仍要关闭 Issue?」,并要求教师二次确认。
  • 已关闭 Issue 的详情页展示关联 PR 编号及其合并状态,方便复查。
  • 若通过 GitHub Web 直接关闭 Issue(绕过 OAEPP),平台 Webhook 监听到 issues.closed 事件后,检查该 Issue 是否有关联 PR;若缺失则在 OAEPP 后台生成「未填 PR 警告」记录,并在教师端 Issues 管理页高亮提示。
  • 规则配置(开启/关闭、是否允许跨仓库 PR、警告策略)保存到平台数据库,支持按课程独立设置。

F-T-008 教师成绩单导出

  • 教师可在成绩管理页面按班级批量导出全班学生成绩单,格式符合教务系统要求(.xlsx,包含固定列顺序与表头)。
  • 导出模板包含必填列:学号、姓名、班级、课程名称、出勤得分、考试得分、代码提交得分、PR 贡献得分、总评成绩、等级(A/B/C/D/F)、备注
  • 总评成绩按课程配置的权重公式自动计算(各维度权重可由教师在系统设置中调整)。
  • 支持按班级、课程、学期筛选导出范围;导出前可预览表格数据并手动修正个别单元格。
  • 导出文件名格式:课程名称_班级_学期_成绩单_导出日期.xlsx
  • 导出操作记录审计日志(导出人、时间、筛选条件、记录数)。
  • 导出时允许追加教师自定义备注列(如「附加说明」),该列内容不参与计算,仅供教务阅读。

F-T-009 学生仓库访问权限配置(GitHub Organization 模式)

依赖前提:F-T-001(学生-GitHub 账号映射表)已完整录入且账号绑定已审核通过(F-T-004)。

正确架构:通过 GitHub Organization(组织)统一管理整个班级的仓库访问权限,而非为每个学生单独添加 Collaborator。

  1. 创建 Organization:教师在 GitHub 上创建课程专属组织(如 oa-epp-2025),仓库迁移至组织下,仓库可见性设为 Private。
  2. 创建 Team:在组织内为每个班级创建对应 Team(如 class-2025-fall),设定仓库权限为 Write
  3. 批量邀请学生加入 Organization & Team:平台根据学生-GitHub 账号映射表,通过 gh api --method PUT /orgs/{org}/memberships/{username} 批量邀请学生加入组织,并通过 gh api --method PUT /orgs/{org}/teams/{team_slug}/memberships/{username} 将其加入对应班级 Team。

  4. 学生接受邀请后即可访问组织内的课程私有仓库,并可在 Issues 中将自己设为 Assignee。

  5. 支持查看当前 Team 成员列表及加入状态(已接受 / 待接受 / 已离开)。
  6. 若部分学生未绑定 GitHub 账号或邀请已过期,平台高亮提示,并支持重新发送邀请。
  7. 操作日志记录每次批量邀请的执行时间、操作人、成功数、失败数及失败原因。
  8. 学期结束后教师可一键将该 Team 从仓库移除,撤销整个班级的访问权限,无需逐一操作。

F-T-010 学生开发日志导出

  • 教师可为任意单个学生生成完整的开发日志报告(支持导出格式:PDF / HTML / Excel)。
  • 报告聚合以下 9 个维度的数据,按时间轴排列:
维度 内容
分支记录 学生创建的所有功能分支名称、创建时间、当前状态(已合并 / 开放 / 已删除)
提交历史 历次 commit 的 Hash、时间、提交信息、修改文件数、变更行数
代码质量分析 各次 commit 触发的 AI 代码质量评分(pr-agent):命名规范、注释覆盖率、潜在 Bug 数、安全警告数
PR 情况 历次 PR 编号、标题、关联 Issue、创建时间、合并时间、状态(Open / Merged / Closed)
PR 分析质量 pr-agent 对每个 PR 的完整审查结论(质量评级、主要问题列表、改进建议摘要)
教师评语 教师对该学生开发过程的历次评论与总结性评语
在线考试 历次考试名称、考试时间、总分、得分、各题答题详情与得分
考勤情况 每节课出勤状态(出勤 / 迟到 / 缺勤)及出勤率汇总
课程得分 各维度得分(出勤 / 考试 / 代码 / PR)、权重、加权后总评成绩、等级
  • 报告封面自动填写:学号、姓名、班级、课程、学期、导出日期、导出教师。
  • 数据来源聚合自平台数据库 + GitHub API(commit/PR/branch),生成时实时拉取最新状态。
  • 支持导出单个学生报告,也支持按班级批量导出全班各人报告(压缩包下载)。
  • 导出操作记录审计日志(导出人、时间、被查学生、导出格式、记录数)。

F-T-011 需求文档一键转 GitHub Issues

本功能是需求管理闭环的关键环节:需求文档经教师与学生审阅确认后,系统自动解析每条功能需求并在 GitHub 仓库中批量创建对应 Issue,作为后续开发任务的起点。

触发条件:需求文档编辑器(F-T-006)中文档状态为「已封存/已确认」,且目标仓库已配置(F-T-009)。

解析规则:系统按以下规则从 Markdown 需求文档中识别 Issue 单元:

解析项 来源字段 规则
Issue 标题 ### F-xxx 功能名 提取 F-xxx 功能名 作为标题
Issue 正文 ### 下的全部 Markdown 内容 功能描述 + 验收标准 + 安全属性(原样保留格式)
原型定位 功能编号与模块映射表 Issue 正文末尾自动附加「> 📐 原型参考:页面名」,标明该功能在哪个原型页面的哪个功能区可预览
Label 功能编号前缀 F-S-xxx → label student-featureF-T-xxxteacher-featureF-D-xxxdevops
Milestone 模块名(## 模块名 自动关联到同名 Milestone(若不存在则自动创建)
Assignee 默认不指定 教师可在预览界面手动分配

操作流程

  1. 教师点击「生成 GitHub Issues 预览」,系统解析文档并列出将要创建的 Issue 清单(标题、正文摘要、Label、Milestone、原型页面)。
  2. 教师在预览表格中可:逐条勾选/取消勾选、修改标题、调整 Label、指定 Assignee;每行显示对应原型页面快链。
  3. 点击「确认创建」,系统调用 GitHub API(POST /repos/{owner}/{repo}/issues)批量创建,并以实时进度条逐条显示创建状态(进行中 / 已创建 / 已存在已跳过 / 失败)。
  4. 每条 Issue 创建成功后立即展示 GitHub Issue 编号(如 #42)及可点击的跳转链接(github.com/.../issues/42),方便教师实时核查。
  5. 全部完成后展示结果摘要面板:成功 N 条 / 跳过 N 条(已存在)/ 失败 N 条,每行附 GitHub 链接。
  6. 已创建的 Issue 在文档中对应需求条目旁标注 Issue 编号(如 #42),支持点击跳转。

防重复机制:创建前检查仓库中是否已存在同标题 Issue(状态为 open 或 closed),若存在则提示「已存在 #N,是否跳过或更新正文?」。

权限要求:调用 GitHub API 需使用教师的 GitHub OAuth Token 或系统 App Token,仅教师角色可触发。

6. 非功能需求(学生端)

6.1 性能

  • 常规页面(课程主页、章节页)在校园网环境下首屏响应目标 ≤ 3 秒。
  • 单文件上传进度可见,上传中断后允许重新发起。

6.2 安全

  • 全站 HTTPS。
  • 关键操作(登录、提交、成绩查询)必须有身份校验与权限控制。
  • 学生仅访问本人私有数据。

6.3 可维护性

  • 功能按模块拆分:账户、课程、提交、成绩、通知。
  • 关键业务事件(提交、评分发布)记录审计日志。

7. 学生功能验收标准(第一阶段)

满足以下条件可视为学生端一期通过验收:

  1. 学生可完成登录、浏览章节、提交任务、查看成绩与评语全流程。
  2. 支持至少一种可扩展认证方案与一种持久化数据库方案。
  3. 作业多次提交与版本记录可用。
  4. 截止规则可配置并在界面正确反馈。
  5. 学生不可越权访问他人数据。

8. 文档节结构导航

节号 标题 说明
0 本方案解决的工程实践痛点 四类痛点与功能映射
1 文档目标 范围与框架说明
2 产品定位 四个核心体验目标
3 技术约束 Reflex / 认证 / 数据层约束
4 角色与边界 学生 / 教师 / 助教角色定义
5 功能需求 36 项功能完整规格(F-S / F-D / F-T)
6 非功能需求 性能 / 安全 / 可维护性
7 验收标准 学生端一期验收条件
8 文档节结构导航 0-18 节完整章节导航表
9 全功能 UML 组件图 36 项功能 PlantUML 组件图
10 GitHub 仓库配置指南 Step 0-8 + gh CLI 命令
11 完整工作流时序图 Phase 0-7 含功能编号标注
12 功能统计表 36 项,分类小计,约 6,850 行
13 统一数据库使用规范 Alembic 迁移教程 + 权限模型 + E-R草图 + SQL草稿
14 GitHub 配置文件驱动功能汇总 11 类配置文件 + 学生推荐插件
15 基于 Coolify 的容器部署方案 4 容器生产环境 + PR 预览 + 大班并发策略
16 学生实践手册 16.1 教学常见问题 / 16.2 Commit 规范 / 16.3 开发环境搭建 / 16.4 Gitflow规范
17 推荐软件开发方法:Superpowers copilot-enhance + HackLLM Memory + 循环协作工作流
18 软件测试方案 UI 测试 / 功能测试 / 基于 Superpowers 的 TDD 工作流
19 工程实践文档要求 开题报告 / 需求分析 / 概要设计 / 详细设计 / 编码 / 测试 / 答辩 PPT / 总结报告

9. 全功能UML组件图

uml diagram

工作流时序图详细解释

Phase 0:学生名单导入 - 教师从教务系统导出学生名单(CSV),上传至 OAEPP,系统自动校验并批量创建学生账号,导入结果实时反馈。 - 学生收到激活通知后可登录平台。

Phase 1:GitHub 账号注册与绑定 - 学生注册 GitHub 账号,登录 OAEPP 并填写 GitHub 用户名,系统自动校验账号有效性。 - 绑定申请需教师审核,确保实名与学号一致。

Phase 2:教师检查学生账号 - 教师在 OAEPP 查看所有学生的 GitHub 绑定状态,系统高亮未实名/未绑定/待审核学生。 - 教师可批量审核通过,学生收到绑定确认通知。

Phase 3:仓库创建与配置 - 教师通过 OAEPP 自动化脚本批量创建私有仓库,配置分支保护、CI/CD、密钥等。 - 系统自动邀请学生加入 GitHub 组织和团队,学生接受邀请后可访问仓库和 Issues。

Phase 4:需求文档 → Issues - 教师在 OAEPP 编辑需求文档,支持 AI 辅助补全和模板导入。 - 需求文档经师生共同审阅后,一键转为 GitHub Issues,便于后续开发任务分配。

Phase 5:学生开发 - 学生认领 Issue,创建功能分支开发,提交 PR 并关联 Issue,推动任务闭环。

Phase 6:AI 审查 + 教师合并 - PR 触发 GitHub Actions,pr-agent 自动生成 AI 代码审查意见。 - 教师复核 AI 审查结果,批准并合并 PR,系统自动关闭 Issue 和分支。

Phase 7:成绩更新与查看 - PR 合并后,OAEPP 自动统计学生贡献分,成绩看板实时更新。 - 教师可导出成绩单和开发日志,支持多维度数据分析与归档。

各 Phase 关键节点均有自动化校验与通知,确保流程高效、可追溯,支持大班级协作与过程性评价。

10. 配套 GitHub 仓库配置指南

本节给出从零开始配置管理平台项目 GitHub 仓库的完整步骤,每个 Step 同时提供浏览器操作gh CLI 命令行操作两种方式,按需选择。

10.1 GitHub 的替代性:Gitea(一体化开源方案)

在高校内网或私有化部署场景中,GitHub 不是唯一选择。可使用开源软件 Gitea 作为替代平台。

Gitea = 自带 Git Server 的完整平台

只需安装 Gitea 一个软件,即可自动具备以下能力:

  • Git 仓库服务(git / ssh / http 协议全支持)
  • Web 界面(交互方式类似 GitHub)
  • PR、Issue、代码评审
  • 用户、团队、权限管理
  • 远程推送(push)、拉取(pull)

并且不需要提前安装 git servergit-daemongitosisgitolite 等额外组件。

教学实施建议

  • 公网协作与开源生态场景:优先使用 GitHub(便于与 Actions、Classroom、生态插件联动)
  • 校内私有化、数据本地化场景:可采用 Gitea(降低外部平台依赖,便于统一运维)

注:本章后续步骤以 GitHub 为示例;若切换到 Gitea,可保留“仓库—分支—PR—评审—CI/CD”的工程流程不变,仅替换平台与对应接口命令。

重要前提:课程仓库应托管在 GitHub Organization(组织) 下,而非个人账号下。通过 Organization + Team 统一管理班级成员权限,是允许整个班级访问私有仓库的正确方式。在执行 Step 1 前,请先完成 Step 0 的组织创建。


Step 0:创建 GitHub Organization 与 Team

浏览器: 1. 登录 GitHub → 右上角 +New organization → 选择 Free 套餐 → 填写组织名(如 oa-epp-2025)→ 创建。 2. 组织主页 → TeamsNew team → 输入班级名(如 class-2025-fall)→ 设置为 Secret(仅成员可见)→ 创建。

gh CLI

# 查看已有组织(确认当前账号所属组织)
gh org list

# 创建班级 Team(需在组织管理员权限下执行)
gh api --method POST /orgs/oa-epp-2025/teams \
  -f name="class-2025-fall" \
  -f privacy="secret"

验证:

gh api /orgs/oa-epp-2025/teams | jq '.[].name'

前置:安装并登录 gh CLI

# macOS
brew install gh

# Ubuntu / Debian
sudo apt install gh

# 登录(浏览器授权)
gh auth login

登录后验证身份:

gh auth status

Step 1:创建仓库

浏览器:登录 GitHub → 右上角 +New repository → 填写信息后点 Create。

gh CLI

gh repo create oa-epp-platform \
  --private \
  --description "基于 Reflex 的《工程实践》课程管理平台" \
  --gitignore Python \
  --clone
字段 推荐值
Repository name oa-epp-platform
Visibility --private
.gitignore Python

Step 2:基础仓库设置

浏览器:进入 Settings → General 调整 Features 和 Pull Requests 选项。

gh CLI

# 关闭 Wiki,开启 Issues,允许 squash merge,合并后自动删除分支
gh repo edit oa-epp-platform \
  --enable-issues \
  --disable-wiki \
  --enable-squash-merge \
  --disable-merge-commit \
  --delete-branch-on-merge

Step 3:分支保护规则

浏览器:进入 Settings → Branches → Add branch protection rule,对 main 配置保护项。

gh CLI(通过 GitHub API):

gh api \
  --method PUT \
  -H "Accept: application/vnd.github+json" \
  /repos/{owner}/oa-epp-platform/branches/main/protection \
  --input - <<'EOF'
{
  "required_status_checks": {
    "strict": true,
    "contexts": ["lint-and-test"]
  },
  "enforce_admins": true,
  "required_pull_request_reviews": {
    "required_approving_review_count": 1,
    "dismiss_stale_reviews": true
  },
  "restrictions": null,
  "allow_force_pushes": false,
  "allow_deletions": false
}
EOF

{owner} 替换为你的 GitHub 用户名或组织名。


Step 4:配置 Secrets

浏览器:进入 Settings → Secrets and variables → Actions → New repository secret

gh CLI

# 交互式输入(不会在 shell 历史中留下明文)
gh secret set DATABASE_URL
gh secret set SECRET_KEY
gh secret set REFLEX_DEPLOY_TOKEN
gh secret set REFLEX_API_URL

# 验证 Secrets 已创建(只显示名称,不显示值)
gh secret list
Secret 名称 用途
REFLEX_API_URL 部署后的 Reflex 服务地址
DATABASE_URL 生产数据库连接串
REFLEX_DEPLOY_TOKEN Reflex Cloud 部署令牌
SECRET_KEY 会话签名密钥

安全提示gh secret set 使用交互模式,Secret 值不会出现在 shell 历史中。


Step 5:配置 GitHub Actions

在仓库根目录创建 .github/workflows/ci.yml

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Lint
        run: |
          pip install ruff
          ruff check .

      - name: Run tests
        run: pytest tests/ -v
        env:
          DATABASE_URL: sqlite:///./test.db

gh CLI - 查看 workflow 状态:

# 查看最新运行结果
gh run list --limit 5

# 查看某次运行详情
gh run view <run-id>

# 手动触发 workflow
gh workflow run ci.yml

Step 6:配置协作者与权限

浏览器:进入 Settings → Collaborators and teams

gh CLI

# 添加协作者(按用户名)
gh api --method PUT /repos/{owner}/oa-epp-platform/collaborators/{username} \
  -f permission=write

# 查看当前协作者列表
gh api /repos/{owner}/oa-epp-platform/collaborators
角色 permission 值
项目负责人 admin
开发成员 write
助教/测试 triage
课程观察者 read

Step 7:配置 Issue 与 PR 模板

本项目已内置 .github/PULL_REQUEST_TEMPLATE.md。添加 Issue 模板:

# 创建模板目录
mkdir -p .github/ISSUE_TEMPLATE

然后在 .github/ISSUE_TEMPLATE/ 下创建 bug_report.mdfeature_request.md,提交推送后 GitHub 自动识别。


Step 8:配置 Topics 与描述

浏览器:仓库主页右上角齿轮图标(About)修改。

gh CLI

gh repo edit oa-epp-platform \
  --description "基于 Reflex 的《工程实践》课程管理平台" \
  --homepage "https://oaepp.uwis.cn" \
  --add-topic reflex \
  --add-topic python \
  --add-topic education \
  --add-topic course-management

配置检查清单

# 一键查看仓库当前配置状态
gh repo view oa-epp-platform --json name,visibility,defaultBranchRef,hasIssuesEnabled,hasWikiEnabled,deleteBranchOnMerge

逐项确认:

  • [ ] 仓库已创建,默认分支为 main
  • [ ] Issue 已启用,Wiki 已关闭
  • [ ] PR 合并后自动删除分支
  • [ ] main 分支保护规则已生效(gh api /repos/{owner}/oa-epp-platform/branches/main/protection
  • [ ] 所有敏感配置已存入 Secrets(gh secret list
  • [ ] CI workflow 至少成功运行一次(gh run list
  • [ ] 团队成员已按角色设置正确权限
  • [ ] 仓库 Topics 和 Description 已填写

11. 完整工作流时序图

以下时序图覆盖从「学生注册 GitHub」到「查看成绩」的完整协作流程,涉及四个实体:学生教师GitHubOAEPP

uml diagram

12. 功能统计表

序号 功能编号 功能名称 预估代码量(行) 预估难度系数
学生功能(F-S)
1 F-S-001 登录 / 退出 150 ★★☆☆☆
2 F-S-002 个人资料维护 100 ★☆☆☆☆
3 F-S-003 GitHub 账号绑定(API 校验 + 审核) 250 ★★★☆☆
4 F-S-010 课程主页 120 ★★☆☆☆
5 F-S-011 章节内容浏览 100 ★★☆☆☆
6 F-S-012 公告与通知 80 ★☆☆☆☆
7 F-S-020 作业提交 200 ★★★☆☆
8 F-S-021 多次提交与版本记录 150 ★★★☆☆
9 F-S-022 截止规则 80 ★★☆☆☆
10 F-S-030 成绩实时统计(四维 Reflex 实时更新) 300 ★★★★☆
11 F-S-031 评语与改进建议 80 ★☆☆☆☆
12 F-S-032 成绩单 Excel 导出(个人) 120 ★★☆☆☆
13 F-S-040 得分看板与可视化(recharts 雷达/柱/线图) 350 ★★★★☆
14 F-S-041 学习时间线 100 ★★☆☆☆
15 F-S-050 响应式界面 80 ★★☆☆☆
16 F-S-051 异常提示与重试 60 ★☆☆☆☆
17 F-S-052 课堂点名(限时 60 秒 + 地理围栏) 280 ★★★★☆
18 F-S-053 课堂在线考试(手机端 + 草稿自动保存) 400 ★★★★★
F-S 小计(18 项) 3,000
开发运维功能(F-D)
19 F-D-001 仓库初始化 100 ★★☆☆☆
20 F-D-002 分支保护 60 ★★☆☆☆
21 F-D-003 Secrets 管理 60 ★★☆☆☆
22 F-D-004 CI 自动化(Ruff + pytest) 80 ★★☆☆☆
23 F-D-005 协作者权限管理 80 ★★☆☆☆
24 F-D-006 自动化脚本 Web 执行界面(实时流输出) 350 ★★★★☆
25 F-D-007 PR 审查与提示词模板管理(pr-agent + 验证 + 清除) 480 ★★★★★
26 F-D-008 GitHub 快捷链接面板 80 ★☆☆☆☆
F-D 小计(8 项) 1,290
教师功能(F-T)
27 F-T-001 学生-GitHub 账号映射表(CSV 批量导入) 250 ★★★☆☆
28 F-T-002 VS Code 扩展 / Copilot 提示词配置管理 180 ★★★☆☆
29 F-T-003 GitHub 账号实名核查(name 字段高亮) 150 ★★☆☆☆
30 F-T-004 GitHub 账号绑定状态看板(批量审核) 200 ★★★☆☆
31 F-T-005 学生名单导入(增量 / 覆盖 + 日志) 250 ★★★☆☆
32 F-T-006 需求文档编辑器(Copilot 辅助 + 实时预览) 400 ★★★★☆
33 F-T-007 Issue 关闭必填 PR 编号(强制关联 + Webhook 监控) 280 ★★★★☆
34 F-T-008 教师成绩单批量导出(教务格式 xlsx + 权重计算) 220 ★★★☆☆
35 F-T-009 学生仓库访问权限配置(GitHub Org + Team 模式) 180 ★★★☆☆
36 F-T-010 学生开发日志导出(9维度 PDF/HTML/xlsx 报告) 450 ★★★★★
F-T 小计(10 项) 2,560
合计(36 项) 6,850

说明

  • 预估代码量以 Reflex Python 行数为基准,含页面组件、State 事件、ORM 模型;不含测试代码。
  • 难度系数:★☆☆☆☆ 极简 / ★★☆☆☆ 简单 / ★★★☆☆ 中等 / ★★★★☆ 较难 / ★★★★★ 高难
  • 三类功能各占比:F-S 学生端 44%(3,000 行)/ F-D 开发运维 19%(1,290 行)/ F-T 教师端 37%(2,560 行)。

13. 统一数据库使用规范

13.1 核心方案

使用 Alembic 数据库迁移工具 + 统一开发数据库 + 严格修改权限控制,实现:

  • 所有学生共用 1 个远程开发数据库,库结构永远统一;
  • 学生不能随便改库,库结构变更必须走 PR → 合并 → 全体自动更新;
  • 永远只有一个官方库版本,可回滚、可追踪、可审计。

本项目采用「统一开发数据库 + Alembic 迁移版本管理」模式,确保 45 名学生开发过程中使用完全一致的数据库结构。所有数据库结构变更必须通过迁移文件提交 Git,并由教师审核合并,学生无权限直接修改数据库表结构。全体学生通过统一的迁移文件自动保持数据库版本同步,实现工程化、规范化、可追踪的团队数据库协作模式。

13.2 整体架构

统一开发数据库(只有 1 个,教师控制)
        │
        ▼
所有 45 个学生 ──── 都连这一个库
        │
        ▼
库结构变更 ──── 必须通过教师合并 Alembic 迁移文件
        │
        ▼
全体学生 git pull + reflex db upgrade → 库结构瞬间统一

权限划分:

角色 业务数据(SELECT/INSERT/UPDATE/DELETE) 表结构(ALTER/CREATE/DROP)
学生 ✅ 允许 ❌ 禁止
教师 ✅ 允许 ✅ 允许(DDL 操作)

13.3 数据库变更标准工作流

uml diagram

13.4 完整 Alembic 使用教程(学生版)

第一步:了解迁移文件是什么

迁移文件(migration)是一段 Python 脚本,描述「数据库从 A 状态变到 B 状态」的步骤。它由 Alembic 自动生成,存放在项目的 alembic/versions/ 目录。每个文件有唯一版本号,形成一条有序的版本链:

alembic/versions/
├── 001_init.py             ← 最初建表
├── 002_add_github_bind.py  ← 加字段
└── 003_add_exam_table.py   ← 加新表(最新)

第二步:查看当前数据库版本

reflex db current

输出示例:002_add_github_bind (head)

第三步:申请变更(提 Issue,等待教师批准)

在 GitHub 仓库提交 Issue,说明:

  • 需要新增哪张表 / 哪个字段
  • 字段类型、是否可空、是否有默认值
  • 业务原因

教师批准后,再进行以下步骤。

第四步:在代码中修改 rx.Model

# 示例:为 Student 模型新增 github_avatar 字段
class Student(rx.Model, table=True):
    name: str
    email: str
    github_username: str = ""
    github_avatar: str = ""   # ← 新增这一行

第五步:生成迁移文件

reflex db migrate

Alembic 自动对比当前模型与数据库结构,生成差量迁移脚本,存入 alembic/versions/

⚠️ 重要:只生成文件,不执行!此时数据库结构尚未改变。

第六步:提交迁移文件,发起 PR

git add alembic/versions/
git commit -m "feat(数据库): 为 Student 新增 github_avatar 字段"
git push origin your-branch

在 GitHub 发起 PR,标题填写变更内容,描述中写 Closes #Issue编号

第七步:教师合并后,执行同步

教师合并 PR 后,全体学生执行:

git pull
reflex db upgrade

reflex db upgrade 会将未执行的迁移文件按顺序应用到数据库,使本地库与最新版本保持一致。

常见操作速查

操作 命令
查看当前版本 reflex db current
查看所有迁移历史 reflex db history
生成迁移文件(不执行) reflex db migrate
升级到最新版本 reflex db upgrade
回退一个版本 reflex db downgrade -1
回退到指定版本 reflex db downgrade <版本号>

13.5 常见问题

Q1:学生会不会乱加字段、乱建表?

不会。学生连接的是统一开发数据库,数据库角色没有 DDL 权限(ALTER TABLE / CREATE TABLE / DROP TABLE 均被禁止)。即使在代码中修改了 rx.Model,若不执行 reflex db upgrade,数据库结构不会改变;而 upgrade 命令仅能执行已合并到主分支的迁移文件。

Q2:会不会出现每个人库版本不一样?

不会。所有人连接的是同一个远程开发数据库,该库只在教师合并迁移文件并执行 reflex db upgrade 后才会升级。

Q3:如何保证大家同步最新库?

git pull
reflex db upgrade

两条命令。建议在项目 README.md 中注明:每次 pull 代码后执行 reflex db upgrade 保持数据库同步。

Q4:如果有人乱改了本地库怎么办?

本地库与远程统一开发库是两套独立的数据库实例。本地随便改不影响团队统一库。只需重新连接远程库,执行 reflex db upgrade 即可恢复正确状态。

13.6 基于当前需求的数据库 E-R 草图(教学草案)

说明:以下为对 F-S / F-T / F-D 需求的教学版草图,用于需求评审与建模讨论;正式实现以 Alembic 迁移文件为准。

%%{init: {'theme': 'default', 'themeVariables': {'fontSize': '18px', 'fontFamily': 'Microsoft YaHei, SimHei, sans-serif'}}}%%
erDiagram
    USERS ||--o| STUDENTS : is
    USERS ||--o| TEACHERS : is
    USERS ||--o{ NOTIFICATIONS : receives

    COURSES ||--o{ CHAPTERS : contains
    COURSES ||--o{ ASSIGNMENTS : contains
    COURSES ||--o{ EXAMS : contains
    COURSES ||--o{ ENROLLMENTS : has

    STUDENTS ||--o{ ENROLLMENTS : joins
    STUDENTS ||--o{ SUBMISSIONS : submits
    STUDENTS ||--o{ ATTENDANCE_RECORDS : checks_in
    STUDENTS ||--o{ EXAM_ATTEMPTS : takes
    STUDENTS ||--o{ SCORE_ITEMS : gets
    STUDENTS ||--o{ FEEDBACKS : receives
    STUDENTS ||--o{ GITHUB_BINDINGS : binds
    STUDENTS ||--o{ PR_RECORDS : creates

    ASSIGNMENTS ||--o{ SUBMISSIONS : versions
    EXAMS ||--o{ EXAM_QUESTIONS : contains
    EXAMS ||--o{ EXAM_ATTEMPTS : has_attempts
    EXAM_ATTEMPTS ||--o{ EXAM_ANSWERS : stores
    EXAM_QUESTIONS ||--o{ EXAM_ANSWERS : answered_by

    USERS {
        bigint id PK
        varchar role
        varchar student_no
        varchar email
        varchar full_name
    }
    STUDENTS {
        bigint user_id PK,FK
        varchar class_name
    }
    TEACHERS {
        bigint user_id PK,FK
        varchar title
    }
    COURSES {
        bigint id PK
        varchar code
        varchar name
        varchar term
    }
    ENROLLMENTS {
        bigint id PK
        bigint course_id FK
        bigint student_user_id FK
    }
    CHAPTERS {
        bigint id PK
        bigint course_id FK
        int chapter_no
        varchar title
    }
    ASSIGNMENTS {
        bigint id PK
        bigint course_id FK
        bigint chapter_id FK
        varchar title
        datetime deadline
    }
    SUBMISSIONS {
        bigint id PK
        bigint assignment_id FK
        bigint student_user_id FK
        int version_no
        datetime submitted_at
    }
    EXAMS {
        bigint id PK
        bigint course_id FK
        varchar title
    }
    EXAM_QUESTIONS {
        bigint id PK
        bigint exam_id FK
        varchar qtype
        decimal score
    }
    EXAM_ATTEMPTS {
        bigint id PK
        bigint exam_id FK
        bigint student_user_id FK
        decimal total_score
    }
    EXAM_ANSWERS {
        bigint id PK
        bigint attempt_id FK
        bigint question_id FK
        decimal score
    }
    ATTENDANCE_RECORDS {
        bigint id PK
        bigint course_id FK
        bigint student_user_id FK
        varchar status
    }
    SCORE_ITEMS {
        bigint id PK
        bigint course_id FK
        bigint student_user_id FK
        varchar score_type
        decimal score
    }
    FEEDBACKS {
        bigint id PK
        bigint student_user_id FK
        bigint teacher_user_id FK
        varchar source_type
    }
    GITHUB_BINDINGS {
        bigint id PK
        bigint student_user_id FK
        varchar github_username
        varchar verify_status
    }
    PR_RECORDS {
        bigint id PK
        bigint student_user_id FK
        bigint course_id FK
        int pr_number
        varchar pr_state
    }
    NOTIFICATIONS {
        bigint id PK
        bigint user_id FK
        varchar title
        tinyint is_read
    }

13.7 基于当前需求的建库 SQL 草稿(MySQL 8.0)

说明:该 SQL 用于课堂讨论与初始原型验证,字段与索引可在实现阶段按迁移脚本细化。

-- ==========================================
-- OA-EPP 教学草案数据库(MySQL 8.0)
-- ==========================================
CREATE DATABASE IF NOT EXISTS oaepp_dev
  DEFAULT CHARACTER SET utf8mb4
  DEFAULT COLLATE utf8mb4_0900_ai_ci;

USE oaepp_dev;

CREATE TABLE users (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  role ENUM('student','teacher','admin') NOT NULL,
  student_no VARCHAR(32) NULL,
  email VARCHAR(128) NOT NULL UNIQUE,
  password_hash VARCHAR(255) NOT NULL,
  full_name VARCHAR(64) NOT NULL,
  is_active TINYINT(1) NOT NULL DEFAULT 1,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE KEY uk_users_student_no (student_no)
) ENGINE=InnoDB;

CREATE TABLE students (
  user_id BIGINT PRIMARY KEY,
  class_name VARCHAR(64) NOT NULL,
  phone VARCHAR(32) NULL,
  CONSTRAINT fk_students_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB;

CREATE TABLE teachers (
  user_id BIGINT PRIMARY KEY,
  title VARCHAR(64) NULL,
  CONSTRAINT fk_teachers_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB;

CREATE TABLE courses (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  code VARCHAR(32) NOT NULL UNIQUE,
  name VARCHAR(128) NOT NULL,
  term VARCHAR(32) NOT NULL,
  status ENUM('draft','open','closed') NOT NULL DEFAULT 'open',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

CREATE TABLE enrollments (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  course_id BIGINT NOT NULL,
  student_user_id BIGINT NOT NULL,
  enrolled_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_enroll_course FOREIGN KEY (course_id) REFERENCES courses(id),
  CONSTRAINT fk_enroll_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  UNIQUE KEY uk_enroll_course_student (course_id, student_user_id)
) ENGINE=InnoDB;

CREATE TABLE chapters (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  course_id BIGINT NOT NULL,
  chapter_no INT NOT NULL,
  title VARCHAR(255) NOT NULL,
  content_md LONGTEXT NULL,
  CONSTRAINT fk_chapter_course FOREIGN KEY (course_id) REFERENCES courses(id),
  UNIQUE KEY uk_course_chapter_no (course_id, chapter_no)
) ENGINE=InnoDB;

CREATE TABLE assignments (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  course_id BIGINT NOT NULL,
  chapter_id BIGINT NULL,
  title VARCHAR(255) NOT NULL,
  description_md LONGTEXT NULL,
  allow_resubmit TINYINT(1) NOT NULL DEFAULT 1,
  deadline DATETIME NOT NULL,
  created_by BIGINT NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_assignment_course FOREIGN KEY (course_id) REFERENCES courses(id),
  CONSTRAINT fk_assignment_chapter FOREIGN KEY (chapter_id) REFERENCES chapters(id),
  CONSTRAINT fk_assignment_teacher FOREIGN KEY (created_by) REFERENCES teachers(user_id)
) ENGINE=InnoDB;

CREATE TABLE submissions (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  assignment_id BIGINT NOT NULL,
  student_user_id BIGINT NOT NULL,
  version_no INT NOT NULL,
  file_url VARCHAR(512) NULL,
  text_content MEDIUMTEXT NULL,
  is_late TINYINT(1) NOT NULL DEFAULT 0,
  submitted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_submission_assignment FOREIGN KEY (assignment_id) REFERENCES assignments(id),
  CONSTRAINT fk_submission_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  UNIQUE KEY uk_assignment_student_version (assignment_id, student_user_id, version_no),
  KEY idx_submission_student_time (student_user_id, submitted_at)
) ENGINE=InnoDB;

CREATE TABLE exams (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  course_id BIGINT NOT NULL,
  title VARCHAR(255) NOT NULL,
  exam_type ENUM('quiz','midterm','final','practice') NOT NULL DEFAULT 'quiz',
  start_at DATETIME NOT NULL,
  end_at DATETIME NOT NULL,
  created_by BIGINT NOT NULL,
  CONSTRAINT fk_exam_course FOREIGN KEY (course_id) REFERENCES courses(id),
  CONSTRAINT fk_exam_teacher FOREIGN KEY (created_by) REFERENCES teachers(user_id)
) ENGINE=InnoDB;

CREATE TABLE exam_questions (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  exam_id BIGINT NOT NULL,
  qtype ENUM('single','multi','blank','short') NOT NULL,
  content TEXT NOT NULL,
  options_json JSON NULL,
  answer_key JSON NULL,
  score DECIMAL(6,2) NOT NULL,
  sort_no INT NOT NULL DEFAULT 1,
  CONSTRAINT fk_question_exam FOREIGN KEY (exam_id) REFERENCES exams(id),
  KEY idx_question_exam_sort (exam_id, sort_no)
) ENGINE=InnoDB;

CREATE TABLE exam_attempts (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  exam_id BIGINT NOT NULL,
  student_user_id BIGINT NOT NULL,
  status ENUM('draft','submitted','graded') NOT NULL DEFAULT 'draft',
  total_score DECIMAL(8,2) NULL,
  submitted_at DATETIME NULL,
  CONSTRAINT fk_attempt_exam FOREIGN KEY (exam_id) REFERENCES exams(id),
  CONSTRAINT fk_attempt_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  UNIQUE KEY uk_exam_student_attempt (exam_id, student_user_id)
) ENGINE=InnoDB;

CREATE TABLE exam_answers (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  attempt_id BIGINT NOT NULL,
  question_id BIGINT NOT NULL,
  answer_text TEXT NULL,
  score DECIMAL(6,2) NULL,
  graded_by BIGINT NULL,
  CONSTRAINT fk_answer_attempt FOREIGN KEY (attempt_id) REFERENCES exam_attempts(id),
  CONSTRAINT fk_answer_question FOREIGN KEY (question_id) REFERENCES exam_questions(id),
  CONSTRAINT fk_answer_teacher FOREIGN KEY (graded_by) REFERENCES teachers(user_id),
  UNIQUE KEY uk_attempt_question (attempt_id, question_id)
) ENGINE=InnoDB;

CREATE TABLE attendance_records (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  course_id BIGINT NOT NULL,
  student_user_id BIGINT NOT NULL,
  status ENUM('present','late','absent') NOT NULL,
  checkin_at DATETIME NULL,
  geo_hash VARCHAR(32) NULL,
  CONSTRAINT fk_attendance_course FOREIGN KEY (course_id) REFERENCES courses(id),
  CONSTRAINT fk_attendance_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  KEY idx_attendance_course_student (course_id, student_user_id)
) ENGINE=InnoDB;

CREATE TABLE github_bindings (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  student_user_id BIGINT NOT NULL,
  github_username VARCHAR(64) NOT NULL,
  github_name VARCHAR(128) NULL,
  verify_status ENUM('pending','approved','rejected') NOT NULL DEFAULT 'pending',
  verified_at DATETIME NULL,
  verified_by BIGINT NULL,
  CONSTRAINT fk_binding_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  CONSTRAINT fk_binding_teacher FOREIGN KEY (verified_by) REFERENCES teachers(user_id),
  UNIQUE KEY uk_binding_student (student_user_id),
  UNIQUE KEY uk_binding_username (github_username)
) ENGINE=InnoDB;

CREATE TABLE pr_records (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  student_user_id BIGINT NOT NULL,
  course_id BIGINT NOT NULL,
  issue_no INT NULL,
  pr_number INT NOT NULL,
  pr_state ENUM('open','merged','closed') NOT NULL,
  quality_score DECIMAL(6,2) NULL,
  merged_at DATETIME NULL,
  CONSTRAINT fk_pr_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  CONSTRAINT fk_pr_course FOREIGN KEY (course_id) REFERENCES courses(id),
  UNIQUE KEY uk_course_pr_no (course_id, pr_number)
) ENGINE=InnoDB;

CREATE TABLE score_items (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  course_id BIGINT NOT NULL,
  student_user_id BIGINT NOT NULL,
  score_type ENUM('attendance','exam','code','pr') NOT NULL,
  ref_id BIGINT NULL,
  score DECIMAL(6,2) NOT NULL,
  scored_by BIGINT NULL,
  scored_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES courses(id),
  CONSTRAINT fk_score_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  CONSTRAINT fk_score_teacher FOREIGN KEY (scored_by) REFERENCES teachers(user_id),
  KEY idx_score_course_student_type (course_id, student_user_id, score_type)
) ENGINE=InnoDB;

CREATE TABLE feedbacks (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  student_user_id BIGINT NOT NULL,
  teacher_user_id BIGINT NOT NULL,
  source_type ENUM('assignment','exam','pr','manual') NOT NULL,
  source_id BIGINT NULL,
  content TEXT NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_feedback_student FOREIGN KEY (student_user_id) REFERENCES students(user_id),
  CONSTRAINT fk_feedback_teacher FOREIGN KEY (teacher_user_id) REFERENCES teachers(user_id),
  KEY idx_feedback_student_time (student_user_id, created_at)
) ENGINE=InnoDB;

CREATE TABLE notifications (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id BIGINT NOT NULL,
  title VARCHAR(255) NOT NULL,
  body TEXT NOT NULL,
  category ENUM('announcement','deadline','grade','system') NOT NULL DEFAULT 'announcement',
  is_read TINYINT(1) NOT NULL DEFAULT 0,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_notification_user FOREIGN KEY (user_id) REFERENCES users(id),
  KEY idx_notification_user_read_time (user_id, is_read, created_at)
) ENGINE=InnoDB;

14. GitHub 配置文件驱动功能汇总

14.1 哪些功能通过配置文件实现

以下功能可以完全通过向仓库提交配置文件来实现,无需在 GitHub 网页界面手动点击。学生应认识到:配置即代码(Configuration as Code),项目行为由文件版本化控制,可审计、可回滚、可复现。

功能 ID 功能名称 配置文件路径 实现内容
F-D-004 CI 自动化(Ruff + pytest) .github/workflows/ci.yml 每次 push/PR 触发:代码风格检查(Ruff)、单元测试(pytest)
F-D-004 Commit 格式自动校验 .github/workflows/commitlint.yml + .commitlintrc.json 对 PR 内所有 commit 消息进行 Conventional Commits 格式校验
F-D-007 PR 审查提示词模板 .github/.pr_agent.toml 配置 pr-agent 行为:模型、语言、审查深度
F-D-007 PR 审查提示词内容 .github/review-prompts/*.md 自定义审查侧重点(安全/性能/可读性等)
F-T-007 Issue 关闭必填 PR 编号 .github/workflows/check-issue-pr.yml 监听 issues.closed 事件,校验评论中是否包含有效 PR 引用
—— Issue 提交模板 .github/ISSUE_TEMPLATE/task.yml 规范 Issue 格式:必填字段(任务描述、验收标准、截止时间)
—— Issue 提交模板(Bug 报告) .github/ISSUE_TEMPLATE/bug_report.yml 规范 Bug 报告格式:复现步骤、预期行为、实际行为
—— PR 提交模板 .github/pull_request_template.md 规范 PR 描述:变更说明、测试情况、关联 Issue(Closes #xxx
F-D-002 代码所有者(分支保护辅助) .github/CODEOWNERS 指定关键目录/文件的审查人,PR 合并前必须获得对应人员 Approve
F-T-002 VS Code 推荐扩展 .vscode/extensions.json 统一团队 IDE 插件:Python、Ruff、Pylance、GitHub Copilot
F-T-002 Copilot 指令配置 .github/copilot-instructions.md 定制 Copilot 回答风格、禁用词、代码规范要求
F-T-002 Copilot 提示词模板 .github/prompts/*.prompt.md 封装常用任务提示词(如「生成 rx.Model」「写单元测试」)

:分支保护规则(F-D-002)的核心部分(Required Reviews、Required Status Checks)通过 GitHub API 或 gh CLI 设置,无法仅靠配置文件推送完成;.github/CODEOWNERS 是其配合手段。

14.2 平台对配置文件的管理能力

所有上述配置文件均在平台中受统一管理。对每一项配置文件,平台提供以下能力:

能力 说明
在线编辑 在平台界面直接修改文件内容,无需本地 clone
内置模板 每种文件提供可选的官方模板(最小可用版本 + 完整版本)
Dry-Run 验证 修改后在测试仓库触发一次真实事件,预览效果,不影响正式仓库
验证痕迹清除 验证产生的测试 Issue / PR / Workflow Run 自动关闭并标记 [test],可一键清除
版本历史 通过 Git 记录文件变更历史,支持一键回退到上一版本

这一能力集成在 F-D-007(PR 审查与提示词模板管理)的扩展框架中,并对所有配置文件统一开放。

14.3 学习建议(教师说明)

建议教师在讲解本节时,以「提交一个文件 → 仓库行为改变」为核心演示思路,帮助学生建立以下认知:

  1. GitHub Actions 工作流文件(.github/workflows/*.yml:YAML 文件描述「什么事件触发什么操作」,是自动化的核心载体。
  2. 模板文件(ISSUE_TEMPLATE/pull_request_template.md:预填表单,引导贡献者提交规范信息,属于协作约束而非代码逻辑。
  3. pr-agent 配置(.pr_agent.tomlreview-prompts/:AI 审查行为由文件版本化控制,修改提示词等同于更新审查规则。
  4. VS Code / Copilot 配置(.vscode/.github/copilot-instructions.md:开发环境标准化,所有人打开项目即获得统一的工具链配置。

14.4 学生 VS Code 推荐插件清单

以下插件已在课程开发服务器上安装并验证可用,建议通过 .vscode/extensions.json 统一推荐给所有学生,确保开发环境一致。

插件 ID 插件名称 用途
shd101wyy.markdown-preview-enhanced Markdown Preview Enhanced Markdown 文档实时预览,支持 PlantUML、Mermaid、数学公式渲染
baryon.baryon-markdown-live-preview Baryon Markdown Live Preview 轻量 Markdown 实时预览,适合快速查看文档
github.vscode-pull-request-github GitHub Pull Requests 在 VS Code 内直接管理 PR、Issue,无需切换浏览器
github.copilot GitHub Copilot AI 代码补全与建议(需单独授权)
github.copilot-chat GitHub Copilot Chat 与 Copilot 对话,辅助代码生成与调试
copilot-enhance.copilot-enhance-tool Copilot Enhance(Superpowers) 增强 Copilot 工作流,提供循环协作与多步任务自动化能力
hacklm.hacklm-memory HackLLM Memory AI 助手跨会话记忆管理,持久化编程规范与项目上下文
ms-vscode-remote.remote-ssh Remote - SSH 通过 SSH 连接远程服务器开发,学生连接课程服务器必备
ms-python.python Python Python 语言支持:语法高亮、Linting、调试
ms-python.vscode-pylance Pylance Python 智能类型推断与自动补全(Reflex 开发必备)
charliermarsh.ruff Ruff Python 代码风格检查与格式化(与 CI 保持一致)
ms-python.debugpy Python Debugger Python 断点调试
marp-team.marp-vscode Marp for VS Code Markdown 幻灯片制作,用于书写答辩 PPT(Section 19.9)

.vscode/extensions.json 推荐配置(可直接提交至仓库):

{
  "recommendations": [
    "shd101wyy.markdown-preview-enhanced",
    "github.vscode-pull-request-github",
    "github.copilot",
    "github.copilot-chat",
    "copilot-enhance.copilot-enhance-tool",
    "hacklm.hacklm-memory",
    "ms-vscode-remote.remote-ssh",
    "ms-python.python",
    "ms-python.vscode-pylance",
    "charliermarsh.ruff",
    "ms-python.debugpy",
    "marp-team.marp-vscode"
  ]
}

学生打开项目时,VS Code 将自动弹出「安装推荐插件」提示,一键完成环境统一。

15. 基于 Coolify 的容器部署方案

15.1 方案概述

本平台采用 Coolify(自托管 PaaS)进行生产部署与 PR 预览环境管理。Coolify 基于 Docker,内置 Traefik 反向代理、Let's Encrypt HTTPS、GitHub 集成,适合高校自建服务器场景。

两套环境:

环境 触发条件 生命周期 用途
生产环境 合并到 main 持久运行 教师和学生日常使用
PR 预览环境 PR 创建/更新 PR 关闭后自动销毁 合并前验证功能、AI 审查真实运行效果

15.2 容器规划

生产环境(4 个容器)

容器名 镜像 对外端口 职责
oaepp-app 自构建(Python 3.11 + Reflex) 8000(经 Traefik 代理为 443) 平台主应用:Reflex 前端 + WebSocket 后端
oaepp-mysql mysql:8.0 仅内网(3306) 统一开发数据库,存储全部业务数据
oaepp-redis redis:7-alpine 仅内网(6379) 会话缓存、事件队列(Reflex 实时推送)
oaepp-phpmyadmin phpmyadmin:latest 5050(管理员专用,按需启动) 教师 DDL 操作与数据库可视化管理

PR 预览环境(每个 PR 独立 2 个容器)

容器名 说明
oaepp-preview-{pr} 以 PR 分支代码构建的平台镜像
oaepp-mysql-preview-{pr} 隔离的测试数据库,预填 fixture 数据

PR 预览环境由 GitHub Actions 调用 Coolify API 自动创建,PR 关闭后自动销毁,不影响生产数据库。

15.3 参考配置文件

docker-compose.yml(生产环境)

version: "3.9"

services:
  oaepp-app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: mysql+pymysql://oaepp:${MYSQL_ROOT_PASSWORD}@oaepp-mysql:3306/oaepp_dev
      REDIS_URL: redis://oaepp-redis:6379
      SECRET_KEY: ${SECRET_KEY}
      REFLEX_ENV: prod
    depends_on:
      oaepp-mysql:
        condition: service_healthy
      oaepp-redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/ping"]
      interval: 30s
      timeout: 10s
      retries: 3
    labels:
      # Coolify / Traefik 自动 HTTPS
      - "traefik.enable=true"
      - "traefik.http.routers.oaepp.rule=Host(`oaepp.yourdomain.com`)"
      - "traefik.http.routers.oaepp.tls.certresolver=letsencrypt"

  oaepp-mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_USER: oaepp
      MYSQL_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: oaepp_dev
    volumes:
      - oaepp_mysql_data:/var/lib/mysql
      # 初始化学生账号(仅 DML 权限)
      - ./scripts/init-student-role.sql:/docker-entrypoint-initdb.d/01-student-role.sql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  oaepp-redis:
    image: redis:7-alpine
    volumes:
      - oaepp_redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  oaepp-phpmyadmin:
    image: phpmyadmin:latest
    profiles: ["admin"]   # 仅需要时启动:docker compose --profile admin up
    environment:
      PMA_HOST: oaepp-mysql
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    ports:
      - "5050:80"
    depends_on:
      - oaepp-mysql

volumes:
  oaepp_mysql_data:
  oaepp_redis_data:

Dockerfile(Reflex 应用镜像)

FROM python:3.11-slim

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl nodejs npm \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# 编译前端(生成静态资源)
RUN reflex export --no-zip

EXPOSE 8000
CMD ["reflex", "run", "--env", "prod", "--loglevel", "warning"]

scripts/init-student-role.sql(学生权限初始化)

-- 创建学生专用账号(仅 DML 权限,无 DDL 权限)
CREATE USER IF NOT EXISTS 'student_role'@'%' IDENTIFIED BY 'changeme';
GRANT SELECT, INSERT, UPDATE, DELETE ON oaepp_dev.* TO 'student_role'@'%';
FLUSH PRIVILEGES;
-- 注意:未授予 CREATE / ALTER / DROP 权限

⚠️ 生产环境请将 student_role 的密码替换为强密码并通过 Secrets 注入,勿硬编码。

15.4 PR 预览环境工作流

# .github/workflows/pr-preview.yml
name: PR 预览环境

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]

jobs:
  deploy-preview:
    if: github.event.action != 'closed'
    runs-on: ubuntu-latest
    steps:
      - name: 调用 Coolify API 创建/更新预览环境
        run: |
          curl -X POST "${{ secrets.COOLIFY_API_URL }}/api/v1/applications" \
            -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{
              "name": "oaepp-preview-${{ github.event.number }}",
              "git_branch": "${{ github.head_ref }}",
              "environment_variables": {
                "DATABASE_URL": "mysql+pymysql://oaepp:preview@oaepp-mysql-preview-${{ github.event.number }}:3306/oaepp_preview",
                "REFLEX_ENV": "dev"
              }
            }'

      - name: 评论预览地址
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚀 预览环境已部署:https://oaepp-preview-${{ github.event.number }}.yourdomain.com'
            })

  destroy-preview:
    if: github.event.action == 'closed'
    runs-on: ubuntu-latest
    steps:
      - name: 销毁预览环境
        run: |
          curl -X DELETE "${{ secrets.COOLIFY_API_URL }}/api/v1/applications/oaepp-preview-${{ github.event.number }}" \
            -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}"

15.5 Coolify 配置要点

配置项 值 / 说明
部署触发 推送到 main 自动重新部署生产环境
HTTPS Traefik + Let's Encrypt,自动续期
环境变量 在 Coolify 控制台配置 MYSQL_ROOT_PASSWORDSECRET_KEY 等,不写入 .env 文件
健康检查 使用 docker-compose.ymlhealthcheck 定义,Coolify 仅在健康后切换流量
数据卷 oaepp_mysql_data 挂载至 Coolify 持久卷,防止重部署丢失数据
PR 预览 通过 GitHub Actions + Coolify REST API 自动管理,无需手动操作

15.6 大班并发问题:预览容器溢出解决方案

问题分析

45 名学生同时开发,若每个 PR 自动创建 2 个独立容器(应用 + 数据库),极端情况下同时存在 90+ 个容器,服务器内存必然耗尽。

典型资源测算(4 核 8 GB 服务器):

组件 单容器内存占用 10 个 PR × 2 容器
Reflex 应用(开发模式) ~300 MB 3,000 MB
MySQL(轻量配置) ~256 MB 2,560 MB
生产环境容器 ~800 MB ——
总计 —— ~5 GB(超出可用内存)

解决方案:三级策略组合

策略一:按需触发,非自动部署(首选)

不对所有 PR 自动创建预览,改为「评论触发」模式。学生或教师在 PR 中评论 /preview,才启动预览环境:

# .github/workflows/pr-preview-on-demand.yml
on:
  issue_comment:
    types: [created]

jobs:
  deploy:
    if: |
      github.event.issue.pull_request &&
      github.event.comment.body == '/preview'
    runs-on: ubuntu-latest
    steps:
      - name: 调用 Coolify API 部署预览
        run: |
          # ... 与 15.4 相同的 curl 调用

效果:同时活跃的预览环境数量由并发 PR 数量降至教师主动检视的 PR 数量(通常 3-5 个)。


策略二:共享预览数据库 + 资源限制

不为每个 PR 创建独立数据库容器,所有 PR 预览环境共用一个「预览专用 MySQL」,通过独立数据库名区分不同 PR 数据:

# docker-compose.preview.yml(所有 PR 预览共享)
services:
  oaepp-mysql-preview:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${PREVIEW_DB_PASSWORD}
    volumes:
      - oaepp_preview_db_data:/var/lib/mysql

  oaepp-preview-${PR_NUMBER}:
    build: .
    environment:
      DATABASE_URL: mysql+pymysql://root:${PREVIEW_DB_PASSWORD}@oaepp-mysql-preview:3306/oaepp_pr_${PR_NUMBER}
      DB_NAME: oaepp_pr_${PR_NUMBER}   # 每个 PR 使用独立数据库
    mem_limit: 512m      # 内存上限
    cpus: "0.5"          # CPU 上限

效果:将每个 PR 的容器数从 2 个降至 1 个,数据库资源节省 50%。


策略三:不活跃预览自动暂停

超过 4 小时无人访问的预览环境自动暂停(docker pause),有流量时自动恢复(docker unpause),暂停状态几乎不占用 CPU,仅保留少量内存快照:

# .github/workflows/preview-cleanup.yml
on:
  schedule:
    - cron: '0 * * * *'   # 每小时检查一次

jobs:
  pause-inactive:
    runs-on: ubuntu-latest
    steps:
      - name: 暂停超过 4 小时无访问的预览环境
        run: |
          curl -X POST "${{ secrets.COOLIFY_API_URL }}/api/v1/applications/pause-inactive" \
            -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}" \
            -d '{"max_idle_hours": 4, "prefix": "oaepp-preview-"}'

补充:PR 关闭时立即销毁对应预览容器(见 15.4 的 destroy-preview job)。


推荐配置(大班 45 人场景)

参数 推荐值 说明
预览触发方式 评论 /preview 非自动部署
最大并发预览数 10 超出时拒绝创建并提示
单预览容器内存上限 512 MB mem_limit: 512m
预览数据库模式 共享 MySQL + 独立数据库隔离 节省数据库容器
不活跃自动暂停 4 小时 减少常驻内存占用
PR 关闭后销毁 立即(PR 关闭 Webhook 触发) 释放资源
生产环境容器优先级 OOM 保护(oom_score_adj: -500 防止被内核 Kill

教学建议:预览环境主要供教师审查重点 PR 使用,学生通过 CI 日志(绿/红)判断代码是否合格,无需每个 PR 都部署完整预览环境。


15.7 教师 Coolify API 配置指南

本节提供教师使用 Coolify REST API 完成从零到全自动 CI/CD 的操作流程,无需在 Coolify Web 界面手动点击,所有配置均可通过脚本批量执行,适合学期初一次性配置。

前置条件

  • Coolify 实例已运行,教师账号具备 adminteam owner 权限
  • 获取 API Token:Coolify 控制台 → SettingsAPI TokensNew API Token,权限选 read-write
  • 环境变量:export COOLIFY_URL=https://coolify.156.239.252.40.nip.ioexport COOLIFY_TOKEN=<your-token>

15.7.1 创建项目(Project)

# 创建工程实践4课程项目
curl -s -X POST "$COOLIFY_URL/api/v1/projects" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "OA-EPP",
    "description": "工程实践4在线平台"
  }' | jq '.uuid'
# 记录返回的 project_uuid,后续步骤使用
export PROJECT_UUID="<上一步返回的 uuid>"

15.7.2 配置 Git 仓库地址与应用

# 创建 Nixpacks/Docker 应用,关联 Git 仓库
curl -s -X POST "$COOLIFY_URL/api/v1/applications" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"project_uuid\": \"$PROJECT_UUID\",
    \"environment_name\": \"production\",
    \"server_uuid\": \"<your-server-uuid>\",
    \"name\": \"oaepp-app\",
    \"git_repository\": \"https://github.com/roboticsystem/OA-EPP\",
    \"git_branch\": \"main\",
    \"build_pack\": \"dockerfile\",
    \"dockerfile_location\": \"/Dockerfile\",
    \"ports_exposes\": \"8000\",
    \"base_directory\": \"/\"
  }" | jq '{uuid: .uuid, name: .name}'
export APP_UUID="<上一步返回的 uuid>"

服务器 UUID 可通过 curl "$COOLIFY_URL/api/v1/servers" -H "Authorization: Bearer $COOLIFY_TOKEN" | jq '.[].uuid' 获取。


15.7.3 配置 git commit 自动部署(Webhook)

Coolify 支持通过 GitHub Webhook 在 push 事件后自动部署。

# 获取应用的 Webhook URL
curl -s "$COOLIFY_URL/api/v1/applications/$APP_UUID" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  | jq '.webhook_url'
# 输出类似:https://coolify.xxx.nip.io/webhooks/source/github/events/manual?uuid=xxx&token=xxx

将上述 Webhook URL 添加至 GitHub 仓库:

# 通过 GitHub API 注册 Webhook
curl -s -X POST "https://api.github.com/repos/roboticsystem/OA-EPP/hooks" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"web\",
    \"active\": true,
    \"events\": [\"push\", \"pull_request\"],
    \"config\": {
      \"url\": \"<上一步获取的 webhook_url>\",
      \"content_type\": \"json\",
      \"insecure_ssl\": \"0\"
    }
  }"

之后,每次 git pushmain 分支,Coolify 将自动拉取最新代码并重新部署。


15.7.4 配置 PR 预览环境

Coolify 支持对每个 PR 自动创建预览环境:

# 启用 PR 预览功能
curl -s -X PATCH "$COOLIFY_URL/api/v1/applications/$APP_UUID" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "preview_url_template": "pr-{PR_NUMBER}.preview.156.239.252.40.nip.io",
    "is_preview_deployments_enabled": true,
    "custom_labels": "traefik.enable=true"
  }'

配置完成后,每次 GitHub PR 创建/更新时,Coolify 将自动部署一个独立预览环境。


15.7.5 获取预览地址

# 查看当前应用的所有预览环境
curl -s "$COOLIFY_URL/api/v1/applications/$APP_UUID/previews" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  | jq '[.[] | {pr_number: .pull_request_id, url: .fqdn, status: .status}]'

输出示例:

[
  { "pr_number": 12, "url": "pr-12.preview.156.239.252.40.nip.io", "status": "running" },
  { "pr_number": 15, "url": "pr-15.preview.156.239.252.40.nip.io", "status": "building" }
]

教师可将预览 URL 直接评论至对应 PR,或通过 GitHub Actions 自动写入 PR 评论(见 Section 11 工作流时序图 Phase 5)。


15.7.6 一键初始化脚本(学期初执行)

将上述步骤整合为可重复执行的初始化脚本,存放于 scripts/coolify_setup.sh

#!/usr/bin/env bash
# coolify_setup.sh — 学期初一键配置 Coolify CI/CD
# 依赖:curl, jq;需提前 export COOLIFY_URL / COOLIFY_TOKEN / GITHUB_TOKEN

set -euo pipefail

echo "=== 1. 创建 OA-EPP 项目 ==="
PROJECT_UUID=$(curl -sf -X POST "$COOLIFY_URL/api/v1/projects" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"OA-EPP","description":"工程实践4在线平台"}' | jq -r '.uuid')
echo "Project UUID: $PROJECT_UUID"

echo "=== 2. 获取服务器 UUID ==="
SERVER_UUID=$(curl -sf "$COOLIFY_URL/api/v1/servers" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" | jq -r '.[0].uuid')
echo "Server UUID: $SERVER_UUID"

echo "=== 3. 创建应用 ==="
APP_UUID=$(curl -sf -X POST "$COOLIFY_URL/api/v1/applications" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"project_uuid\":\"$PROJECT_UUID\",\"environment_name\":\"production\",
       \"server_uuid\":\"$SERVER_UUID\",\"name\":\"oaepp-app\",
       \"git_repository\":\"https://github.com/roboticsystem/OA-EPP\",
       \"git_branch\":\"main\",\"build_pack\":\"dockerfile\",
       \"ports_exposes\":\"8000\"}" | jq -r '.uuid')
echo "App UUID: $APP_UUID"

echo "=== 4. 获取 Webhook URL ==="
WEBHOOK_URL=$(curl -sf "$COOLIFY_URL/api/v1/applications/$APP_UUID" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" | jq -r '.webhook_url')
echo "Webhook URL: $WEBHOOK_URL"

echo "=== 5. 注册 GitHub Webhook ==="
curl -sf -X POST "https://api.github.com/repos/roboticsystem/OA-EPP/hooks" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"web\",\"active\":true,\"events\":[\"push\",\"pull_request\"],
       \"config\":{\"url\":\"$WEBHOOK_URL\",\"content_type\":\"json\"}}"

echo "=== 6. 启用 PR 预览 ==="
curl -sf -X PATCH "$COOLIFY_URL/api/v1/applications/$APP_UUID" \
  -H "Authorization: Bearer $COOLIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"is_preview_deployments_enabled\":true,
       \"preview_url_template\":\"pr-{PR_NUMBER}.preview.156.239.252.40.nip.io\"}"

echo "✅ Coolify CI/CD 配置完成!"
echo "   生产部署:main 分支 push 后自动触发"
echo "   PR 预览:创建 PR 后自动部署,地址格式 pr-<N>.preview.156.239.252.40.nip.io"

本节汇总工程实践 4 课程中学生需要掌握的三类实践规范:教学常见问题应对(16.1)、Git Commit 规范(16.2)、本机开发环境搭建(16.3)。

16.1 教学常见问题与平台应对策略

本小节针对工程实践 4 教学中的典型问题,说明平台如何检测、预警、并支持教师采取干预措施。

16.1.1 学生不认领 Issue(未参与实践)

问题描述:学生没有在 GitHub 上 Assign 任何 Issue 给自己,表明其未实际参与开发任务分配。

平台检测手段

  • 教师通过 F-T-010(学生开发日志导出)中的「Issue 参与度」维度,查看每位学生认领 Issue 数量;
  • F-T-004(GitHub 绑定状态看板)显示哪些学生已绑定 GitHub 账号但零 Issue 活动;
  • 系统在得分看板(F-S-040)中将「无 Issue」学生高亮标记为⚠️。

建议应对方式

严重程度 判断标准 教师操作
提醒 截止日 3 天前,该学生 Issue 认领数为 0 发送平台站内通知(F-S-012)
警告 截止日当天,仍无认领记录 标记为「未参与」,可视作该任务得分 0
记录 整个项目周期无任何 Issue 活动 导出开发日志时单独生成该生报告,留存教学档案

平台不替学生认领 Issue,所有参与行为必须由学生本人操作,确保记录真实反映贡献。


16.1.2 学生不提交 PR(实践未完成)

问题描述:学生有 Issue 认领记录,但始终未提交 PR 关闭 Issue,表明任务开始但未完成。

平台检测手段

  • F-T-007(Issue 关闭必填 PR 编号)强制规则:Issue 关闭时若无有效 PR 引用,Webhook 自动重新打开 Issue 并通知学生;
  • F-T-010 导出报告「PR 提交率」维度展示每位学生 Issue 认领数 vs PR 提交数的比值;
  • 教师在 F-T-004 看板中可筛选「有 Issue 无 PR」的学生列表,一键发送提醒。

建议应对方式

截止状态 判断标准 建议操作
进行中(宽限期内) Issue 已认领,PR 尚未提交,但未到截止日 无需干预,CI 会在 PR 提交后自动审查
逾期未提交 截止日已过,Issue 仍处于 Open 状态 教师手动将 Issue 标记 overdue,得分置 0
提交但 CI 失败 PR 已提交,但 Ruff/pytest 未通过 不算完成,学生需修复后重新推送

16.1.3 只有一次 Commit(提交习惯不规范)

问题描述:学生将所有代码改动压缩为一次提交,无法体现开发过程,也无法进行有效代码审查。

平台检测手段

  • F-T-010 导出报告「Commit 频率」维度:统计每位学生的 PR 平均 Commit 数;
  • 低于阈值(默认 3 次 / PR)的学生在教师看板高亮显示;
  • pr-agent 审查时(F-D-007)在 AI 评语中自动提示:「本 PR 仅含 1 次提交,建议将开发过程拆分为多个语义化提交」。

教学规范要求(建议写入项目 README)

Commit 规范:
1. 每完成一个独立功能点,提交一次 commit
2. commit 消息使用语义化格式(参考项目 Commit Message 规范)
3. 单个 PR 内 commit 数量建议不少于 3 次
4. 不接受 "all changes" / "fix" 等无意义 commit 消息

建议应对方式

情形 判断标准 教师操作
轻微不规范 PR 含 2 次提交 pr-agent 自动给出建议,教师 Request Changes
明显不规范 PR 仅含 1 次提交,且改动量 > 100 行 直接 Close PR,要求学生拆分后重新提交
习惯性压提交 学生多个 PR 均为单次提交 扣除「开发规范」项目得分,写入评语

16.1.4 其他常见问题速查

问题 检测方式 平台功能
PR 描述为空 PR 模板必填字段校验 .github/pull_request_template.md 结构约束
未关联 Issue 关闭 PR Webhook 监听 PR 合并事件 F-T-007 强制关联规则
分支直接推送 main GitHub 分支保护规则 F-D-002 分支保护配置
CI 失败仍请求合并 Required Status Checks F-D-004 CI 自动化 + 分支保护
抄袭代码(与他人高度相似) 代码相似度检测(扩展功能) 教师手动审查 + pr-agent 标注

16.2 学生 GitHub Commit 规范

本小节规定《工程实践 4》项目中,学生提交代码时必须遵守的 Commit 规范。规范基于 Conventional Commits 标准,并针对课程场景进行简化。

16.2.1 Commit Message 格式

<类型>(<范围>): <简要描述>

[可选:详细说明]

示例:

feat(学生提交): 实现作业文件上传功能

支持单文件上传,进度条显示,文件类型校验(仅允许 .py / .zip)。
上传成功后自动刷新提交记录列表。
fix(成绩页面): 修复 GPA 计算时除零错误

当学生尚无任何成绩时,weight_sum 为 0,导致 ZeroDivisionError。
改为在 weight_sum == 0 时直接返回 0.0。
refactor(数据模型): 将 User 模型的 github_url 字段重命名为 github_profile

16.2.2 类型(Type)说明

类型 含义 示例场景
feat 新增功能 实现登录页面、添加新字段
fix 修复 Bug 修正计算错误、修复页面崩溃
refactor 代码重构(不改变功能) 提取公共函数、重命名变量
style 格式调整(不影响逻辑) 缩进修正、空行整理
test 测试相关 新增单元测试、修改 fixture
docs 文档变更 更新 README、补充注释
chore 杂务(构建、依赖等) 更新 requirements.txt

16.2.3 范围(Scope)建议

范围用于说明改动所属模块,使用中文描述:

场景 推荐范围写法
学生账户相关 学生账户
作业提交功能 作业提交
成绩与反馈 成绩页面
数据模型变更 数据模型
CI/CD 配置 CI配置
数据库迁移 数据库迁移

16.2.4 提交频率要求

核心原则:每完成一个独立的小功能点,立即提交一次。

要求 说明
每个 PR 不少于 3 次 commit 体现开发过程,避免「一次性压提交」
单次 commit 改动行数建议 ≤ 100 行 超过 100 行应拆分为多个功能点分别提交
禁止在 commit 消息中使用无意义描述 fixupdateall changes修改aaa
每次 commit 前确保代码可运行 不提交明显会导致语法错误的半成品代码

好的提交拆分示例(同一个 PR,3 次提交):

1. feat(成绩页面): 新增成绩列表组件(仅前端,数据为静态)
2. feat(成绩页面): 连接后端 API,改为动态加载成绩数据
3. fix(成绩页面): 修复空成绩时组件渲染崩溃

错误示例(不接受):

1. all changes   ← 禁止:无意义描述
2. 改了很多东西   ← 禁止:描述不具体
3. fix           ← 禁止:缺少范围和说明

16.2.5 自动检查机制

项目通过 GitHub Actions 对 commit 消息进行自动格式校验:

# .github/workflows/commitlint.yml
name: Commit Message 检查

on: [pull_request]

jobs:
  commitlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: wagoid/commitlint-github-action@v5
        with:
          configFile: .commitlintrc.json
// .commitlintrc.json
{
  "extends": ["@commitlint/config-conventional"],
  "rules": {
    "type-enum": [2, "always", ["feat","fix","refactor","style","test","docs","chore"]],
    "subject-min-length": [2, "always", 5],
    "header-max-length": [2, "always", 100]
  }
}

不符合规范的 commit 消息将导致 CI 失败,PR 无法合并。


16.2.6 快速参考卡

✅ 正确格式:
feat(作业提交): 实现文件上传进度条
fix(登录页面): 修复 Token 过期后未自动跳转
refactor(数据模型): 提取 BaseModel 公共字段

❌ 禁止格式:
update
fix bug
修改了一些东西
all changes
aaaaaa

建议将此规范打印或贴在学生工位显眼处,并在项目 README.md 第一节放置指向本规范的链接。


16.2.7 提交内容规范:哪些文件不应进入仓库

仓库只应包含源代码与配置文件,以下类型的文件必须通过 .gitignore 排除,禁止提交。

禁止提交的文件类型

类别 典型文件 / 目录 原因
编译产物 __pycache__/*.pyc*.pyo*.pyd 由 Python 自动生成,每台机器路径不同,提交后产生无意义的 diff
前端构建产物 .web/(Reflex 编译输出)、node_modules/ 体积巨大(数百 MB),可由 reflex export 重新生成
虚拟环境 .venv/venv/env/ 包含数万个依赖文件,应由 pip install -r requirements.txt 恢复
数据库文件 *.db*.sqlite3*.sqlite 包含运行时数据,不属于源代码;统一数据库通过迁移文件管理
密钥与 Secrets .env*.pem*.keysecrets.json 一旦提交即永久留存于 Git 历史,即使后续删除也可被还原
IDE 配置(个人) .idea/*.swp*.swo~$* 仅对当前开发者有效,提交后影响他人 .gitignore 优先级
操作系统文件 .DS_Store(macOS)、Thumbs.db(Windows) 系统自动生成,与项目无关
日志与临时文件 *.logtmp/temp/ 运行时产物,内容随环境变化

⚠️ 一旦将 .env 或密钥提交至 GitHub,必须立即吊销并重新生成相应密钥,即使立刻删除文件,历史记录中仍可被提取。

标准 .gitignore(Reflex + Python 项目)

将以下内容保存为项目根目录的 .gitignore,并提交至仓库:

# Python 编译产物
__pycache__/
*.py[cod]
*$py.class
*.pyd

# 虚拟环境
.venv/
venv/
env/
ENV/

# Reflex 构建产物
.web/
*.db

# 环境变量与 Secrets(绝不提交)
.env
.env.*
!.env.example

# 数据库文件
*.sqlite3
*.sqlite

# IDE 配置(个人)
.idea/
.vscode/settings.json
*.swp
*.swo

# 操作系统
.DS_Store
Thumbs.db
desktop.ini

# 日志与临时文件
*.log
tmp/
temp/

说明.vscode/extensions.json(推荐插件配置)和 .vscode/launch.json(调试配置)应当提交,有助于团队统一开发环境。.vscode/settings.json(个人设置)不提交,留给每位开发者自行配置。

如果已误将文件提交,如何补救

# 从 Git 历史中彻底删除某文件(危险操作,执行前请通知团队)
git rm --cached <文件名>
git commit -m "chore(仓库清理): 从追踪列表删除 <文件名>"

# 将文件加入 .gitignore 防止再次提交
echo "<文件名>" >> .gitignore
git add .gitignore
git commit -m "chore(仓库清理): 补充 .gitignore 排除 <文件名>"

# 如果误提交了密钥,还需通知教师立即吊销该密钥

注意:git rm --cached 只从 Git 追踪中移除,不删除本地文件。如果需要从历史记录中彻底清除(如密钥泄漏),需使用 git filter-repo 工具,操作前务必备份仓库。


16.3 学生本机开发环境搭建与提交指南

本小节面向45 名学生,说明如何在本机搭建 Reflex 开发环境、本地测试运行,并将代码提交到 GitHub。

16.3.1 前置条件

在本机安装以下工具(一次性操作):

工具 版本要求 下载地址
Python 3.11 及以上 https://www.python.org/downloads/
Node.js 18 LTS 及以上 https://nodejs.org/
Git 任意新版 https://git-scm.com/
VS Code 最新版 https://code.visualstudio.com/

Windows 用户建议使用 WSL 2(Ubuntu) 作为终端环境,避免路径与编码问题。


16.3.2 开发方式选择

课程采用 方式 B(本机开发 + 远程数据库) 为推荐方式,降低课程服务器负荷,确保 45 人并发开发时服务稳定:

方式 说明 推荐
B:本机开发 + 远程数据库 本机安装 Python/Node,Reflex 在本机运行,数据库连接课程远程服务器 ✅ 推荐
A:Remote SSH 连接课程服务器 直接在课程服务器上开发,所有计算负荷集中在服务器端 仅限服务器端调试

方式 B 架构说明:

学生本机                          课程服务器(156.239.252.40)
┌─────────────────────┐           ┌──────────────────────────┐
│ Reflex 前端(:3000)│           │ MySQL(:3306)           │
│ Reflex 后端(:8000)│◄─────────►│ Redis(:6379,可选)     │
│ VS Code             │  TCP/IP   │                          │
└─────────────────────┘           └──────────────────────────┘

学生 Reflex 应用运行在本机,数据库托管在课程服务器,45 人同时开发时服务器只承担 DB 负载,前端/后端计算分散到各学生机器。


16.3.3 克隆仓库

# 使用自己的 GitHub 账号 fork 课程仓库,再 clone 到本地(或服务器)
git clone https://github.com/<你的GitHub账号>/OA-EPP.git
cd OA-EPP

16.3.4 搭建 Python 虚拟环境

# 创建虚拟环境
python3 -m venv .venv

# 激活(Linux / macOS / WSL)
source .venv/bin/activate

# 激活(Windows PowerShell)
.venv\Scripts\Activate.ps1

# 安装项目依赖
pip install -r requirements.txt

16.3.5 配置环境变量

项目根目录创建 .env 文件(不要提交到 Git.gitignore 已排除):

cp .env.example .env

编辑 .env,填写课程服务器连接信息:

# .env - 本地开发环境变量(请勿提交到 Git)

# 课程共享开发数据库(连接课程服务器)
# 服务器:156.239.252.40,端口:3306
# 账号密码由教师课堂发放,以实际发放为准
DATABASE_URL=mysql+pymysql://oaepp_dev:<教师发放密码>@156.239.252.40:3306/oaepp_dev

# Redis 会话缓存(可选,初始阶段可不配置)
REDIS_URL=redis://156.239.252.40:6379/0

# 应用密钥(本机开发随机生成即可)
SECRET_KEY=dev-local-change-me-in-production

注意:每位学生使用相同 MySQL 服务器地址,通过独立数据库名(student_学号)隔离各自数据。教师在开学初执行 CREATE DATABASE student_学号; GRANT ALL ON student_学号.* TO 'oaepp_dev'@'%'; 为每位学生创建专属数据库。


16.3.6 初始化数据库

# 应用所有数据库迁移
reflex db upgrade

# 验证迁移状态
reflex db status

16.3.7 本地启动 Reflex 开发服务器

# 启动开发服务器(支持热重载)
reflex run

启动成功后终端输出:

─────────────────────────────── Starting Reflex App ────────────────────────────────
App running at: http://localhost:3000
Backend running at: http://localhost:8000

打开浏览器访问 http://localhost:3000 验证运行正常。

Remote SSH 方式:VS Code 会自动将服务器端口转发到本机,直接在本机浏览器访问即可。


16.3.8 运行测试

提交代码前必须确保测试通过:

# 运行所有测试
pytest

# 运行代码风格检查(与 CI 保持一致)
ruff check .
ruff format --check .

16.3.9 提交代码到 GitHub

遵循 16.2 的 Commit 规范,且按 Issue → 分支 → PR 的方式开发。完整工作流如下:

# Step 1:同步上游最新代码(避免冲突)
git fetch origin
git rebase origin/develop

# Step 2:基于自己的 Issue 创建功能分支(每个 Issue 一个分支)
git checkout -b feat/issue-123-学号-功能描述 origin/develop
# 示例:git checkout -b feat/issue-123-2023001-add-assignment-submit origin/develop

# Step 3:编写代码,完成后检查
ruff check .
pytest

# Step 4:暂存并提交
git add .
git commit -m "feat(作业提交): 实现文件上传与版本记录功能

- 新增 FileUpload 组件支持多格式附件
- 作业记录写入 Submission 表,支持多次提交

BREAKING CHANGE: 无
Close #Issue编号: $(git rev-parse --short HEAD)"

# Step 5:推送分支
git push origin feat/issue-123-学号-功能描述

# Step 6:在 GitHub 上创建 Pull Request
# 目标分支:develop
# 描述中注明:功能说明、测试截图、关联 Issue 编号

16.3.10 常见问题

问题 解决方案
reflex: command not found 检查虚拟环境是否激活:source .venv/bin/activate
数据库连接失败 确认 .envDATABASE_URL 正确,课程服务器防火墙已开放 3306 端口
端口 3000 被占用 reflex run --frontend-port 3001
pytest 找不到模块 确保在项目根目录执行,且虚拟环境已激活
推送被拒绝(non-fast-forward) git fetch origin && git rebase origin/develop 后重试
CI 检查未通过 本机先执行 ruff check .pytest 修复问题

遇到未解决的问题,参考 Section 16「教学常见问题与平台应对策略」,或在课程 Issue 中提交问题(附截图和错误信息)。


16.4 学生分支策略:基于 Gitflow 的 Issue 开发模型

为避免 45 人并行开发时互相覆盖,本课程采用 Gitflow(教学简化版)

16.4.1 Gitflow 是什么

Gitflow 是一种分支管理模型,核心思想是:

  • main:始终保持可发布、可演示的稳定版本
  • develop:日常开发集成分支
  • feature/*:功能分支(学生主要使用)
  • release/*:发版准备分支(教师主导)
  • hotfix/*:线上紧急修复分支(教师主导)

课程要求:学生不允许直接向 maindevelop 推送代码,只能通过 PR 合并。

16.4.2 学生必须遵守的分支规则

  1. 一个 Issue 对应一个分支,禁止多个 Issue 混在同一分支。
  2. 分支必须从 develop 创建,禁止从他人 feature 分支派生。
  3. 分支命名必须包含 Issue 编号和学号,推荐格式:
feat/issue-<编号>-<学号>-<简短描述>
fix/issue-<编号>-<学号>-<简短描述>

示例:

feat/issue-123-2023001-assignment-upload
fix/issue-245-2023001-login-token-expired
  1. PR 必须写明 Closes #Issue编号,并以 develop 为目标分支。
  2. 一个 PR 只解决一个 Issue,便于审查与评分追踪。

16.4.3 课程里的 Gitflow 合并路径

学生 feature/*  -->  PR 到 develop  -->  教师验收后合并
develop         -->  release/*      -->  main(阶段发布)
main 线上问题    -->  hotfix/*       -->  main + develop 回合并

16.4.4 学生最小操作清单

# 1) 同步 develop
git fetch origin
git checkout develop
git pull origin develop

# 2) 从 develop 创建自己的 issue 分支
git checkout -b feat/issue-123-2023001-xxx

# 3) 开发并自测
ruff check .
pytest

# 4) 提交并推送
git add .
git commit -m "feat(作业提交): 完成 issue-123 文件上传"
git push origin feat/issue-123-2023001-xxx

# 5) 发起 PR 到 develop,并在描述中写 Closes #123

教师在阶段验收通过后,再将 develop 合并到 main。这样可确保主分支始终稳定,避免多人并行开发时主线失控。

16.4.5 教师端 Gitflow 执行规范

本小节用于回答两个核心问题:

  1. 什么时候执行 develop -> release/* -> main
  2. 什么时候走 hotfix/*

A. 常规迭代发布:走 develop -> release/* -> main

适用条件(满足任一条即可进入 release 流程):

  • 到达教学里程碑节点(如第 4/8/12 周阶段验收)
  • 本阶段 P0/P1 Issue 已完成并通过验收
  • develop 连续稳定(CI 全绿,关键缺陷为 0)

执行步骤:

# 1) 从 develop 创建 release 分支
git checkout develop
git pull origin develop
git checkout -b release/2026w08

# 2) 仅允许发布准备类改动
# 例如:版本号、文档、配置微调、发布说明

# 3) 提交并推送 release 分支
git push origin release/2026w08

# 4) PR: release/* -> main(发布)
# 5) PR: release/* -> develop(回合并,避免分叉)

规则约束:

  • release/* 禁止再合入新功能(feature)
  • 发布 PR 必须附带:变更清单、测试结果、回滚方案
  • 发布完成后打 Tag(如 v2026.08)便于追踪

B. 紧急修复发布:走 hotfix/*

触发条件(需满足“线上已受影响”):

  • main 对应线上环境出现阻断性问题(无法登录、核心流程不可用)
  • 安全漏洞需立即修补(凭据泄漏、越权访问等)
  • 数据错误持续扩大,必须立即止损

执行步骤:

# 1) 从 main 创建 hotfix 分支
git checkout main
git pull origin main
git checkout -b hotfix/2026w08-login-critical

# 2) 最小修复原则:只改故障相关代码

# 3) 推送并发起 PR 到 main,快速审查并合并
git push origin hotfix/2026w08-login-critical

# 4) 修复上线后,必须回合并到 develop
# PR: hotfix/* -> develop

规则约束:

  • hotfix/* 禁止夹带需求开发内容
  • 修复后 24 小时内补齐:根因分析、回归测试、预防措施
  • 所有 hotfix 必须同步回 develop,防止下次发布“修复丢失”

C. 教师端决策速查表

场景 走哪条分支流程 目标
阶段性验收、计划内发布 develop -> release/* -> main 稳定发布 + 可追溯版本
线上重大故障、紧急安全问题 main -> hotfix/* -> main,再回合并 develop 先止血,再补齐主线
普通功能迭代 学生 feature/* -> develop 日常开发集成

口令化执行原则:新功能进 develop,计划发布走 release,线上救火走 hotfix,修完必须回 develop。

16.4.6 一页值班版流程图(Mermaid)

用法:值班教师按图从上到下判断,遇到「否」直接执行对应动作;遇到「是」进入下一节点。

flowchart TD
  A[值班开始:收到代码变更/告警] --> B{是否线上已受影响?}

  B -- 是 --> C[从 main 建 hotfix/*]
  C --> D[最小修复 + 快速回归]
  D --> E[PR: hotfix/* -> main]
  E --> F[合并并上线]
  F --> G[PR: hotfix/* -> develop 回合并]
  G --> H[24h内补齐RCA/回归测试/预防项]
  H --> Z[值班结束]

  B -- 否 --> I{是否达到阶段发布条件?\n里程碑到期 / P0-P1完成 / CI稳定}
  I -- 否 --> J[继续日常节奏:feature/* -> develop]
  J --> Z

  I -- 是 --> K[从 develop 建 release/*]
  K --> L[仅允许发布准备改动\n版本号/文档/配置微调]
  L --> M[PR: release/* -> main]
  M --> N[合并发布并打Tag]
  N --> O[PR: release/* -> develop 回合并]
  O --> P[发布记录归档:变更清单/测试结果/回滚方案]
  P --> Z

16.5 为课程相关仓库点 Star

点 Star 是本课程的学习考核要求之一,所有学生须在第 1 周内完成以下仓库的 Star 操作,教师将通过 GitHub API 核查。

须 Star 的仓库列表

以下仓库均属于卢军老师(roboticsystem 组织)名下,学生须在第 1 周内完成全部 Star:

仓库 地址 说明
工程实践4在线平台 roboticsystem/OA-EPP 本课程项目仓库(必须 Star)
工程实践1-4在线教材 roboticsystem/engineering-practice-1-4-textbook 配套教材文档站
需求文件自动生成 Issue roboticsystem/github-issue-autogen-from-srs-ai 从 SRS 自动生成 GitHub Issue 工具
GitHub 贡献统计系统 roboticsystem/github-contrib-stat 学生贡献数据可视化
实验室多服务器管理 roboticsystem/Lab-Multi-Server-Management-System-LMMS 课程服务器基础设施
研究生考试评分系统 roboticsystem/postgraduate_exam_scoring_web_system Web 考试平台(PDF/Markdown 自动出题)
TurboWarp 课堂 AI 管理 roboticsystem/TurboWarp_Class_AI_TeachStuManage 教学场景双端管理工具
Armbian 儿童教育机顶盒 roboticsystem/ArmbianKidBox ARM 开发板儿童教育平台
电力 LLM 攻防研究 roboticsystem/SCPowerLLMAttackDefense 电力 AI 系统安全研究
STM32 散热电源监控 roboticsystem/stm32-fan-power-monitor-freertos STM32 + FreeRTOS 嵌入式项目
STM32 容器模拟器 roboticsystem/STM32_Container_Simulator_Dev STM32 容器化开发环境
AI 视频自动生成 roboticsystem/TurboMoiveAutoMaker 无版权素材自动拼接视频
2025 工作论文代码 roboticsystem/LJ-work-paper-code-2025 科研代码存档
eLORAN roboticsystem/eLORAN eLORAN 定位技术研究

如何点 Star

  1. 登录 GitHub(github.com
  2. 访问上表中的仓库链接
  3. 点击页面右上角 ⭐ Star 按钮
  4. 确认 Star 按钮变为已激活状态(高亮显示)

核查方式:教师将在课程期间通过 GitHub API 随机核查学生 Star 列表,未完成 Star 的学生将被提醒,持续未完成者扣除过程分 5 分。

为何要求点 Star? Star 是开源社区对项目贡献者最直接的支持方式,也是工程师职业素养的体现。本课程鼓励学生从现在开始参与开源社区,关注工具生态的发展动态。


17. 推荐软件开发方法:Superpowers AI 驱动开发

本节介绍课程推荐的软件开发方法——基于 copilot-enhance(Superpowers)的 AI 驱动开发工作流。该方法将 GitHub Copilot 与结构化循环协作机制结合,显著提升开发效率与代码质量,是本课程鼓励学生掌握的现代开发实践。


17.1 什么是 Superpowers 开发方法

Superpowers 是由 copilot-enhance.copilot-enhance-tool 插件提供的增强型 AI 开发工作流,其核心思想是:

AI 助手不应只回答一次性问题,而应成为持续协作的开发伙伴,在整个功能开发周期中保持上下文、追踪任务、自主推进。

与传统"一问一答"的 Copilot Chat 使用方式相比,Superpowers 工作流具备以下特点:

特性 传统 Copilot Chat Superpowers 工作流
上下文保持 单次对话,会话结束即失忆 持久化记忆,跨会话保留项目规范
任务追踪 依赖人工记忆 自动 Todo 列表,逐项执行标记
执行能力 建议代码,需手动操作 直接调用 IDE 工具执行文件编辑/终端命令
错误恢复 需要重新描述问题 自动重试,策略性更换方案
协作深度 问答式,浅层协助 循环协作,深层参与功能开发全流程

17.2 核心组件

Superpowers 工作流由三个组件协同工作:

① GitHub Copilot(基础 AI 引擎)

提供代码生成、解释、重构、测试生成等基础 AI 能力。需独立订阅(学生可申请 GitHub Education 免费版)。

② copilot-enhance(循环协作控制器)

  • VS Code 插件:copilot-enhance.copilot-enhance-tool
  • 功能:在每轮 Copilot 对话结束后自动触发下一步,形成「输入→执行→报告→同步」的持续循环
  • 避免任务中断,确保多步骤工作不因上下文丢失而中途停止

③ HackLLM Memory(跨会话记忆)

  • VS Code 插件:hacklm.hacklm-memory
  • 功能:将项目规范、编码偏好、架构决策以 Markdown 文件形式存储于 .memory/ 目录
  • Copilot 在每次对话开始时自动加载相关记忆,实现「一次说明,永久生效」

17.3 推荐工作流

一个典型的 Superpowers 功能开发流程如下:

┌─────────────────────────────────────────────────────────────────┐
│                   Superpowers 功能开发循环                        │
│                                                                 │
│  1. 打开 VS Code,激活 copilot-enhance                            │
│  2. 描述任务目标(自然语言)                                        │
│  3. Copilot 读取项目记忆(.memory/)+ 分析代码库                   │
│  4. 制定 Todo 计划,逐步执行(文件编辑 / 终端命令)                 │
│  5. 自动校验(运行测试 / lint)                                    │
│  6. 生成 Commit Message,提交代码                                 │
│  7. copilot-enhance 同步节点,询问下一步                           │
└─────────────────────────────────────────────────────────────────┘

实际操作步骤(以实现「F-S-020 作业提交」功能为例):

  1. 在 VS Code Copilot Chat 中输入:帮我实现 F-S-020 作业提交功能,参考 F-S-021 版本记录需求
  2. Copilot 读取 .memory/ 中已有的 Reflex 项目规范和数据模型
  3. 自动分析现有代码结构(grep_search + semantic_search
  4. 生成 Todo 列表:创建 Model → 设计 State → 实现上传组件 → 编写测试
  5. 逐项执行,每完成一项自动标记、进入下一项
  6. 完成后运行 pytestruff check . 验证
  7. 生成符合 16.2 规范的 Commit Message,等待人工确认后提交

17.4 项目记忆管理

.memory/ 目录是 Superpowers 的"知识库",推荐在项目初期由教师或组长建立:

.memory/
├── instructions.md    # AI 助手行为准则(禁止做什么、如何输出)
├── preferences.md     # 代码风格偏好(命名规范、缩进、注释语言)
├── decisions.md       # 架构决策记录(为何选 Reflex、为何用 MySQL)
├── quirks.md          # 项目特有坑点(已知 bug、临时 workaround)
└── security.md        # 安全规则(哪些内容绝对不能出现在代码中)

写入示例(preferences.md):

# 代码偏好

- 变量命名:Python snake_case,Reflex State 方法用动词开头
- 注释语言:中文
- 数据库操作:统一通过 rx.Model 的 ORM 方法,禁止裸 SQL
- 错误处理:用 rx.State 的 toast 通知用户,不用 print
- 测试:每个 State 事件方法必须有对应 pytest 测试

一旦写入,Copilot 在所有后续对话中都会遵守这些规则,无需每次重复说明。


17.5 与课程工作流的集成

Superpowers 开发方法与本课程的 GitHub 工作流无缝集成:

课程环节 Superpowers 辅助方式
认领 Issue 告诉 Copilot Issue 编号,自动分析需求、生成实现方案
创建分支 Copilot 自动执行 git checkout -b feat/学号-功能名
编写代码 循环协作,自主推进,人工审查关键节点
提交 Commit 自动生成符合 16.2 Conventional Commits 规范的 Message
创建 PR 自动生成 PR 描述,关联 Issue 编号,填写测试说明
Code Review 根据 pr-agent 反馈,让 Copilot 自动修改并重新提交

17.6 学生使用建议

第一次使用

  1. 安装插件(copilot-enhance.copilot-enhance-tool + hacklm.hacklm-memory,见 Section 14.4)
  2. 克隆项目仓库,打开 VS Code
  3. 在仓库根目录创建 .memory/preferences.md,写入 3-5 条编码偏好
  4. 在 Copilot Chat 中发送:读取我的项目记忆,然后帮我了解这个项目的结构
  5. 从简单的功能开始(如修改一个现有页面的样式),体验循环协作流程

注意事项

  • Copilot 的建议不总是正确的,每个关键操作(文件修改、git push)前需人工审查
  • 记忆文件(.memory/)应纳入版本控制,团队成员共享项目记忆
  • 遇到 Copilot 卡住或循环出错,直接在 Chat 中说"停止,重新分析"
  • AI 辅助开发的代码仍需通过 CI 检查(pytest + ruff),CI 失败时由学生本人负责修复

学生提示:避免上下文溢出导致 LLM“降智”

在 AI 辅助编程中,模型“突然变笨”通常不是能力消失,而是上下文窗口被低价值信息占满。可用以下 7 种方法稳定效果:

  1. 任务分块(最优先)
  2. 不要一次性让 AI 处理“全仓库重构”。
  3. 改为:一个问题、一个文件(或一条调用链)一轮完成。

  4. 先给结构化摘要,再给细节

  5. 先提供:目标、约束、验收标准(3-5 条)。
  6. 需要时再补具体代码段,避免无关日志和整文件粘贴。

  7. 控制上下文“噪声”

  8. 少贴重复报错、超长终端输出、无关历史对话。
  9. 只保留“当前问题必须的信息”(最小必要上下文)。

  10. 建立并复用项目记忆

  11. 把固定规则写入 .memory/(命名规范、测试命令、架构约束)。
  12. 减少每轮重复说明,留出窗口给当前任务。

  13. 阶段性重置会话

  14. 当对话已很长且偏题时,开启新会话。
  15. 用“阶段总结”作为新起点(已完成/待完成/阻塞点)。

  16. 让 AI 先检索再修改

  17. 先定位相关文件和符号,再做改动。
  18. 避免“盲改 + 大范围猜测”占用大量上下文。

  19. 固定输出格式,降低漂移

  20. 要求 AI 按固定模板输出:
    • 变更点
    • 影响范围
    • 验证步骤
  21. 格式稳定后,后续轮次更不容易跑偏。

课堂实用口诀小步快跑、少贴废话、先总结再续聊。

学术诚信:Superpowers 工具用于提升开发效率,不是替代学习的捷径。学生需理解 Copilot 生成的每一行代码,在 Code Review 中能够解释代码逻辑,这是课程考核的重要维度。


18. 软件测试方案

本节定义《工程实践》课程管理平台的测试策略,覆盖功能测试UI 测试,并规定基于 Superpowers(Section 17)的 TDD(测试驱动开发) 工作流,确保代码质量与 CI 的无缝衔接。


18.1 测试层次与工具选型

平台测试采用三层结构,由下至上:

┌─────────────────────────────────────────────────────────────┐
│  Layer 3:UI 测试(端到端)  Playwright                       │
│  Layer 2:功能/集成测试       pytest + Reflex AppHarness      │
│  Layer 1:单元测试            pytest(纯 Python 逻辑)        │
└─────────────────────────────────────────────────────────────┘
层次 工具 职责 运行时机
单元测试 pytest 测试 State 方法、工具函数、数据模型验证 每次 git push 触发 CI
功能/集成测试 pytest + reflex.testing.AppHarness 测试页面路由、State 事件、数据库读写 每次 PR 触发 CI
UI 测试(E2E) Playwright + pytest-playwright 模拟真实用户操作,验证完整用户旅程 PR Merge 前强制通过

18.2 单元测试(pytest)

适用范围:State 事件方法、数据验证函数、工具模块。

目录结构

tests/
├── unit/
│   ├── test_auth_state.py       # F-S-001 登录/退出 State 测试
│   ├── test_submission_state.py # F-S-020/021/022 作业提交 State 测试
│   ├── test_grade_state.py      # F-S-030/031/032 成绩 State 测试
│   └── test_utils.py            # 工具函数测试
├── integration/
│   └── test_db_operations.py    # 数据库 CRUD 集成测试
└── e2e/
    └── test_student_flow.py     # Playwright UI 流程测试

典型单元测试示例(State 方法)

# tests/unit/test_submission_state.py
import pytest
from unittest.mock import AsyncMock, patch
from oaepp.states.submission import SubmissionState

@pytest.mark.asyncio
async def test_submit_assignment_success():
    """F-S-020:作业提交成功路径"""
    state = SubmissionState()
    state.file_content = b"print('hello')"
    state.assignment_id = 1

    with patch.object(SubmissionState, '_save_to_db', new_callable=AsyncMock) as mock_save:
        mock_save.return_value = {"id": 42, "version": 1}
        await state.submit_assignment()

    assert state.submission_success is True
    assert state.latest_submission_id == 42

@pytest.mark.asyncio
async def test_submit_after_deadline_rejected():
    """F-S-022:截止日期后提交被拒绝"""
    from datetime import datetime, timedelta
    state = SubmissionState()
    state.assignment_deadline = datetime.now() - timedelta(hours=1)
    state.file_content = b"late submission"

    await state.submit_assignment()

    assert state.submission_success is False
    assert "截止时间" in state.error_message

18.3 功能/集成测试(Reflex AppHarness)

reflex.testing.AppHarness 可以启动完整的 Reflex 应用实例(无 UI),对路由、State 变更、数据库写入进行集成验证。

使用示例

# tests/integration/test_db_operations.py
import pytest
from reflex.testing import AppHarness
from oaepp import app

@pytest.fixture(scope="session")
def harness():
    """启动 Reflex AppHarness,使用测试数据库"""
    with AppHarness.create(root=".", app_source=app) as h:
        yield h

def test_create_submission_persisted(harness):
    """提交作业后,数据库中应存在对应记录"""
    with harness.state_manager.get_state(token="test-student") as state:
        state.file_content = b"test code"
        state.assignment_id = 1

    import asyncio
    asyncio.run(harness.get_state(token="test-student").submit_assignment())

    # 验证数据库中的记录
    with harness.state_manager.get_state(token="test-student") as state:
        assert state.submission_count >= 1

18.4 UI 测试(Playwright)

Playwright 是 Reflex 官方推荐的 E2E 测试工具,支持 Python API,可模拟真实浏览器操作。

安装

pip install pytest-playwright
playwright install chromium   # 安装 Chromium 浏览器驱动

典型 UI 测试(学生作业提交流程)

# tests/e2e/test_student_flow.py
import pytest
from playwright.sync_api import Page, expect

BASE_URL = "http://localhost:3000"

@pytest.fixture(autouse=True)
def login_as_student(page: Page):
    """每个 E2E 测试前先登录"""
    page.goto(f"{BASE_URL}/login")
    page.fill('[name="username"]', "student_001")
    page.fill('[name="password"]', "testpass")
    page.click('button[type="submit"]')
    expect(page).to_have_url(f"{BASE_URL}/dashboard")

def test_submit_assignment_ui(page: Page):
    """F-S-020:完整作业提交 UI 流程"""
    page.goto(f"{BASE_URL}/assignments/1")

    # 上传文件
    page.set_input_files('input[type="file"]', "tests/fixtures/sample_code.py")
    page.click('button:has-text("提交")')

    # 验证成功提示
    expect(page.locator(".toast-success")).to_contain_text("提交成功")
    expect(page.locator(".submission-version")).to_contain_text("版本 1")

def test_grade_display_after_review(page: Page):
    """F-S-030:成绩评定后实时显示在仪表板"""
    page.goto(f"{BASE_URL}/grades")
    expect(page.locator(".grade-card")).to_be_visible()
    expect(page.locator(".grade-score")).not_to_be_empty()

18.5 基于 Superpowers 的 TDD 工作流

TDD(Test-Driven Development,测试驱动开发)的核心循环是:红(失败测试)→ 绿(最小实现)→ 重构。结合 Superpowers,可将此流程全面提速:

TDD + Superpowers 标准循环

1. 描述需求给 Copilot
   └→ "帮我为 F-S-020 作业提交功能写 pytest 测试,包含成功路径、截止规则和版本记录"

2. Copilot 生成测试代码(RED 阶段)
   └→ 运行 pytest → 所有测试预期失败(功能尚未实现)

3. 告诉 Copilot 测试已写好
   └→ "现在根据这些测试,实现 SubmissionState 的 submit_assignment 方法"

4. Copilot 生成最小实现(GREEN 阶段)
   └→ 运行 pytest → 测试通过

5. 请求重构
   └→ "代码可以工作了,帮我重构 submit_assignment,消除重复,保持测试通过"

6. 验证后提交
   └→ Copilot 生成 Commit Message,git commit + push

关键优势

  • 测试先行,需求边界在编码前已明确
  • Copilot 生成实现时会参照测试,减少遗漏
  • 重构有测试保护,不用担心引入新 bug
  • 记忆文件中存储测试规范,后续功能自动复用同一风格

18.6 CI 测试集成(GitHub Actions)

所有测试在 CI 中自动执行,PR Merge 前须全部通过:

# .github/workflows/test.yml
name: 测试套件

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: testpass
          MYSQL_DATABASE: oaepp_test
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: 安装依赖
        run: pip install -r requirements.txt && pip install pytest-playwright && playwright install chromium

      - name: 运行单元测试
        run: pytest tests/unit/ -v
        env:
          DATABASE_URL: mysql+pymysql://root:testpass@localhost:3306/oaepp_test

      - name: 运行集成测试
        run: pytest tests/integration/ -v
        env:
          DATABASE_URL: mysql+pymysql://root:testpass@localhost:3306/oaepp_test

      - name: 启动应用并运行 E2E 测试
        run: |
          reflex run --env dev &
          sleep 15
          pytest tests/e2e/ -v --base-url http://localhost:3000
        env:
          DATABASE_URL: mysql+pymysql://root:testpass@localhost:3306/oaepp_test

      - name: 代码风格检查
        run: ruff check . && ruff format --check .

18.7 测试覆盖率要求

层次 最低覆盖率要求 测量工具
单元测试 80% 行覆盖率 pytest-cov
集成测试 核心 State 方法 100% pytest-cov
UI 测试(E2E) 覆盖所有 F-S-xxx 功能的主流程 手动审查
# 生成覆盖率报告
pytest tests/unit/ tests/integration/ --cov=oaepp --cov-report=html
# 报告生成于 htmlcov/index.html

课程评分依据:测试覆盖率报告将作为作业质量评分的重要指标。覆盖率低于 60% 或 E2E 测试存在失败项,扣除对应功能分项的 30%。


19. 工程实践文档要求

本节规定学生在整个课程周期内须提交的全部文档,统一使用 Markdown 格式书写,每份文档独立一个文件,以章节划分内容。文档提交至团队 GitHub 仓库 docs/reports/ 目录,提交时需通过 CI 检查(拼写 / 链接 / 标题层级)。

所有文档均须用中文撰写,代码、命令、函数名等技术标识符保留英文原词。


19.1 开题报告

文件名01_开题报告.md

内容要点

  1. 课题背景:说明选题来源,现有同类系统的痛点(不少于 3 条),引用不少于 2 篇相关文献或开源项目
  2. 课题目标:用 SMART 原则描述项目要达到的目标,含可量化指标
  3. 技术路线:说明将采用的核心技术栈(Reflex / MySQL / Docker / GitHub Actions),并给出选择理由
  4. 系统功能简述:列出拟实现的核心功能(5–10 项),对应后续需求分析
  5. 团队分工初稿:列出各成员的职责方向(可在详细设计阶段调整)
  6. 里程碑计划:以甘特图或表格形式给出各阶段时间节点
  7. 风险与应对:列出 3–5 项技术/协作风险及预案

19.2 需求分析

文件名02_需求分析.md

内容要点

  1. 用户角色定义:说明所有参与者(学生 / 教师 / 管理员)的核心使用目标
  2. 用例图(PlantUML / Mermaid):覆盖系统全部 Use Case
  3. 功能需求列表:按 F-R-XXX 编号,每条需求含描述 / 优先级(P0/P1/P2)/ 验收标准
  4. 非功能需求:响应时间 / 并发用户数 / 可用性目标 / 安全合规
  5. 限制与约束:技术约束(Reflex 框架限制)/ 时间约束 / 数据约束
  6. 数据字典:列出核心实体及其关键属性的中英文对照
  7. 需求变更记录:记录评审过程中的修改(版本号 / 日期 / 变更内容)

19.3 概要设计——数据库设计

文件名03_概要设计_数据库.md

内容要点

  1. E-R 图(PlantUML erDiagram 语法):展示所有实体及关系,标注基数
  2. 表结构说明:每张表的字段名 / 类型 / 约束 / 注释,以 Markdown 表格呈现
  3. 索引策略:说明为哪些字段建索引及理由(查询频率 / 联合索引)
  4. 关联关系:外键定义,级联行为(ON DELETE / ON UPDATE)
  5. 分库/分表预案(可选):若数据量大,说明未来扩展策略
  6. 数据初始化脚本db/init.sql 的说明与关键语句摘录
  7. 迁移管理:Alembic 版本文件命名规范,reflex db migrate 使用说明

19.4 概要设计——功能设计

文件名04_概要设计_功能.md

内容要点

  1. 系统架构图(PlantUML component 图):展示前端 / 后端 / 数据库 / 外部服务的层次关系
  2. 模块划分:按功能域(认证 / 作业 / 成绩 / 通知等)划分模块,每模块列出包含的文件
  3. 接口设计概览:State 事件方法列表(方法名 / 触发条件 / 输入输出 / 对应需求编号)
  4. 页面路由规划:URL 路径 / 对应页面组件 / 访问权限
  5. 技术选型说明:Reflex State 模式 / 前端渲染方式 / 缓存策略
  6. 团队最终分工:明确到每人负责的模块与接口,作为后续编码的依据

19.5 原型设计

为什么必须做快速原型设计?

快速原型设计是保证全班同学软件界面风格、交互体验一致的关键步骤。只有先统一原型,后续用 Reflex 实现时才能做到“效果最稳、改得最少”,避免每人风格各异、返工频繁。原型图一经确定,开发阶段直接 1:1 对照实现,极大提升效率。

推荐原型工具与 Reflex 映射

  • Lovable:最佳选择,免费,组件丰富,设计稿与 Reflex 组件高度一致。原型定稿后,几乎可以 1:1 还原到 Reflex,改动极少。
  • Stitch:免费、稳定、简洁,视觉风格统一。原型到 Reflex 需多一步组件映射,但整体一致性好,适合大班协作。
  • v0:体验最佳,但为付费工具。适合有预算或追求极致效率的小组。

建议优先选 Lovable(免费+1:1 映射),如需极简风格可选 Stitch。Figma、Excalidraw 适合草图阶段,正式原型建议用 Lovable 或 Stitch。

原型设计不是走过场,而是后续开发“最省心”的保障。务必全班统一原型,开发时严格对照,确保交付物风格一致、验收顺利。

文件名05_原型设计.md

内容要点

  1. 设计工具说明:使用的原型工具(Figma / Excalidraw / Balsamiq 等),提供可访问链接
  2. 核心页面线框图:每个主要页面的低保真线框图(截图嵌入 Markdown 或用 ASCII 图示意)
  3. 交互流程说明:关键用户流程(登录 → 作业列表 → 提交 → 查看成绩)的点击路径描述
  4. 设计规范:主色调 / 字体 / 按钮样式 / 间距规则(至少 4 条)
  5. 可用性自评:参照 Nielsen 10 原则,说明原型中已考虑的 3 条可用性原则
  6. 用户反馈记录(可选):若进行了原型评审,记录评审意见及修改项

19.6 详细设计

文件名06_详细设计.md

内容要点

  1. 类图(PlantUML class 图):所有 rx.Model 子类和核心 State 类,含属性与方法签名
  2. 时序图(PlantUML sequenceDiagram):每个 P0 功能的详细交互序列(前端 → State → DB → 前端)
  3. 算法说明:涉及复杂逻辑的算法(版本控制策略 / 成绩计算公式 / 权限判断逻辑)用伪代码或流程图说明
  4. 错误处理策略:列出主要异常类型及前端展示方式(toast / 错误页)
  5. 安全设计:认证机制说明 / CSRF 防护 / SQL 注入防护(Reflex ORM 参数化查询)
  6. 性能设计:N+1 查询规避方案 / 分页策略 / Redis 缓存使用场景
  7. 接口变更记录:从概要设计到详细设计的接口变更,含原因

19.7 项目编码

文件名07_项目编码.md

内容要点

  1. 目录结构说明:项目根目录结构图(tree -L 2 输出),说明各目录职责
  2. 编码规范:遵循 PEP 8,使用 ruff 检查,列出项目定制的规则(pyproject.toml 中的 ruff 配置摘录)
  3. 关键模块说明:每个核心模块(State / Model / 页面组件)的职责、对外接口、依赖关系
  4. 第三方依赖清单requirements.txt 中各包的用途说明
  5. 环境配置说明.env 文件各变量含义,本地 vs 生产的差异
  6. Git 工作流:分支命名规范 / Commit Message 格式(Conventional Commits)/ PR 模板
  7. 开发日志(可选):记录编码过程中遇到的重要技术决策和解决方案

19.8 功能测试

文件名08_功能测试.md

内容要点

  1. 测试计划:测试范围 / 测试环境说明(使用 oaepp_test 数据库)/ 测试工具版本
  2. 测试用例表:每条需求对应的测试用例,含:用例编号 / 所测需求 / 前置条件 / 步骤 / 预期结果 / 实际结果 / 状态(通过/失败)
  3. 单元测试报告pytest tests/unit/ -v 的输出摘要,覆盖率截图或 pytest-cov 报告摘录
  4. 集成测试报告:AppHarness 集成测试结果,重点验证数据库写入正确性
  5. UI 测试报告:Playwright E2E 测试截图(关键步骤)+ 测试结果汇总表
  6. 缺陷跟踪:发现的 Bug 列表(GitHub Issue 链接 / 严重程度 / 修复状态)
  7. 测试总结:覆盖率是否达标 / 未覆盖的风险点 / 遗留问题说明

19.9 项目集成及答辩 PPT

文件名09_答辩PPT.md(使用 Marp 格式,可直接渲染为幻灯片)

Marp Front Matter

---
marp: true
theme: default
paginate: true
---

幻灯片结构要点(建议 15–20 张):

幻灯片 内容
封面 项目名称 / 团队成员 / 日期
课题背景 痛点描述(1–2 张,含数据或图表)
需求概要 核心功能列表(F-S-xxx 编号)
系统架构 架构图(可用 Mermaid 或截图)
数据库设计 核心表关系图
核心功能演示 截图或 GIF,逐功能说明(3–5 张)
技术亮点 选取 2–3 个技术难点说明解决方案
测试结果 覆盖率图表 / CI 截图
团队分工 各成员贡献说明(结合 GitHub 统计)
项目总结 达成目标 / 未完成项 / 后续计划
Q&A 留白页

提示:答辩 PPT 须在答辩前 24 小时推送至仓库,由教师从仓库取用,不接受现场 U 盘临时替换。


19.10 课程总结报告

文件名10_课程总结报告.md

内容要点

  1. 项目回顾:对照开题报告的目标,逐条说明完成情况(达成 / 部分达成 / 未达成),分析偏差原因
  2. 技术收获:列出掌握的 3–5 项核心技术,每项用 100–200 字说明学到了什么
  3. 协作总结:团队协作过程中的经验(工具使用 / 冲突解决 / 代码评审),含量化数据(PR 数 / Commit 数 / Issue 数)
  4. AI 辅助工具使用总结:使用 Superpowers / GitHub Copilot 的具体场景,效率提升的主观评价,以及对 AI 生成代码的审查经验
  5. 不足与改进:指出项目在设计、代码质量、测试覆盖率等方面的不足,给出具体改进建议
  6. 个人贡献说明:每位成员独立撰写 300–500 字,说明本人的主要贡献与个人学习收获
  7. 参考资料:按 GB/T 7714 格式列出所有引用的文献、开源项目、文档资源

19.11 文档提交规范汇总

文件名 格式 提交截止时间 存放路径
01_开题报告.md Markdown 第 2 周末 docs/reports/
02_需求分析.md Markdown + PlantUML 第 4 周末 docs/reports/
03_概要设计_数据库.md Markdown + E-R 图 第 6 周末 docs/reports/
04_概要设计_功能.md Markdown + 架构图 第 6 周末 docs/reports/
05_原型设计.md Markdown + 线框图 第 7 周末 docs/reports/
06_详细设计.md Markdown + UML 第 9 周末 docs/reports/
07_项目编码.md Markdown 第 14 周末 docs/reports/
08_功能测试.md Markdown + 截图 第 15 周末 docs/reports/
09_答辩PPT.md Marp Markdown 答辩前 24h docs/reports/
10_课程总结报告.md Markdown 最终答辩后 1 周 docs/reports/

评分权重:10 份文档合计占课程总分的 40%;项目代码实现占 40%;答辩表现占 20%。每份文档的具体评分细则由教师在对应周次课上公布。