4.4 完成业务:实现核心流程与异常处理¶
把分散功能连接成一条真正完成的业务流程¶
多个管理页面,不等于完整业务
“用户管理、信息管理、记录管理”都能增删改查,并不代表项目已经解决了用户问题。完整业务必须有明确的发起人、处理过程、状态变化和最终结果。
本节只选择项目最重要的一条流程,把不同角色、页面、接口和数据连接起来,并同时处理失败、越权和重复操作。
本节学习目标
根据需求和系统设计,实现一条跨页面或跨角色的核心业务流程,确保每一步具有明确权限、状态和数据变化,并能够演示正常流程和关键异常流程。
🎯 本节完成后,你要交付¶
| 成果 | 要求 |
|---|---|
| 核心流程实现 | 能够从用户发起连续操作到获得最终结果 |
| 业务状态实现 | 页面、接口和数据库使用一致的状态及转换规则 |
| 权限与异常处理 | 未登录、越权、状态不允许和数据异常得到明确处理 |
| 数据一致性验证 | 关键操作成功时一起完成,失败时不留下错误数据 |
| 流程测试记录 | 至少覆盖正常、业务失败和权限失败流程 |
| 阶段演示版本 | 不依赖手工修改数据库即可连续演示 |
本节不要求把项目所有功能做完。先完成一条最有价值的核心流程,再补充其他功能。
🧭 第一步:选定唯一的核心流程¶
从《需求分析说明书》中选择一条最能体现项目价值的流程。
| 项目 | 核心流程示例 |
|---|---|
| 宿舍报修 | 学生提交 → 管理员分配 → 维修人员处理 → 学生确认 |
| 活动报名 | 管理员发布活动 → 学生报名 → 管理员查看名单 → 学生查看结果 |
| 图书管理 | 用户查询图书 → 提交借阅 → 办理归还 → 库存恢复 |
| 校园集市 | 卖家发布商品 → 买家联系或下单 → 卖家处理 → 交易完成 |
| 预约管理 | 用户选择服务 → 提交预约 → 管理员确认 → 用户查看结果 |
核心流程必须回答¶
- 谁发起?
- 什么时候允许发起?
- 谁负责处理?
- 处理过程中状态怎样变化?
- 哪些数据被新增或修改?
- 用户最终得到什么结果?
- 失败时数据和页面怎样处理?
本节只选择一条主线
不要同时实现报修、通知、统计、评价和推荐等多条流程。先把最重要的一条流程做完整,再把其他能力作为后续任务。
🗺️ 第二步:把流程拆成可验证的小步骤¶
以“宿舍报修”为例:
把每一步写成任务表:
| 步骤 | 操作角色 | 页面操作 | 接口 | 前置状态 | 数据变化 | 成功结果 |
|---|---|---|---|---|---|---|
| 1 | 学生 | 提交报修 | 【填写】 | 无 | 新增报修记录 | 状态为待分配 |
| 2 | 管理员 | 分配人员 | 【填写】 | 待分配 | 写入维修人员 | 状态为待处理 |
| 3 | 维修人员 | 开始或完成处理 | 【填写】 | 待处理 | 写入处理结果 | 状态为待确认 |
| 4 | 学生 | 确认结果 | 【填写】 | 待确认 | 写入确认时间 | 状态为已完成 |
一次只实现一步¶
推荐顺序:
每一步都应有独立的成功结果。不要等整条流程全部写完后才第一次启动项目。
🔄 第三步:用状态控制业务操作¶
状态表示业务对象当前处于哪个阶段,也决定哪些操作可以执行。
状态转换表¶
| 当前状态 | 允许操作 | 操作角色 | 前置条件 | 下一状态 | 不允许时 |
|---|---|---|---|---|---|
| 待分配 | 分配维修人员 | 管理员 | 维修人员有效 | 待处理 | 返回状态或人员错误 |
| 待处理 | 提交处理结果 | 被分配的维修人员 | 已填写结果 | 待确认 | 返回权限或状态错误 |
| 待确认 | 确认完成 | 报修学生本人 | 结果已提交 | 已完成 | 返回权限或状态错误 |
| 已完成 | 无核心操作 | — | — | — | 拒绝重复处理 |
项目应使用自己的真实状态,不要照抄示例。
状态实现的三个一致¶
例如数据库使用 PENDING,后端却判断 WAITING,页面再显示“处理中”,流程一定会出现混乱。状态名称和含义应以《系统设计说明书》为准。
不允许前端任意修改状态¶
错误做法:
正确思路:
状态变化应由具有业务含义的操作触发,而不是由前端自由填写。
🔐 第四步:在每一步检查权限和数据范围¶
跨角色流程不仅要判断“是不是管理员”,还要判断“能不能操作这一条数据”。
| 操作 | 功能权限 | 数据权限 |
|---|---|---|
| 学生提交报修 | 已登录学生 | 创建人只能是当前用户 |
| 管理员分配任务 | 管理员 | 只能处理管理范围内的记录 |
| 维修人员提交结果 | 维修人员 | 只能处理分配给自己的任务 |
| 学生确认完成 | 已登录学生 | 只能确认自己提交的报修 |
当前用户从登录状态获取¶
不要信任前端提交的 userId、role 或“是否管理员”。即使页面没有显示按钮,后端仍要拒绝直接调用接口的越权请求。
权限失败后的结果¶
- 未登录:返回 401,页面引导重新登录;
- 角色不允许:返回 403,页面提示没有权限;
- 无权操作该数据:拒绝操作,不返回敏感内容;
- 数据不存在:返回明确业务错误;
- 拒绝操作时,数据库内容保持不变。
🧠 第五步:把业务规则放在后端¶
页面可以提前提示,但最终规则必须由后端业务层执行。
示例:活动报名¶
各层职责¶
| 位置 | 适合处理 |
|---|---|
| 前端页面 | 按状态显示按钮、进行基础格式校验、展示失败原因 |
| Controller/Servlet | 接收参数、获取当前用户、调用业务服务 |
| Service | 权限、业务状态、重复操作和核心规则 |
| Mapper/DAO | 按条件查询和更新数据库 |
| 数据库 | 主键、唯一、非空、外键等最后约束 |
不要把全部判断写在页面中,也不要让 Controller 或 Servlet 堆积整条业务流程。
🛡️ 第六步:处理失败、重复和数据一致性¶
课程项目不需要设计复杂的高并发架构,但必须处理会明显造成错误数据的情况。
必须考虑的异常¶
| 异常 | 应有结果 |
|---|---|
| 对象不存在 | 返回明确提示,数据不变 |
| 当前状态不允许 | 拒绝操作并提示当前状态 |
| 重复点击提交 | 不创建多条重复记录 |
| 重复确认或归还 | 不重复修改状态、库存或数量 |
| 操作他人数据 | 返回无权限,不能泄露数据 |
| 多步写入中途失败 | 已完成的部分能够回滚 |
| 登录过期 | 清除失效状态并重新登录 |
事务只解决“一起成功或一起失败”¶
以下操作通常需要放在同一个事务中:
如果其中一步失败,其他步骤也不能保留。
事务不能代替业务检查
开启事务并不会自动阻止重复提交或越权操作。仍然需要检查当前用户、当前状态、唯一约束和业务条件。
最后一个库存或名额¶
对于库存、名额等有限资源,至少做到:
- 在后端重新读取和检查当前数量;
- 使用数据库条件更新、唯一约束或适合当前技术的控制方式;
- 更新失败时返回“库存不足”或“名额已满”;
- 不让数量变成负数;
- 用两个快速或并行请求验证结果。
如果项目没有库存、名额或类似竞争资源,不必为了使用“锁”而增加复杂设计。
🖥️ 第七步:让页面跟随业务结果变化¶
完整流程不仅是后端状态变化,用户还需要知道下一步做什么。
页面需要处理¶
- 当前记录处于什么状态;
- 当前用户可以执行什么操作;
- 不允许操作时为什么;
- 操作进行中时防止重复点击;
- 操作成功后刷新列表、详情或数量;
- 操作失败后保留原表单并显示原因;
- 登录过期后跳转登录;
- 空数据、加载和请求失败状态。
不同角色看到不同入口¶
前端应根据角色和状态调整按钮,但最终能否操作仍由后端判断。
🧪 第八步:连续走查完整流程¶
准备不同角色测试账号和固定测试数据,不依赖手工修改数据库推进流程。
最低测试集合¶
正常流程¶
业务失败流程¶
选择一个最重要的失败情况:
- 重复报名;
- 库存不足;
- 已关闭活动继续报名;
- 已完成任务重复处理;
- 不存在的对象被操作。
权限失败流程¶
选择一个越权情况:
- 普通用户直接调用管理接口;
- 用户修改 ID 操作他人数据;
- 未分配的处理人员提交结果;
- 退出登录后继续提交。
数据一致性检查¶
操作后检查:
- 业务记录状态是否正确;
- 关联字段是否正确;
- 库存、名额或数量是否正确;
- 操作失败时数据是否保持不变;
- 页面显示是否与数据库一致。
核心流程测试记录¶
| 场景 | 初始数据 | 操作步骤 | 预期结果 | 实际结果 | 证据 | 结论 |
|---|---|---|---|---|---|---|
| 正常流程 | 【填写】 | 【填写】 | 最终状态正确 | 【填写】 | 页面/接口/数据库 | 通过/未通过 |
| 业务失败 | 【填写】 | 【填写】 | 拒绝且数据不变 | 【填写】 | 接口/数据库 | 通过/未通过 |
| 权限失败 | 【填写】 | 【填写】 | 返回 401/403 | 【填写】 | Network/接口 | 通过/未通过 |
| 重复操作 | 【填写】 | 【填写】 | 不产生重复结果 | 【填写】 | 数据库 | 通过/未通过 |
需要手工改数据库才能演示,说明流程还没有完成
状态变化应通过页面和接口完成。初始化测试数据可以使用脚本,但不能在演示过程中手工把状态从“待处理”改成“已完成”。
🤖 第九步:让 AI 围绕一条流程工作¶
先要求 AI 读取当前实现并检查设计对应关系:
实现时一次只处理一个状态转换:
遇到错误时提供完整证据,不要只说“流程不对”:
👀 第十步:集成审查与提交¶
完整流程跑通后,进行一次集中审查:
- 每一步是否对应已确认需求;
- 页面、接口和数据库状态是否一致;
- 后端是否检查角色和数据权限;
- 是否允许非法状态跳转;
- 重复操作是否造成重复数据;
- 多表更新失败时是否留下部分数据;
- 页面是否正确刷新和提示;
- 是否仍依赖模拟数据或手工改库;
- 是否修改了无关模块或公共基础设施;
- 测试记录能否复现。
按流程步骤提交更容易定位问题:
提交前运行相关测试、重新走查完整流程,并检查:
✅ 本节验收清单¶
- 已选择项目最重要的一条核心流程;
- 流程具有明确发起人、处理过程和最终结果;
- 已拆分为可以逐步验证的小步骤;
- 每一步明确页面、接口、角色、状态和数据变化;
- 状态值在页面、后端和数据库中一致;
- 状态只能通过明确业务操作转换;
- 后端检查登录、角色和数据权限;
- 当前用户身份不依赖前端提交;
- 关键业务规则在后端执行;
- 重复操作不会产生重复业务结果;
- 相关多表更新具有正确的事务边界;
- 页面能够展示当前状态和失败原因;
- 正常流程能够从开始连续完成到结束;
- 至少验证一条业务失败流程;
- 至少验证一条权限失败流程;
- 操作失败时数据库保持正确;
- 演示过程不需要手工修改数据库;
- 已保存测试证据和清楚的 Git 提交。
📝 总结¶
- 完整业务不是页面数量:要有发起、处理、状态变化和最终结果。
- 状态决定当前能做什么:每次转换都要检查角色、条件和目标状态。
- 权限要检查具体数据:不仅判断角色,还要判断能否操作这一条记录。
- 失败不能破坏数据:重复、越权和中途失败都应得到明确处理。
- 演示必须连续完成:不依赖模拟数据和手工改库,才算真正形成业务闭环。