TTeamDesk 学习手册
本章目录 展开导航 ↓

06 · 验证、排错与练习:证明业务真的跑通#

“页面能打开”和“业务正确”不是一回事。本章分别检查成功路径、失败边界和并发行为,并把错误定位到具体一层。以下是供你本地执行的学习验收,不表示教程编写过程中已经启动数据库或完成这些运行测试。

1. 按一条故事完成手动验收#

先用当前仓库的注册账号创建团队,成为管理员。依次操作:

操作 预期 验证的知识点
空白标题、空描述提交 被拒绝,有字段提示 输入校验
创建“无法登录” 提示成功并打开详情 前端到数据库写入
刷新详情 内容仍存在 数据持久化
回列表,切换状态筛选 总数与行数据匹配 条件与分页
待处理 → 处理中 成功,版本递增 合法路径
处理中 → 已解决,不填说明 当前版本拒绝 依赖业务状态的校验
填说明后解决,再填新的说明重新打开 成功,显示新状态 可恢复状态与说明规则
填写关闭说明后关闭工单 成功,之后无可用状态操作 终态规则
访问格式错误的 ID 明确错误 UUID 输入校验
访问合法但不存在的 UUID 未找到提示 资源不存在处理
旧版本修改同一工单 冲突并读取最新状态 并发控制

最后用 Prisma Studio 对照标题、状态、版本和团队归属。当前版本还会有操作记录和提醒任务,这是后续阶段接入留下的数据,不代表第一阶段必须掌握它们的完整实现。

2. 自动检查各自能证明什么#

bash
pnpm lint
pnpm typecheck
pnpm format:check
pnpm test

ESLint 检查代码规则,TypeScript 检查类型,Prettier 检查格式,Vitest 执行行为测试。任何单项通过都不能代替其他验证,尤其类型正确不意味着权限正确。

默认测试中,未提供 TEST_DATABASE_URL / TEST_REDIS_URL 的集成测试会跳过。输出全绿时也要看通过与跳过的数量,不能据此宣称数据库链路已验证。

先读两个测试入口#

  • tests/ticket.test.ts:拒绝空白输入、非法优先级与异常分页。无须运行数据库,适合理解边界条件。
  • tests/tickets.integration.test.ts:使用真实数据库、appRouter.createCaller 和明确用户上下文,覆盖当前工单、身份与团队规则,包括版本冲突。

状态路径和操作记录的更多测试在 ticket-events.integration.test.ts。这些测试按业务约定列合法路径,而非直接读取 allowedTransitions 生成预期值,避免实现写错后测试也跟着错。

两个文件的证据不能混写:tickets 用例明确检查并发修改只有一次成功、另一项 CONFLICT;它末尾对关闭后修改只断言抛错,没有固定断言是哪种错误。完整 16 组路径及状态说明的精确检查在记录测试中,读法见第 21 章。

createCaller 经过 tRPC procedure、输入校验与服务层,但不经过浏览器网络和 Next.js 的 HTTP Route Handler。因此它不能单独证明 Cookie 传输、Origin 检查、页面消息都正常;这些要由相应认证测试、HTTP 或浏览器验收补足。

3. 运行真实数据库测试#

使用独立的本地测试数据库,并先对它应用仓库迁移。以下示例通过现有 PostgreSQL 容器创建 teamdesk_test,仅首次创建时执行 createdb:

bash
pnpm db:up
# 仅首次创建;已有 teamdesk_test 时跳过下一行
docker compose exec -T postgres createdb -U teamdesk teamdesk_test
DATABASE_URL=postgresql://teamdesk:teamdesk@localhost:5433/teamdesk_test pnpm db:migrate
pnpm db:generate
TEST_DATABASE_URL=postgresql://teamdesk:teamdesk@localhost:5433/teamdesk_test pnpm exec vitest run tests/tickets.integration.test.ts tests/ticket-events.integration.test.ts

如果数据库已存在,跳过创建即可。上面的临时环境变量仅影响该条命令,不会改写 .env。这两组测试聚焦当前工单与状态规则,不要求 Redis 队列在线;当前创建逻辑仍会在 PostgreSQL 中保存提醒任务。

学习第一阶段不要求立即执行所有后台任务测试。如果要验证当前项目的全部集成能力,暂停开发 Worker,启动 Redis 后执行:

bash
TEST_DATABASE_URL=postgresql://teamdesk:teamdesk@localhost:5433/teamdesk_test \
TEST_REDIS_URL=redis://127.0.0.1:6380 pnpm test:integration

测试仅清理自己创建的数据,队列测试使用随机前缀;仍应明确指向测试环境。Worker 消费哪个库取决于它自己的 DATABASE_URL,临时设置 TEST_DATABASE_URL 不会让它自动切库。不要启动连接测试库的常驻 Worker;与测试库完全分离的开发 Worker 不会因这个测试命令就读到测试待办。没有连接环境时,如实记录“集成测试未执行”,不要把跳过写成通过。

4. 按数据流排查,不从头盲改#

先问“失败最早出现在哪层”,再查下一层:

现象 优先检查 原因
Docker 无法连接 Docker Desktop 是否启动 CLI 存在不代表引擎在运行
数据库连接失败 容器健康、5433 端口、DATABASE_URL 宿主机连接地址与容器端口不同
报表或列不存在 pnpm db:migrate 是否对当前库成功 生成 Client 不会创建表
Prisma 字段或类型过期 pnpm db:generate,重启开发进程 模型、生成文件、旧进程可能不一致
POST 被来源检查拒绝 地址栏 Origin 与 BETTER_AUTH_URL localhost 与 127.0.0.1 不相同
已登录却不能操作 会话 Cookie、当前团队、成员角色 登录与业务授权是两次判断
提交后没有请求 Form 字段提示、safeParse 结果 可能在浏览器就已拒绝
请求有响应但创建失败 tRPC 错误内容、字段错误 HTTP 返回不等于业务成功
数据库更新了,页面仍旧 mutation 成功回调、query key、失效逻辑 服务端写入不会自动更改客户端缓存
看起来没变化却 CONFLICT 详情版本与数据库版本 分配、回复等当前能力也可能推进版本
默认测试通过但未查库 Vitest 的 skipped 与环境变量 集成测试受显式连接开关控制
提醒 / 导出不执行 Worker、Redis、队列前缀 属于后台任务链路,不是基础建表失败

浏览器 Network 适合确认请求是否发出、输入与响应;服务端日志适合定位内部异常;Prisma Studio 适合确认最终数据。不要在浏览器日志输出连接串或密钥。

5. 三个跟源码走的练习#

练习 A:画出创建工单的字段来源#

不改代码,列出创建请求和数据库新增记录的字段,逐个标注来自表单、会话、团队上下文还是默认值。

动手练习查看参考答案

参考答案:标题、描述、优先级来自表单;团队 ID 来自选择后还要服务端验权;创建者来自已验证会话;状态与版本使用初始默认值;ID、编号与时间由 Prisma / 数据库机制产生;当前截止时间由服务端根据优先级计算。

检验点:浏览器不能通过传一个 creatorId 就替别人创建,也不能凭借真实的 teamId 自动获得权限。

练习 B:解释筛选后为什么回第一页#

先翻到后面的页码,再筛选只剩少量工单的状态。找到 setFilters({ status: ..., page: 1 }),解释去掉 page: 1 后会发生什么。

动手练习查看参考答案

参考答案:旧页码可能超出新结果集范围,接口返回空页,但总数非零。重置页码属于交互语义,不是数据库丢了记录。即使正确重置,其他用户并发改变结果集时,仍可能需要处理越界页的体验。

练习 C:用旧 version 描述一次冲突#

找到集成测试中的 CONFLICT 断言,按“首次创建 → 两次修改同时携带初始版本 → 一次成功、另一次冲突 → 读取最终状态”复述场景。源码使用 Promise.allSettled 并发发起请求,不预设哪一个先成功。

动手练习查看参考答案

参考答案:先取得写入机会的请求把 v0 变成 v1,另一个仍携带 v0,因此被拒绝;测试断言恰好一个成功,另一个返回 CONFLICT。若只比较标题或 updatedAt,容易把显示精度、时钟与业务版本混淆;整数版本让编辑依据更明确。

6. 再做两个扩展思考#

如果要加“已取消”,是不是只加一个 Select 选项?

需要先确定它是终态还是可恢复状态,再协调数据库枚举与迁移、共享 schema、标签、合法路径、服务校验、UI 和测试;当前还要考虑操作说明、提醒与操作记录。需求是跨层合同,不是局部视觉变更。

如果创建请求超时,应该直接自动重发吗?

不一定。请求可能已经写入数据库,只是响应丢失。自动重发可能重复创建。可靠重试通常需要为一次业务意图分配稳定请求标识,并由服务端唯一约束与事务保证幂等;单纯禁用按钮做不到这一点。当前回复接口已有相关实践,可在第三阶段继续学习。

7. 第一阶段完成标准#

你不需要背会每个库的 API,但应能独立解释以下链路,并用页面、Network、数据库和测试各找一份证据:

text
前端收集输入
  → 接口运行时校验与会话 / 团队权限
  → 服务层决定允许的业务行为
  → Prisma 与 PostgreSQL 完成读取、约束和事务写入
  → 返回明确数据或错误
  → 前端刷新相关查询并反馈给用户

接着进入 第 07 章:登录认证,系统学习登录、成员角色和团队隔离。本阶段已保留并使用这些保护,但学习重心先放在工单业务与数据库之间的完整闭环。

把知识变成自己的理解

完成本章练习,再继续下一站。

TEAMDESK HANDBOOK · 从一张工单,走进全栈开发

搜索教程

输入关键词,搜索全部章节。

阅读设置

外观
正文字号

从一张工单出发,读懂数据如何流转。

设置与进度仅保存在当前浏览器。