软件工程第二次团队作业

2026秋软工实践 天生一队 ——项目原型设计+概要设计

项目 内容
作业所属课程 2026-01 软件工程与软件工程实践(H202601)· 福州大学 计算机与大数据学院
https://edu.cnblogs.com/campus/fzu/2026-01SoftwareEngineeringandSoftwareEngineeringPractice
作业要求 第二次团队作业(原型设计+概要设计)
https://edu.cnblogs.com/campus/fzu/2026-01SoftwareEngineeringandSoftwareEngineeringPractice/homework/16774
作业的目标 在第一次团队作业的需求分析之上,把「要做什么」翻译成「做成什么样、怎么搭起来」:用原型工具把 20 个功能页面画出来并写清交互规则,用 UML 与 ER 图把系统的模块划分、对象协作和数据关系固定下来,最终产出《系统设计说明书》与《数据库设计说明书》两份可评审的文档,并把团队协作的过程留痕在仓库与博客里
团队名称 天生一队
团队成员学号-名字 蔡靖熹 102402116、吴政迅 102402138、庄剑钇 102402152、沈文驰 032401135、梁倩 102401607、王可人 102402104、陈瑀涵 102402107、吴雅晴 102402103、张佩琪 102402102、章玲 102402101、徐张睿 102402139、陈佳慧 052405110

一、这次我们交了什么

我们的项目是「大学生课程课件笔记整理与知识梳理 Agent」——学生上传课程课件(PDF / PPT / DOCX / Markdown 等 9 类共 16 种扩展名),系统自动产出一份能点回原文的结构化笔记,并在材料范围内回答提问。一句话概括产品的底线:每条结论都必须指得回课件的具体页码和原文,查不到依据的内容一律丢弃。

上一轮作业我们把需求讲清楚了。这一轮不再谈「要不要做」,而是把它做成三件可以评审的东西:

交付物 内容 形式 位置
原型设计说明书 20 个功能页面(学生端 U01–U15、管理端 A01–A05)、交互规则、业务规则、统一状态映射 Figma 原型 + 说明书 仓库根目录 / 在线原型链接
系统设计说明书 分层架构、模块职责、22 个接口、笔记生成六段管线、状态机、非功能指标、部署运维 Word / PDF 仓库根目录
数据库设计说明书 概念模型(2 张 ER 图)、关系模型(13 张表)、数据字典、ORM 映射、事务设计、14 条验收用例 Word / PDF + schema.sql 数据库设计/

后面还有 14 张概要设计图(UML 7 张 + 架构 3 张 + 数据 4 张),按统一图例绘制,附在正文里逐张解释。

我们给自己定的标准是:文档里每写一句话,都要能回答「在代码或数据库的哪个位置落地」。做不到的,就明确写进「已知限制」。


二、原型设计(40%)

2.1 工具、规格与页面地图

原型用 Figma 完成。作业要求里写的是「如 Axure 或墨刀」,举的是例子;我们选 Figma 的实际理由有三条:

  1. 评审成本最低。链接发出去,组员和助教在浏览器里就能翻页、评论,不用装客户端,也不用为了看一眼原型去注册账号。
  2. 一套文件同时产出原型和切图。页面本身就是 1440×1080 的矢量稿,后面做前端时可以直接量间距、取色值,不用二次返工。
  3. 组件复用。20 个页面里有大量重复元素(顶栏、侧边导航、出处角标、状态标签),做成组件后改一处全站同步——这也是后面页面之间能保持一致的原因。

规格:1440×1080,蓝色科技主题,学生端 15 页 + 管理端 5 页,另附 2 页设计说明(配色、字号、组件库)。

页面地图如下。编号不是随手编的,U / A 前缀区分学生端与管理端,后面所有文档、测试用例、缺陷单都用这个编号互相引用:

编号 页面 核心功能
U01 登录 学生 / 管理员双身份切换登录,失败提示不泄露账号是否存在
U02 注册 仅学生开放;密码 8–20 位含字母数字;学号邮箱查重
U03 上传课件与手写公式 选课程 → 加材料 → 写整理目标,三步引导
U04 Agent 任务进度 五阶段执行进度,支持停止与从失败阶段重试
U05 结构化笔记 章节目录 + 笔记正文 + 本章考点清单,正文带上标出处
U06 文本思维导图 知识树展开收起,节点联动右侧原文预览
U07 例题与公式校对 例题分步解析;手写公式 LaTeX 人工确认
U08 AI 课程问答 有据问答,无证据固定拒答
U09 历史整理记录 任务列表、搜索筛选、版本管理、失败重试
U10 笔记增量更新 补充材料,新增 / 修改 / 冲突逐条确认
U11 考试范围复习 按章节与已发布标签生成复习清单
U12 笔记导出 Markdown / LaTeX 两种格式预览与导出
U13 错题本与薄弱点分析 错题按知识点聚合薄弱度,支持重做与定位原文
U14 智能自测 按考点自动组卷,交卷自动批改
U15 图形化思维导图 可缩放知识图谱,节点摘要与出处
A01 课件资料入库维护 公共资料新增 / 替换 / 停用,解析与页码核验
A02 例题库维护 题干、答案、教学解析、知识点关联
A03 测试案例管理 版本化测试集,覆盖有据问答、公式识别、拒答
A04 Agent 效果对比测试 同测试集下两套配置逐例对比与失败定位
A05 考试知识点标签维护 章节标签树、属性配置、关联资料与去重校验

把 20 个页面摊开看,会发现它们其实是四条链,其余页面都是链上的节点:

  • 整理链:U03 → U04 → U05 → U07(材料进,笔记出)
  • 问答链:U08 → U06(问与答,答完能跳原文)
  • 巩固链:U14 → U13 → U11(考、错、复习)
  • 治理链:A01 → A02 → A03 → A04 → A05(资料、题库、测试集、对比、标签)

2.2 三条主流程在原型里长什么样

第一条:从上传到拿到笔记。 U03 是三步引导页,右侧有个常驻面板叫「本次将生成」,把结构化章节笔记、考点清单、文本知识导图、例题解析、LaTeX 与原文出处五样东西提前摆出来——用户在上传前就知道会得到什么,这比上传完再解释有用得多。点「开始解析」后跳到 U04,页面把 Agent 的执行拆成五个公开阶段(资料解析 / 公式识别 / 知识点抽取 / 结构化笔记 / 出处核验),每阶段显示处理摘要,例如「课件正文与 32 页页码索引已建立」「识别 1 条公式,发现 1 处低置信字符」。

这里有个刻意的设计:只展示阶段,不展示模型在想什么。 思维链不对外,用户看到的必须是可核验的产物和可操作的状态。

第二条:问一个问题,答不出就说答不出。 U08 是聊天界面,但和普通聊天框有三个区别:顶部常驻「当前关联笔记版本 + 检索范围」,回答后面挂「已核验 N 处证据」和引用出处,右侧面板给出建议追问。页面里专门放了一个拒答示例——学生问「这门课的期末考试原题是什么」,回答是「该内容在所选材料中未涉及,无法回答」。把拒答做成正面案例展示出来,是我们对这个产品最想表达的态度。

第三条:考完试,错题自己进错题本。 U14 出题时每道题就标注出处页码;交卷后展示正确率、得分、逐题对错与解析;错题一键进 U13。U13 上半部是按知识点聚合的薄弱度卡片(如「等价类划分 5 道错题·薄弱度高」),下半部是错题表格,重做连续答对 2 次自动标记「已掌握」。

2.3 智能体在界面上的三个「露脸」位置

作业要求「结合项目中的领域 Agent,展示用户与智能体的交互界面」。我们没有做一个拟人化的机器人头像,Agent 出现在三个具体的、可操作的位置:

位置 用户看到什么 为什么放在这
U04 任务进度 五个执行阶段、每阶段处理摘要、低置信项待确认卡片 异步任务最怕「不知道在干什么」,把阶段公开就是最好的解释
U07 公式校对 LaTeX 源码 + 排版预览 + 低置信字符标红(如分母「100」置信度 72%)+ 确认勾选 识别不确定的地方必须由人拍板,Agent 不许自己决定
U08 问答 「已核验 N 处证据」+ 引用出处列表 + 固定拒答文案 让「有据」这件事在界面上可见,而不是写在宣传语里

三处合起来是一条原则:Agent 的产出必须可核对,不可核对的部分交回给人。

2.4 原型里的规则比界面上更多

Figma 是静态稿,交互逻辑只能靠文字写。我们在每个页面底部固定一段「操作与规则」,格式统一为「什么操作 → 系统做什么 → 失败怎么办」。举三个真实例子:

  • U03:点击开始解析 → 校验课程权限、文件格式、重复附件 → 通过则建任务编号 T-20261103-014 并跳 U04;不通过的文件留在清单里并说明原因,不静默丢弃。单文件 ≤50MB、单任务 ≤10 个文件。
  • U04:离开页面不停止任务,可在 U09 继续查看;停止后保留已完成阶段但不发布半成品;阶段失败显示原因与重试入口,重试沿用任务身份从失败阶段恢复。
  • U10:增量更新时差异分「新增 / 修改 / 冲突」三类,冲突必须逐项勾选处理方式并勾「我已核对两份材料并确认此选择」,全部确认后才生成 v2,不静默覆盖 v1。

这套写法还有一个副作用:它天然成了后端接口的验收清单。后面写《系统设计说明书》的接口章节时,大部分边界条件是从这些「规则」段直接搬过去的。

2.5 在线原型

原型在线链接(Figma 交互原型 agent2.0):

https://www.figma.com/proto/iDdth59yZaOjSaWgHvajkQ/agent2.0

建议用浏览器无痕窗口打开一次做自查:如果未登录状态下能看到页面并能左右翻页,说明分享权限是「知道链接的任何人可查看」,助教也就能打开。若被要求登录,需要在 Figma 里把 Share 改成 Anyone with the link → Can view。

已实现的线上系统(可交互,非原型):

http://47.114.55.115:8000

2.6 原型截图

下面 5 张取自同一份设计稿,挑的是最能体现「Agent 怎么和用户配合」的几个关键页面。完整 20 个页面请点上面的 Figma 链接直接翻页查看。

图 1 U03 上传课件与手写公式

u03

三步式引导:01 选择课程 → 02 添加学习材料 → 03 整理目标,一次只让用户做一个决定。右侧「本次将生成」面板在点按钮之前就把产出物列清楚(结构化章节笔记、重点考点清单、文本知识导图、例题答案与教学解析、LaTeX 公式与原文出处),让用户对"要花几分钟换什么"有预期,而不是点完干等。附件清单给出大小、状态、移除三个字段,并明确标注"以上均为演示材料,并非真实上传文件"。

图 2 U04 Agent 任务进度

u04

这一页是整个原型的核心。左侧「公开执行步骤」把任务拆成五个可核验的阶段:资料解析(已建立 32 页页码索引)、公式识别(识别 1 条公式,发现 1 处低置信字符)、知识点抽取(提取 12 个知识点,关联 4 道典型例题)、结构化笔记(进行中,正在组织 3.1–3.4 章节)、出处核验(待处理)。进度条右侧同时给出 68% 和"已完成 3/5 个阶段",并注明"进度为阶段估计,不代表剩余时间"——不给用户假精确的倒计时。

右侧那张黄底的「待你确认」卡片是设计上的关键取舍:分母里的"100"识别置信度偏低,系统不猜,直接把疑点抛给人,并给出跳转 U07 复核的入口,同时声明"未确认公式不进入最终笔记"。下方「失败恢复示例」说明出处核验超时后如何保留已完成阶段、如何从失败阶段重试而不重复上传。页面底部还写了一条边界:只展示公开执行状态,不展示模型内部思维链。

图 3 U05 结构化笔记

u05

左栏章节目录、中栏笔记正文、顶部四个视图切换(结构化笔记 / 考点清单 / 文本思维导图 / 例题与公式)。页眉固定显示 笔记 N-014 · v1 和 来源任务 T-20261103-014,把"这份笔记是哪次任务、哪个版本产出的"变成常驻信息。正文里每个结论后面跟着上标式出处编号,点击可回到材料原页——这是"有据可查"落到界面上的具体形态,而不是一句口号。

图 4 U08 AI 课程问答

u08

右上角绿色徽标 有据回答,下方小字 已检索 2 处证据。回答把"等价类划分"和"边界值分析"分开讲,每段末尾带 [1] [2],底部出处卡片写明来源是 第3章_软件测试.pdf · 第12、14页 并提供「定位原文」。

同一个页面里还演示了反面情况:紧接着问"这门课的期末考试原题是什么?",系统返回黄条 "该内容在所选材料中未涉及,无法回答。" —— 这就是作业里说的"无证据时固定拒答",宁可少答也不编。右侧「当前检索范围」把课程、章节、材料、材料版本(M-003 v1)、笔记版本(N-014 v1)全部摊开,用户随时知道 AI 是"在哪个范围里"回答的。

图 5 A04 Agent 效果对比测试(管理端)

a04

教研端用来判断"改一版提示词到底有没有变好"。同一测试集 TS-03 v1(20 例)下跑两个配置:配置甲·基线(检索 3 段 · 基础提示 v1) 与 配置乙·候选(检索 5 段 · 出处核验 v2),三项指标并排:

指标 甲 乙
准确性 · 案例通过率 80%(16/20) 90%(18/20)
出处正确率 80%(12/15) 93.3%(14/15)
范围外拒答通过率 80%(4/5) 100%(5/5)

只看达标率没法改进,所以下面还有逐例对比表:标出 5 条差异案例,每条都能点「定位」。右侧「失败定位」把 K-007 拆成预期、甲输出、乙输出、建议四段——原来甲引用了第 12 页的等价类段落来回答边界值问题,乙改引第 14 页就对了,于是建议"检查命中与出处核验,不只核对答案"。测试集、材料版本、两个配置在执行前锁定,结果另存为新批次,不覆盖旧记录。


三、概要设计(40%)

概要设计要回答的是「这个系统由哪些部分组成、它们怎么协作」。我们按自顶向下的顺序展开:分层 → 模块 → 对象 → 数据,越往下越具体。

3.1 自顶向下:分层与模块

fig01-layer

图 6-1 系统分层架构图

六层结构,规定「只允许上层调用下层」的依赖方向:

层 组成 职责
用户层 学生、管理员 提出整理、问答、复习与评测需求
接入层 Web 前端(Vue 3)、REST API 网关 页面交互;鉴权、限流、参数校验
应用层 课件、笔记、知识、问答、复习、自测、导图七个模块 业务规则与事务边界,不直接跨模块查库
Agent 编排层 意图识别与任务规划、检索增强生成、引用校验 把自然语言任务拆成可执行步骤并组织模型调用
工具层 文件解析、OCR、向量检索、公式转 LaTeX 为 Agent 提供受控的原子能力
数据层 MySQL 8 业务库、向量库、对象存储、版本历史 业务数据、原文块向量、课件原文件与笔记快照

分层有两条硬约束,写在这里是为了防止后面被悄悄绕过:

  1. Agent 层不越过应用层直接写业务表,所有落库动作都经由应用层的服务接口。这条约束让「引用校验」有地方执行——Agent 交出结果,应用层验证完才写库。
  2. 向量只存于向量库,MySQL 的 courseware_chunk 只保留 embedding_ref 引用,避免两套存储各存一份原文,日后对不上。

《系统设计说明书》里按五层描述(前端交互 / 后端服务 / Agent 智能 / 工具调用 / 数据)。两张不是矛盾,是切法不同:五层是开发视角(省掉用户层),六层是设计视角(把使用者也画进来)。同一套依赖关系,表述服务不同读者。

fig02-modules

图 6-2 后端功能模块图

七个业务模块,每个模块对应一组数据表,模块之间只通过服务接口调用:

模块 主要职责 主要数据表
课件管理 上传、解析、切块、解析状态管理 courseware、courseware_chunk
知识整理 笔记生成、章节树、知识点抽取与出处绑定 note、note_section、knowledge_point
智能问答 检索增强生成、引用回查、问答留痕 qa_record、courseware_chunk
记忆卡片 卡片生成、SM-2 调度、复习流水 flashcard、review_log
错题与自测 组卷、作答、批改、错题回填 self_test、self_test_item、wrong_question
思维导图 节点生成、树形浏览、出处跳转 mindmap_node
后台管理 用户、公共资料、测试集、效果对比 sys_user、qa_record 及评测数据

模块的划分依据是「数据的所有者」而不是页面位置。举个具体的例子:「查看原文出处」这个动作在笔记、错题、导图三个页面都会出现,但它读写的是 courseware_chunk,所以归属课件管理模块,由其他模块调用,不重复实现。按页面切模块,同一个查询会被写三遍,然后三遍各自演化出不同的边界。

3.2 UML:五张图讲五件事

3.2.1 用例图——系统能做什么

fig03-usecase

图 6-3 用例图

两类参与者(学生、管理员),用例按业务域分五组:材料整理、知识查看与问答、自测与错题、历史与导出、管理功能。

图里有两处关系必须用文字补充,否则会被误读:

  • 提交自测作答 包含(<<include>>)评定自测作答:交卷必然触发批改,不是可选步骤。
  • 自动收录错题 扩展(<<extend>>)提交自测作答,条件是「存在错误作答」:全对就不产生错题。

另外,「回查原文出处」是跨组用例,它同时服务笔记、问答、错题、导图四个场景——这也是为什么它在模块划分里归到课件管理。

3.2.2 活动图——流程怎么走

fig04-activity-note

图 6-4 活动图:课件整理主流程

三条泳道(学生 / 系统 / Agent)表达一次完整的课件整理。学生登录、选课件、提交整理目标后,系统依次做身份与格式校验、材料解析与原文定位、创建整理任务;Agent 识别章节结构并抽取知识点、检索授权材料、生成笔记与考点清单及文本导图、核验出处并标记待确认内容;控制权回到系统后判断生成是否成功、是否需要人工确认,学生核对确认后保存版本并更新任务状态。

两个判定节点对应两条失败路径,且都不是「报错重来」:校验不通过回到学生并提示错误;解析失败记录失败原因并提供重试入口。这与 courseware.parse_status 的取值(0 待解析 / 1 解析中 / 2 成功 / 3 失败)一一对应——图上的每个判定,在数据库里都有字段接着。

fig05-activity-test

图 6-5 活动图:智能自测与错题收录

自测从组卷到错题回填的闭环。学生选题量题型 → 系统检查权限与范围 → Agent 依据材料生成题目、答案、解析与证据 → 系统校验有效性后保存并隐藏参考答案 → 学生作答提交 → 系统校验归属与提交状态,未处理过才接收并标记处理中 → Agent 评分 → 系统保存逐题结果与总分,有错题时由 Agent 收录并更新知识点错误次数。

图里两个判定直接对应后面的数据库设计:「已处理本次提交?」是 self_test.status = 2 的幂等控制;「收录错题,避免重复统计」是 wrong_question 的唯一键 (user_id, source_type, source_id) 加 self_test_item.is_backfilled。

3.2.3 类图——系统由哪些概念构成

fig06-class

图 6-6 类图(业务概念类图)

领域概念层的类图,十个核心概念:用户、材料、出处、整理任务、笔记、知识点、自测、试题、错题记录、作答记录。主要关系:

  • 用户 1 : 0..* 材料(拥有);材料 1 : 0..* 出处(定位)
  • 用户 1 : 0..* 整理任务(发起);整理任务 1 : 0..* 笔记(创建)
  • 笔记 1 : 0..* 知识点(包含);出处 0..* : 0..* 知识点(引用)
  • 知识点 0..* : 0..* 试题(考查);自测 1 : 0..* 试题(包含)
  • 试题 1 : 0..* 作答记录(回答);作答记录 0..* : 0..1 错题记录(来源)

这张图的作用是把「知识点是枢纽」这件事在概念层说清楚:错题、试题、笔记、出处都绕知识点建立关联。它和数据库里 knowledge_point 的高入度是完全对应的——如果类图画出来知识点是边缘节点,那数据表一定有地方设计错了。

3.2.4 时序图——重要模块怎么交互

fig07-seq-note

图 6-7 时序图:笔记生成

参与者为:学生、前端交互层、后端服务层、Agent 编排器、知识库/文件存储。调用顺序:学生提交材料 → 前台上传 → 后端解析文本与章节结构 → 后端创建整理任务并交给 Agent → Agent 检索原文知识点,取得带出处片段 → 生成结构化笔记、考点清单与思维导图 → 返回结果与状态 → 前端展示并保存版本 → 学生预览、追问或保存。

图里新加的「保存版本」一步对应 note.version:重新整理不覆盖旧版本,新版本独立成行。

fig08-seq-test

图 6-8 时序图:自测提交与批改

参与者为:学生、自测页面、后端服务、Agent 评分器、题库/学习记录。两个设计要点值得单独说:

  1. 取题时不下发答案。前端只拿到题干与选项,正确答案留在服务端——这不是不信任前端,是承认前端不可信。
  2. 批改与错题回填在服务端完成,前端只接收结果。错题回填必须和分数写入在同一个事务里,否则会出现「分数有了、错题没进库」的半截状态。

3.2.5 协作图——对象之间怎么连

fig09-collab

图 6-9 协作图

与时序图是同一类场景,但侧重链接关系而非时间顺序。参与者:学生、Web 前端、后端 API、领域 Agent、业务数据库、知识库/课件原文、管理员。消息按编号读:1 提交任务 → 2 前端上传/对话/展示 → 3 后端鉴权/任务/版本 → 4 Agent 检索原文与知识点 → 5 写入结果与记录 → 6 返回结构化结果 → 7 更新界面 → 8 反馈/追问。

协作图的价值在于暴露耦合点:领域 Agent 只与后端 API 和知识库相连,不直接连业务数据库;所有落库都经后端 API 的第 5 步完成。管理员一侧独立维护测试集与评测,与主链路只在「测试数据」处相连——这条边画出来的意义是:管理端坏了不影响学生端。

3.3 领域 Agent 怎么接进系统

fig10-agent

图 6-10 领域 Agent 与系统集成架构图

集成方式可以概括成「一个核心、两条边界」:

  1. 一个核心:Agent 智能核心承担提示词约束、任务规划与结果校验。它不直接面向用户,只接收后端服务层发来的「Agent 请求」,返回「结构化结果 / 评分」。
  2. 上行边界:Agent 需要外部能力时,只能调用工具层(文件解析、向量检索、OCR、公式转 LaTeX),由工具层访问数据层,再把原文片段与历史上下文回传。Agent 不持有数据库连接。
  3. 下行边界:所有生成结果先回到后端服务层,由后端完成鉴权、会话归属与落库;管理员通过测试集与通用模型对比做效果评测,评测数据同样经由数据层读取。

这条设计的目的是让「每条结论都有出处」在架构上可执行,而不只是写在提示词里:Agent 产出必须携带原文块引用,后端在落库前做引用校验;检索范围限定在当前用户有权限的课件,避免跨用户串数据。

落到实现里,这条边界的表现是笔记生成管线被切成六段,每段都有明确的失败处理:

阶段 进度 动作 失败处理
① 读取分块 5% 按任务的材料集合取全部分块 无分块则任务失败并说明原因
② 逐块抽取 10%~45% 每块调用抽取提示词,产出候选知识点 单块失败记告警继续
③ 证据绑定 — 每条候选必须带 chunk_key + 连续引文,回查原文 查不到即丢弃,计入 dropped_items
④ 去重归并 50% 按知识点名称归一化合并,取更长定义、并集要点与证据 —
⑤ 检索补全 58% 用知识点名回检材料,补充关联证据 检索为空则跳过
⑥ 渲染与自评 68%~94% 生成大纲、要点、思维导图;按 85/10/5 自评 渲染失败走确定性兜底

管线按「块」处理而不是整篇一次性投喂,好处很直接:单块失败不影响整单,坏块只记告警并跳过。代价是调用次数变多——68 页的全章整理要 71 次调用、49,167 tokens,单次成本约 1~2 元。这个取舍我们认了:宁可多花钱,也要让每一条知识点都能被查证。

3.4 数据库设计

3.4.1 一条主线

整个数据模型只有一条主线:

courseware(课件) → courseware_chunk(原文块) → knowledge_point(知识点)

原文块是最小定位单元:courseware_chunk 记录页码、页内序号、字符区间 char_start/char_end、内容类型;公式块额外存 LaTeX,图注块存截图路径。前端点「查看原文」能精确高亮到那句话,靠的就是这对比字符偏移。

知识点是枢纽:卡片、错题、自测题、导图节点全部通过 knowledge_point_id 汇聚,而知识点只通过 source_chunk_id 指回原文一次。出处只维护一次,下游所有功能自动获得出处——这是整个模型里最重要的一条决定。

3.4.2 概念模型:两张 ER 图

fig11-er-main

图 6-11 ER 图:数据主干(课件—原文块—知识点)

符号约定在此说明一次,全站统一:实线为外键,鸦爪端为多端,标注 1:N;虚线为无外键的逻辑关联。 qa_record.cited_chunk_ids 是 JSON 数组,逻辑上指向原文块,因此用虚线,不建数据库外键。

这里踩过一个坑:最初的两张 ER 图用的是 UML 空心三角表一对多,这一节重绘时改成鸦爪。两套符号并存会直接违反作业「使用一致的图形符号」的要求。最后统一到鸦爪,并在说明书里注明以本节图例为准。

fig12-er-loop

图 6-12 ER 图:学习闭环(卡片、错题、自测与导图)

挂在知识点上的四类学习行为,各自的结构选择都有理由:

  • flashcard 1:N review_log:卡片只存当前 SM-2 状态(ease_factor、interval_days、due_at),复习流水只追加评分记录,历史可复算。
  • self_test 1:N self_test_item:试卷与题目分离。逐题解析、逐题出处、错题回填都需要题目级粒度,塞进一个大表的 JSON 字段做不到。
  • wrong_question 独立成表:用 (user_id, source_type, source_id) 唯一键去重,再按知识点聚合出薄弱度。
  • mindmap_node 自关联:parent_id 指向父节点,邻接表存树,配合递归 CTE 一次拉出整棵树。

四类资产都指向同一知识点,保证卡片、错题、自测、导图的薄弱度统计口径一致——否则同一个人在不同页面看到的「薄弱」会是四个不同的东西。

3.4.3 关系数据模型:13 张表

fig13-relational

图 6-13 关系数据模型图(13 张表)

按三个数据域组织:账号与课件域 3 张、知识组织域 4 张、学习闭环域 6 张。主键、外键、唯一键与删除策略如下(节选关键关系):

关系 基数 删除策略
courseware → courseware_chunk 1:N CASCADE
courseware → note 1:N CASCADE
note → note_section 1:N(parent_id 自关联) CASCADE
courseware_chunk → knowledge_point 1:N(source_chunk_id) SET NULL
knowledge_point → flashcard / wrong_question / self_test_item / mindmap_node 1:N CASCADE / SET NULL
flashcard → review_log 1:N CASCADE
self_test → self_test_item 1:N CASCADE

删除策略不是随手配的,它对应一条产品规则:课件被删掉,学生已经攒下的错题不应该跟着消失。 所以 courseware → courseware_chunk 是 CASCADE(原文块没有独立价值),而 courseware_chunk → knowledge_point 是 SET NULL(知识点保留,出处标记为缺失,界面显示「依据待核验」)。同理 wrong_question.courseware_id 也是 SET NULL——错题是学生的资产,不是课件的附属品。

完整关系模式见说明书第 5 章数据字典(13 张表逐字段列出类型、约束、默认值与索引),这里不重复占篇幅。

3.4.4 对象关系映射

fig14-orm

图 6-14 ORM 映射图(实体类与数据表)

13 个实体类与 13 张表一一对应。映射约定写死在这里,避免各人写法不一:

  • 表字段 snake_case ↔ 类属性 camelCase 自动转换,如 knowledgePointId ↔ knowledge_point_id;
  • id 与外键用 64 位整数;DECIMAL 映射精确十进制类型,得分与 EF 不用浮点数;
  • DATETIME 按 UTC 解释,展示时本地化;
  • JSON 字段序列化为列表或对象并校验结构;
  • TINYINT 枚举在实体层转枚举类型;
  • 软删除字段由框架统一过滤,查询默认排除 deleted_at 非空的记录。

一条容易被忽略的:数据库级 CASCADE 不等于 ORM 级自动删除。实体删除统一走服务层,避免「ORM 删一遍、数据库再级联删一遍」这种重复级联。

3.4.5 三类数据分别怎么存、怎么管

作业要求「说明领域知识、用户交互记录或任务执行结果等数据的存储与管理方式」。这三类在我们的设计里走的是三条不同路径:

数据类别 载体 存储方式 管理方式
领域知识 courseware_chunk + knowledge_point 原文块存纯文本与字符区间,公式块另存 LaTeX;向量存向量库,MySQL 只留 embedding_ref 随课件版本走;按 (courseware_id, note_version, kp_code) 唯一,重新整理时新版本独立成行,不覆盖旧版
用户交互记录 qa_record + review_log 问答存问题、回答与引用块 ID 数组(JSON);复习流水只追加,不更新 引用编号必须逐个做归属校验,记录了编号不代表回答已获原文支持;流水只增不改,支持历史复算
任务执行结果 note / note_section / self_test / self_test_item 笔记按版本存正文与章节树;自测主从两表存试卷与题目 失败不发布(模型调用失败时不得发布半套结果);交卷幂等(status=2);错题只回填一次(is_backfilled)

3.4.6 事务与一致性

四处一致性设计,都是踩过或预判过的并发问题:

  • 评分事务:写 review_log 流水与更新 flashcard 状态在同一事务内完成(sp_review_card)。这里有个已知缺陷写在文档里:过程内部加锁,但「下一次间隔」等参数在调用前就算好了,加锁无法保证入参基于最新状态。修法是二选一——同一事务内锁定后计算,或引入预期版本号拒绝过期更新。我们不假装它会自动正确。
  • 交卷幂等:self_test.status = 2 保证整卷只批改一次;self_test_item.is_backfilled 保证每道错题只回填一次,重复交卷不会把 wrong_count 加两次。锁住同一自测主记录,使并发提交串行执行。
  • 版本一致性:知识点按 (courseware_id, note_version, kp_code) 唯一;卡片、错题、自测题、导图各存 note_version 快照,避免新旧版本混用。写新内容时必须显式复制版本号,不能依赖默认值 1——这是最容易出的错。
  • 出处一致性:课件删除时下游 source_chunk_id 置空而不级联删除,历史记录保留;但物理删除课件会级联删掉复习流水,所以流水并非永久保留,清理前必须先定保留政策。本版不安排自动物理清理。

3.5 设计模型与已实现结构的对齐

这一节是我们内部争论最久的地方,也是这份文档最值得写的地方。

概要设计文档给出的是 13 张表的正式部署模型(MySQL 8.0.16+);当前线上演示版本跑在 SQLite 上,业务表为 10 张,加 1 张 FTS5 虚拟表、5 张影子表和 sqlite_sequence,共 17 个对象。 这两个数字不一样,不是笔误,是两个口径。

设计模型(13 表,MySQL) 已实现(SQLite) 说明
sys_user users 账号、口令哈希、角色
courseware documents 上传文档、解析状态、页数、分块数
courseware_chunk chunks 分块原文、页码、章节归属
note / note_section / knowledge_point notes / note_items / note_versions 笔记正文、知识点条目、版本快照(实现层做了拆分)
qa_record chats 问答历史
— example_records / formula_records 实现端新增,设计模型里并入知识点与出处
flashcard / review_log / wrong_question / self_test / self_test_item / mindmap_node 未实现 学习闭环 6 张表处于设计阶段,表结构与事务已定,代码未写
courseware_chunk 全文检索 ft_chunks(FTS5 + 5 影子表) 设计里计划用向量库 + ngram 全文索引,当前用 FTS5 二元组

为什么这样安排:这一轮的交付目标是「把设计讲清楚」,不是「把 13 张表都跑起来」。 已经上线的部分先保证可演示、可验证(单机 SQLite、WAL、备份即拷文件);学习闭环那 6 张表,涉及 SM-2 调度、交卷幂等、错题回填三处并发,宁可先把模型和事务边界定死、写完验收用例,也不要先写一版后面推倒重来。

所以《数据库设计说明书》第 9 章的 14 条验收用例(T01–T14)目前是待执行状态,文档里也明确写了「尚未在 MySQL 实例中执行初始化、并发测试、性能测试或恢复演练,不能据此声称验收已通过」。这句话是给自己留的账,不是谦虚。

3.6 概要设计的边界:我们没做什么

一份诚实的概要设计,边界和功能一样重要。当前明确不在范围内的:

不在范围 影响 处理方式
领域知识库未灌数据 问答只基于用户上传的课件 表结构已建,管理端可维护,数据待填
智能自测与错题本未实现 用例图里的自测 / 错题本当前不可用 设计稿与表结构已就绪,见 3.4.2、3.4.3
向量检索关闭 同义改写问题召回不足 配置项 ENABLE_VECTOR=0 保留,后续与二元组结果融合排序
手写公式 OCR 未启用 图片型课件无法识别 接口预留,未配置密钥时明确报错,不假装解析成功
无事实核验能力 多份材料冲突时并列保留,不判断对错 设计上如实呈现冲突,由用户在 U10 逐项取舍
单 worker 并发整理会排队 与 SQLite 匹配的取舍;后续可上任务队列

四、团队协作(20%)

4.1 协作机制

12 个人的项目,最怕的不是写得慢,是合并的时候互相覆盖。这一轮我们把四条规则定在最前面:

  1. 文档分人负责,一份文档一个主笔。系统设计说明书由徐张睿主笔,数据库设计说明书由章玲主笔(吴雅晴负责数据库设计、出图与建库脚本),原型设计说明书随原型出。主笔之外的人只提修改意见,不直接改正文——避免一份文档被 12 种语气写出来。
  2. 图纸共用一套图例。14 张概要设计图分三组出:UML 六类图(图 6-3~6-9)由张佩琪、陈瑀涵画,数据模型图(图 6-11~6-14)由吴雅晴画,架构与集成图(图 6-1、6-2、6-10)随后端设计一并出。画的人不同,但配色、图例、字号是同一套,博客里再统一编号——这是「使用一致的图形符号」唯一能落地的做法。
  3. 仓库即唯一事实源。文档、图、SQL 全部进 GitHub 团队仓库,群里的文件和仓库里的文件冲突时,以仓库为准。这一条直接连着后面 4.5 那个问题:为什么 Contributors 一开始只统计到 3 个人。
  4. 节点写在群里,不写在脑子里。「11.5 原型 / 11.7 UML 与数据库 / 11.8 两份说明书 / 11.9 博客」这四个时间点是 11 月 4 日当晚在群里公开定下的(截图见 4.4),谁落后了群里直接问,不靠私下催。

4.2 本次作业分工

分工是 11 月 4 日晚上在群里定下来的:组长吴政迅发完作业链接,当场给出临时分工,大家拍一拍确认(过程见 4.4),事后沉淀进飞书协作文档。落到纸面上是这样:

成员 学号 本次作业负责 产出
王可人 102402104 原型设计:Figma 制作用户端与后台管理原型(与梁倩合作) 20 个页面原型
梁倩 102401607 原型设计:同上(与王可人合作) 20 个页面原型
张佩琪 102402102 UML 设计:用例图、活动图、类图、时序图、协作图(与陈瑀涵分工) 图 6-3~6-9
陈瑀涵 102402107 UML 设计:同上(与张佩琪分工) 图 6-3~6-9
吴雅晴 102402103 数据库设计:ER 图、关系数据模型、ORM 映射、建库脚本 图 6-11~6-14、schema.sql
蔡靖熹 102402116 GitHub 仓库管理:仓库搭建、文档提交、版本管理 仓库结构与提交记录
徐张睿 102402139 后端组长;系统设计说明书主笔 系统设计说明书 V1.1
章玲 102402101 数据库设计说明书主笔 数据库设计说明书 V1.1
沈文驰 032401135 后端设计展示 后端设计图与展示文档
庄剑钇 102402152 博客发布:排版、编写、提交 本篇博客
吴政迅 102402138 组长;团队协作记录:开发计划、分工安排、协作过程 飞书协作文档
陈佳慧 052405110 机动补位、项目验收测试 补位产出、验收记录

这张表有个特点值得一提:它是按「一份交付物一个主笔」切的,不是按「一个人一块代码」切的。这一轮的产出是文档和图,主笔制能保证一份文档从头到尾只有一个语气;到第三、四周真正写代码时,分工才切回按模块(见 4.3 第 2–3 周)。

所以表里有两处「两人合作」和一处「机动」:王可人和梁倩一起把 20 个页面画完,张佩琪和陈瑀涵分掉六类 UML 图,陈佳慧不预先绑定模块,哪边工作量大就去哪边补——项目里最容易被忽略的角色,往往是那个没有固定模块的人。

4.3 后续开发计划(从本次截止日起)

作业要求「开发计划至少包括从本次作业截止日期开始的后续开发安排」。这一轮不只是交文档——项目整体完结时间定在 2026 年 12 月 5 日,从本次交稿起还剩 4 周,按周排是这样:

周次 时间 里程碑 主要任务 产出
第 1 周 11.10–11.16 数据层和接口准备完毕 新增 3 张表(错题表、自测表、导图节点表)的 SQL 编写和迁移;接口文档定稿;前端原型确认 SQL 脚本、接口文档、原型确认
第 2 周 11.17–11.23 核心功能上线 错题收集、错题统计模块开发;出题、自动批改模块开发;前后端联调 错题本页面、自测页面、接口联调通过
第 3 周 11.24–11.30 扩展功能联调完成 图形化思维导图开发;全链路联调;回归测试 思维导图页面、联调报告、测试报告
第 4 周 12.01–12.05 项目验收完成 修 bug、性能优化;演示录屏;答辩 PPT;最终部署 测试报告、演示录屏、答辩 PPT

第 1 周先做数据层和接口,是因为第 2 周的后端和前端要并行开工——表结构和接口契约不定下来,两边就只有互相等。同理,第 3 周把功能开发和联调压在一起,是因为思维导图是唯一一个「前端渲染重、后端只给节点」的模块,它必须等到其他模块的节点数据格式稳定后才能开工。

另外,这 4 周分下去之后,分工就从 4.2 的「按交付物」切回了「按模块」:后端 6 人分两组(错题收集与统计、出题与批改),前端 4 人一人一个页面,张佩琪负责前后端联调与回归测试。文档阶段一起写,实现阶段各管一块——这是两次切分的衔接方式。

每个里程碑的判定标准统一为一条:「能不能在没有作者在场的情况下被别人跑起来」。跑不起来就不算完成。

4.4 协作过程记录

这一轮的分工不是开会宣布的,是在群里谈出来的。整个过程可以按时间线还原,三张截图都来自「软件工程第2小组」群(12 人)。

图 7 11 月 4 日 17:33,组长发作业链接并给出临时分工

collab-chat-1

第一条消息是作业链接,第二条只有一句话:「第二次团队作业(原型设计+概要设计),大家都读一下题。」几分钟后,组长把分工按人发了出来——王可人、梁倩做原型;张佩琪、陈瑀涵做 UML;吴雅晴做数据库;沈文驰做后端设计展示。

图 8 分工补完,用「拍一拍」确认

collab-chat-2

接着补完剩下的几块:徐张睿写系统设计说明书、章玲写数据库设计说明书、蔡靖熹管 GitHub 仓库、庄剑钇发博客、吴政迅出团队协作记录;陈佳慧不绑模块,作为机动人员「哪边工作量大,就在群里说一声找她帮忙」。末尾一句是:「大家觉得可以的话,拍一拍我的头像,有其他意见在群里说,11.9 截止时间挺赶的 @全体成员」。

图 9 18:43,把分工落成四个时间节点

collab-chat-3

分工定完,还差一个关键的东西:每个节点什么时候交。于是有了这四行——

11.5 完成原型设计
11.7 UML和数据库设计
11.8 两个说明书
11.9 博客

分工与时间表最后沉淀成一份飞书文档《天生一队 团队协作记录 — 开发计划与分工安排》,里面有 4 周开发计划、12 人分工表和三次作业的分工追溯。其中 4 周计划与本次分工已摘录进上面的 4.2、4.3 两节,原文档在这里(团队内部文档,如需查阅可联系组长开通权限):

https://my.feishu.cn/docx/O1xxdqtRkodnpWxqk4QcdQ9mnSe

图 10 团队仓库的提交记录

image

4.5 GitHub 贡献记录

团队仓库:https://github.com/TCHHPK/FZU-SoftwareEngineeringTeamHW02

作业要求「团队的每一个成员都应该在 GitHub 上有贡献记录」。这一条我们经历了两个阶段。

第一阶段(11 月 8 日上午):Contributors 只有 3 个人。 原因是文档走的是「主笔汇总、统一上传」的路子——一个人写完、一个人上传,其他人的贡献留在评审意见和线下修改稿里,没形成 commit。

第二阶段(11 月 8 日下午到晚上):补到 12 个人。 发现问题之后,我们按模块把工作拆开重新提交了一遍:UML 六类图两次提交(Starry-Nova、cccch412)、后端设计展示与 14 张设计图(otonash1)、数据库说明书与建库脚本(102402103wuyaqing)、系统设计说明书 PDF(always123-11)、验收报告(zzzoe116)……

贡献者 提交数 主要提交内容
Xun1342 3 仓库初始化与文档上传
102402103wuyaqing 2 数据库设计说明书、ER 图、建库脚本
KongCheng06、Luminance-l、Starry-Nova、always123-11、asakinza、cccch412、otonash1、zzzoe116、VioletQAQ666、TCHHPK 各 1 原型说明书、UML 图、后端设计展示、系统设计说明书 PDF、验收报告等

五、踩过的坑

写文档的过程比写代码更容易暴露问题。这一轮遇到四个值得记下来的:

1. 目录生成不出来。 Word 里插了目录域,更新后是空的。原因是正文齐刷刷用了「加粗 + 手动调字号」,而不是真正的标题样式——排版看着一样,结构上是平的。后面把所有章节改成 Heading 1/2/3 才能自动出目录。这件事的教训不是「记得用标题样式」,而是「看起来对」和「结构对」是两件事。

2. 图例不统一。 前两张 ER 图用 UML 空心三角表示一对多,后五张用鸦爪。两套符号混在一份文档里,等于让评阅人做符号翻译。最后统一重绘成鸦爪,并在正文注明以新图例为准。

3. 表数量对不上。 数据库文档说 13 张表,系统文档说 17 张表。查了半天发现两边都在数自己那套口径:一个是设计模型的业务实体数,一个是 SQLite 里的物理对象数(含 FTS5 虚拟表、5 张影子表和 sqlite_sequence)。数字冲突往往不是谁算错了,而是没写清楚「在数什么」。 现在两个文档里都注明了统计口径。

4. 页码与行内注释。 .env 里写了 ENABLE_VECTOR=0 # 向量检索,结果值把注释一起读进去了,布尔开关静默失效,查了两小时。代码现在做了剥离处理,但这条已经写进配置说明的注意事项里。


六、交付物与链接

交付物 链接
GitHub 团队仓库 https://github.com/TCHHPK/FZU-SoftwareEngineeringTeamHW02
原型在线演示(Figma) https://www.figma.com/proto/iDdth59yZaOjSaWgHvajkQ/agent2.0
系统设计说明书 PDF(22 页) https://github.com/TCHHPK/FZU-SoftwareEngineeringTeamHW02/blob/main/系统设计说明书-V1.1.pdf
数据库设计说明书 PDF https://github.com/TCHHPK/FZU-SoftwareEngineeringTeamHW02/blob/main/系统设计说明书-V1.1.pdf
已实现系统演示 http://47.114.55.115:8000

七、一点感想

这次作业做到一半,我们意识到它其实在逼我们回答一个问题:设计文档到底是给谁看的?

如果是给老师看的,那它应该更像一份「成果展示」——把漂亮的东西放在前面,把没做的藏在附录。但我们最后选了另一种写法:把没实现的 6 张表、没跑的 14 条验收用例、没统一的表数量口径、以及 Contributors 从 3 个人补到 12 个人的全过程,全部写进正文。

理由很实际:下一轮开发的是我们自己。如果现在把「SQLite 版」写成「MySQL 版已上线」,三周后接手的人(可能就是我们自己)会照着一份假的文档去写代码,然后在联调时发现问题,再把设计推翻重来——那时候的代价比现在写清楚大得多。

所以这份概要设计里,我们最看重的不是 14 张图画得多整齐,而是 3.5 节那张「13 表 vs 17 表」的对照表,和 3.6 节那张「我们没做什么」的清单。一个系统的成熟度,往往体现在它敢不敢承认自己现在到哪一步。

原型画了 20 个页面,概要设计画了 14 张图,最后落在两句话上:每条结论都要有出处,每份文档都要对得上代码。 这两句话,我们打算一路带到答辩。

posted @ 2026-11-09 19:34  庄剑钇  阅读(15)  评论(0)    收藏  举报