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

31 · 部署验收:从 HTTP 请求追到 Worker,再回到下载#

小周看到镜像启动、Web 200,就准备宣布发布完成。小陈还想确认:注册后的 Cookie 是否带进业务请求?容器 Worker 是否真的消费了导出?下载拿到的是这次验收创建的工单,还是碰巧某个成功响应?

本章沿 deployment-smoke.mjs 的真实请求逐行验证,再补它没覆盖的页面与故障故事。验收结论必须写明环境、版本、执行证据和未执行部分;教程内容完成不表示远端 CI、生产发布或故障演练已经完成。

1. 先把四类证据放到各自位置#

证据 可以回答 不能替代
静态检查与生产构建 代码能否通过规则、类型与打包 运行时数据库、队列与权限
数据库 / Redis 测试 指定业务规则与调度场景成立吗 真实生产镜像入口与 HTTP
镜像 smoke HTTP、会话、数据库与独立 Worker 能接棒吗 浏览器控件与所有角色流程
页面及故障验收 用户操作和故障恢复是否符合预期 未执行的容量、备份与外部服务保障

先按第 28 章跑代码检查,按第 27 章跑显式连接的集成测试。看到 skipped 要写“未执行”,不要把命令退出 0 的截图当成全部通过。教程阅读站另跑 tutorials:test,和业务部署是不同产物。

2. 给 smoke 一个可确认的数据写入目标#

沿用第 29 章的隔离项目 teamdesk-stage6 和 .env.stage6,确认这是你自己的学习环境,URL 与 BETTER_AUTH_URL、WEB_PORT 一致。若它们改过端口,下面 URL 同步修改。

bash
docker compose -p teamdesk-stage6 --env-file .env.stage6 -f compose.deploy.yaml ps -a
curl --fail http://localhost:3100/api/health
docker compose -p teamdesk-stage6 --env-file .env.stage6 -f compose.deploy.yaml exec -T worker ./node_modules/.bin/tsx scripts/worker-health.ts

核对 migrate 成功、Web / Worker 状态及探针,再运行:

bash
SMOKE_ALLOW_CREATE=1 SMOKE_BASE_URL=http://localhost:3100 node scripts/deployment-smoke.mjs

脚本 必须同时有这两个变量,否则入口抛错。SMOKE_ALLOW_CREATE 是明确的数据创建开关,不是检测目标是否生产的自动保护;设置为 1 并不会证明 URL 安全。应由操作者确认目标,不指向已有真实用户的实例。

脚本生成随机邮箱和密码,创建账号、团队、工单和任务,结束不自动删这些数据,失败后也可能留下已完成的前半段。不要反复运行而不记环境和账号;CI 用一次性部署整体回收,不代表本机要清掉现有数据卷。

3. request 帮助函数怎样把会话传下去#

先读 request(path, body),不要从最后一句“验收通过”反推覆盖范围:

js
// 源码节选:每次请求与 Cookie 接棒
headers: {
  'Content-Type': 'application/json',
  Origin: base,
  Cookie: cookie,
},

有 body 就 POST、否则 GET;每个 fetch 配 10000ms 超时。响应有 Set-Cookie 时,提取各项分号前的 name=value 拼成后续 Cookie;再断言 response.ok。

这模拟了当前验收流程所需的会话接棒,不是浏览器完整 Cookie jar:它没有实现通用域、路径、过期管理,也不验证浏览器 SameSite、Secure 或跨站行为。不能用 Node fetch 成功证明所有浏览器认证属性已验收。

Origin=base 用于与认证目标一致;配置错地址时先检查站点 URL,不应删掉服务端 Origin 检查来让脚本通过。

4. 按请求顺序读每一条断言#

顺序 请求 / 操作 直接检查的证据
1 GET /api/health HTTP 成功,基础数据库检查可用
2 POST /api/auth/sign-up/email 注册成功,Cookie 非空
3 RPC team.create 新团队 ID 可用于后续业务
4 RPC ticket.create 新团队建一张普通工单
5 RPC task.createExport 保存导出任务,得到 ID
6 GET task.list,按 ID 查找 没有 FAILED,最终见 COMPLETED
7 GET /api/exports/任务ID 请求成功,文本包含验收标题

rpc 包装返回 tRPC JSON 的 result.data;列表查询将 { teamId, page:1, pageSize:10 } JSON 编码进 input。它们经过真实 HTTP 与会话,和 createCaller 直接注入 userId 的测试不同。

注意第二步是注册后建立会话,没有另一次邮箱密码登录调用。脚本末尾提示写“登录”,但解释源码时要说清它并未覆盖退出后重新登录、密码恢复或邮箱验证。

创建普通工单也会产生默认延迟提醒,但脚本不等它到期,不断言通知。管理员导出自己的新团队数据,不能证明同筛选不同客户、跨团队或跨所有者下载的负向边界;这些仍要前几章对应的测试与页面验收。

5. 轮询的“30 秒”不是总时长硬上限#

js
// 源码节选:循环的关键判断
const current = result.items.find((item) => item.id === task.id);
assert(current?.status !== 'FAILED', '独立 Worker 执行失败');
if (current?.status === 'COMPLETED') {
  completed = true;
  break;
}

循环最多查 30 次,每次未完成等 1 秒;每个请求还会消耗自身网络时间,另有单次 10 秒超时。虽然最终断言消息说“30 秒内完成”,代码没有一个覆盖整个流程的 30 秒 deadline,实际墙钟时长可能更长。

列表没找到目标时 current 为 undefined,这一轮不会立即失败,而是继续等。直到没有见到 COMPLETED 才触发最终断言。排错时要区分“读到了 WAITING”和“根本没在列表找到那个 ID”,不要凭同一句超时提示推断原因。

下载只断言文本包含“容器异步导出验收”,没有解析全文件、核对全部行数或断言 Content-Disposition 等响应头。它是一条有针对性的连通性证据,不是 CSV 全部规则的测试替代。

6. 根据失败阶段追踪源码与数据#

失败阶段 优先阅读 / 核对 需要得到的事实
health 请求失败 健康路由、Web / 数据库 状态码、连接与迁移是否准备好
注册或 Cookie 断言失败 认证入口、站点 URL 目标实例、Origin、会话响应
team / ticket RPC 失败 router、成员与工单服务 会话是否带到请求、输入是否合规
任务一直等待 dispatcher、Worker 原 taskId/run、投递与消费在哪停下
task FAILED processor、lastError 明确业务拒绝还是临时失败耗尽
下载失败或缺标题 下载服务、实际文件 所有者、资格、状态、期限、筛选

脚本的请求错误提示含路径和状态码,并不输出完整响应体、密码或 Cookie。诊断也应保持这个边界。独立任务失败时用 taskId/run 关联日志与账本;不要用再提交一份成功导出来掩盖原任务未恢复。

当前脚本没有打印创建的 taskId。为了事后能找到它,先记本轮开始时间、一次只运行一轮;在这套隔离部署中可做以下只读查询,再按时间和 teamId 与日志对应:

bash
docker compose -p teamdesk-stage6 --env-file .env.stage6 -f compose.deploy.yaml exec -T postgres psql -U teamdesk -d teamdesk <<'SQL'
SELECT task.id, task."teamId", task.status, task.run,
       task.attempts, task."createdAt", task."lastError"
FROM "BackgroundTask" AS task
JOIN "Team" AS team ON team.id = task."teamId"
WHERE team.name = '一次性部署验收团队' AND task.kind = 'EXPORT'
ORDER BY task."createdAt" DESC
LIMIT 10;
SQL

这个查询不读取密钥、CSV 或工单正文。多轮 smoke 会有多个同名团队,不能只凭团队名称认定是同一轮;没有候选记录时,可能失败还没走到创建导出的步骤。这里也没有在宿主机直接连接部署数据库的 5433 端口。

7. 再用浏览器补一条多角色故事#

在同一学习部署准备管理员 A、客服 C、客户 D / E,以及另一团队账号。用分开的浏览器会话确认当前身份,再按顺序走:

  1. A 建团队,添加已注册成员并设置角色。记录 teamId 与各角色;检查客户界面不显示工作人员操作。
  2. D 创建“第六阶段登录问题”,C 分配给自己并回复,D 查看回复。E 读取 D 的详情和历史应被拒绝,另一团队同样不能访问。
  3. C 按 OPEN → IN_PROGRESS → RESOLVED 操作,解决时填写说明;再重开为 OPEN 并说明,最后关闭。历史应能对应真实操作者与动作,关闭后没有后续状态路径。
  4. 另用一张未关闭的学习工单,在回复输入中保留草稿后离开再回来,核对账号 / 团队 / 工单隔离;改变筛选、复制 URL,验证恢复条件与浏览器前进后退。
  5. C 按唯一标题筛选导出,确认任务页与下载;D 导出本人范围,另一账号访问同一文件链接应被拒绝。另用新未解决工单做一分钟提醒,不在已关闭工单上设置。

记录 Network 请求、页面提示、账本与历史中的实际值,不要求不同机器截图中的时间和版本数字完全一样;Worker 或额外修改也可能推进版本。真正要解释的是每次有效变化与权限边界。

这份清单是手动验收步骤,不是已有 Playwright 自动测试。smoke 没有浏览器 UI、Zustand 或 nuqs 操作,不能用它的一句 passed 替代这五步。

8. 故障恢复用什么证据验收#

故障实验 恢复后的核对 已有证据及边界
停止自己部署的 Worker,再提交导出 原任务 ID 从等待变完成,文件正确 可按第 30 章操作;本章 smoke 单次成功不覆盖中断
Redis 投递失败 待办保留,恢复后可继续投递执行 第 23 章已有失败桩;不是完整真实断线恢复
旧 run / 旧执行者回来 新轮与租约不被旧结果覆盖 第 25~27 章有并发关卡与真实 Worker stalled / 迟到失败回调集成;完整容器中断仍需验收
事件写入失败 工单、回复、成员联动共同回滚 第 21 章真实数据库触发器用例;不在 HTTP smoke 内
成员降权或文件过期 执行 / 下载不延用旧资格 第 25、27 章有读取中撤权、最终授权锁与下载拒绝断言;已下载副本无法撤回
SIGTERM 排空 / 退出超时 心跳撤销、在途租约与重启最终结果一致 第 30 章真实子进程用例;完整容器信号转发与默认 75 秒期限仍需验收
迁移失败或代码回退 不让不兼容业务恢复,确认结构与版本 当前没有自动回滚或备份恢复演练脚本

故障实验优先用受控测试和一次性环境,不删除正在使用的 Redis 队列、不对真实数据清卷。没有执行的窗口写成“待验证”,而不是“理论可恢复,所以验收通过”。

9. 写一份可以复核的交付记录#

建议每次记录:提交 SHA、镜像 / 部署目标、日期与端口、迁移结果、检查命令和退出结果、测试通过与跳过数、smoke 是否创建数据、页面步骤结果、故障窗口及未执行项。日志与截图只留必要定位字段,不包含真实密钥和业务正文。

分开写“教程构建通过”“日志单元测试通过”“镜像 smoke 通过”“远端 CI 通过”,不要压成一个含糊的“全部通过”。Docker 未启动、缺测试连接或没有远端运行时,保留对应的未执行说明。

拓展阅读从可运行到可上线还差什么?

当前仓库有检查、迁移、独立进程、探针、日志和受限导出,但尚无自动远程发布、数据库恢复演练、容量与告警保障。域名 / HTTPS、生产备份与恢复、公开账号恢复等需要具体方案和验证;S3 与 Sentry 的后续范围见第 32 章。不能把“具备本地验收手段”直接写成“生产运行已保障”。

10. 练习与答案#

练习 A:脚本通过,能证明退出后重新登录正常吗#

动手练习查看参考答案

参考答案:不能。当前调用 sign-up/email 后沿注册会话继续请求,没有登出与 sign-in/email 流程。要单独设计已有账号登录、退出和 Cookie 生命周期的验证,浏览器属性还需真实浏览器检查。

练习 B:如何补强下载验收而不堆重复断言#

动手练习查看参考答案

参考答案:按目标风险补证据:核对附件响应头和有效 CSV 解析结果;让其他客户 / 团队也有匹配筛选的数据;用另一个真实会话请求同一任务文件并检查拒绝。分别验证文件结构、内容范围与所有权,不只再检查一次包含标题。

练习 C:关闭 Worker 后运行 smoke 失败,是否说明重启恢复也失败#

动手练习查看参考答案

参考答案:不能。它只说明这次等待未见完成或遇到错误。恢复实验需要保留原 taskId,重启后查同一任务、结果与日志;重新运行 smoke 会创建新账号、新团队和新任务,不能替原任务的恢复作证。

第六阶段链路:独立检查环境验证代码 → 镜像生成与迁移 → Web 会话和输入授权 → 数据库存待办 → Redis / Worker 执行 → 结果落库 → HTTP 鉴权下载 → 页面与故障补证据 → 据实记录可用范围。

把知识变成自己的理解

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

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

搜索教程

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

阅读设置

外观
正文字号

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

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