TT Lab
开始
学习 学习路径 课程

生产级后端 API 综合项目

毕业项目:守住组织边界的幂等订单 API

在 TT Lab 中继续学习

目标

完成一个订单 API,并让它具备生产环境所需的全部责任:数据契约、事务幂等性、组织边界授权、可运行的 HTTP、三类可观测信号、安全的部署对象,以及可以交接给他人的文档,都在一个项目中连成整体。

八个步骤分别从不同维度评分:正确性、安全、问题解决、运维和文档。

关于时间与存储的警告——请先阅读

因此,本实验按一次坐下完成来设计。如果感觉时间不够,先浅层完成到第 8 步,通常比深入做到第 3 步后全部丢失更好。若必须中途离开,请把项目目录打包为 tar 并手动下载。

环境

PostgreSQL 16 已经运行。

postgresql://lab@127.0.0.1:5432/labdb

项目根目录是 /root/work/backend-capstone,评分器只查看这一路径。请先创建。

mkdir -p /root/work/backend-capstone
cd /root/work/backend-capstone
mkdir -p migrations tests docs deploy

需要创建的文件

app.py                          psycopg 로 붙고 --host/--port 로 뜬다
migrations/001_create_orders.sql  BEGIN/COMMIT 으로 감싼 순방향 마이그레이션
tests/test_integration.py       재시도와 다른 조직 반례를 행동으로 검증
deploy/deployment.yaml          JSON 으로도 읽히는 YAML
docs/ADR-001.md                 status/context/alternatives/decision/consequences
docs/incident-report.md         impact/timeline/root cause/detection/corrective actions
portfolio.json                  backend-capstone-v2 스키마

所有步骤之前都会执行的一项检查

评分器会在检查步骤之前扫描整个项目寻找 secret。只要发现一项,无论评分哪个步骤都会以相同提示失败,因此很容易误以为原因就在当前步骤而浪费时间。

비밀번호가 들어간 접속 문자열   postgresql://lab:1234@...
클러스터 내부 주소             *.svc.cluster.local
Bearer 토큰 원문               Authorization: Bearer eyJ...
password/api_key/secret 대입   password = "hunter2"
개인 키                        -----BEGIN PRIVATE KEY-----

连接信息应从环境变量读取,示例 token 使用 <token> 这样的 placeholder。同时也会检查大小限制:文件不超过 4,096 个、单文件不超过 1MB、总计不超过 8MB。.git 与 __pycache__ 不计入。

步骤

  1. 数据契约——orders 表包含 identity PK、amount_cents > 0 检查、owner_id·org_id、created_at timestamptz、unique(org_id, idempotency_key),以及 (org_id, owner_id) 查询索引。整体用 BEGIN;/COMMIT; 包裹。
  2. 事务幂等性——使用 ON CONFLICT (org_id, idempotency_key) 吸收重试,并在异常时 rollback。值必须通过参数绑定。
  3. 授权——authenticate 创建 identity,authorize(identity, order) 先检查组织,再检查 owner 或 admin。仅 import 此文件时不得输出任何内容。
  4. HTTP——必须能通过 python3 app.py --host 127.0.0.1 --port <포트> 启动。实现 POST /orders、GET /orders/{id}、POST /orders/{id}/cancel、/healthz,且所有响应都包含 X-Trace-Id。状态码应区分 201/200/400/401/403。
  5. 可观测性——orders_created_total、http_request_duration_seconds,以及包含 event·route·status·duration_ms·trace_id 的 JSON 日志。不得记录 authorization header 和内部 DNS。
  6. 部署——在 deploy/deployment.yaml 中配置 non-root、seccompProfile: RuntimeDefault、allowPrivilegeEscalation: false、只读 root、requests/limits、两个 probe、secretKeyRef,以及固定 digest 的 image。
  7. 文档——ADR 与事故报告。事故报告中的每项措施都要写明负责人和截止日期。
  8. 作品集——portfolio.json 中只放允许的五项摘要,不得包含原始 header、token、私有 IP、内部 DNS、terminal 记录。

参考

第 3 步的“import 时保持安静”是经常失败的地方。如果模块顶层连接数据库或执行 print,评分器会看到输出。请把连接动作移入 main(),诊断信息则交给 logger。

执行 PostgreSQL 16 schema

数据契约——orders 表包含 identity PK、amount_cents > 0 检查、 owner_id·org_id、created_at timestamptz、unique(org_id, idempotency_key), 以及 (org_id, owner_id) 查询索引。整体用 BEGIN;/COMMIT; 包裹。

在 migrations/001_create_orders.sql 中创建 identity PK、正数金额、owner/org、组织范围幂等 UNIQUE、TIMESTAMPTZ 和查询索引,并用事务包裹。app.py 必须使用 psycopg。

实现事务幂等创建

事务幂等性——使用 ON CONFLICT (org_id, idempotency_key) 吸收重试, 并在异常时 rollback。值必须通过参数绑定。

使用 psycopg transaction/cursor、参数绑定和 ON CONFLICT (org_id, idempotency_key),异常时 rollback。为重试和不同组织的反例编写行为测试。

分离认证与 owner、组织授权

授权——authenticate 创建 identity,authorize(identity, order) 先检查组织,再检查 owner 或 admin。仅 import 此文件时 不得输出任何内容。

authenticate 创建 Bearer identity,authorize(identity, order) 先判断组织,再判断 owner/admin 关系。import 和错误输出中都不得留下 token。

开放真实 HTTP API 契约

HTTP——必须能通过 python3 app.py --host 127.0.0.1 --port <포트> 启动。 实现 POST /orders、GET /orders/{id}、POST /orders/{id}/cancel、/healthz, 且所有响应都包含 X-Trace-Id。状态码应区分 201/200/400/401/403。

使用标准库或已安装 runtime 支持 app.py --host ... --port ...。实现 POST /orders、GET /orders/id、POST /orders/id/cancel、health/readiness,以及状态、JSON、X-Trace-Id 契约。

把三类可观测信号关联到请求

可观测性——orders_created_total、http_request_duration_seconds,以及 包含 event·route·status·duration_ms·trace_id 的 JSON 日志。 不得记录 authorization header 和内部 DNS。

将 orders_created_total、http_request_duration_seconds、JSON 日志中的 event/route/status/duration_ms 与响应 trace ID 关联。不得记录 Authorization 和内部 DNS。

创建可解析且安全的部署对象

部署——在 deploy/deployment.yaml 中配置 non-root、seccompProfile: RuntimeDefault、 allowPrivilegeEscalation: false、只读 root、requests/limits、 两个 probe、secretKeyRef,以及固定 digest 的 image。

将 deploy/deployment.yaml 编写为 JSON-compatible YAML。把 non-root/seccomp/禁止提权/只读 root、资源、两个 probe、Secret 引用和 digest image 放到正确对象路径。

连接 ADR 与事故学习

文档——ADR 与事故报告。事故报告中的每项措施都要写明负责人和截止日期。

ADR 中写入 status/context/alternatives/decision/consequences;事故报告中写入 impact/timeline/root cause/detection/corrective actions 及负责人、截止日期,并移除 secret 与内部地址。

发布 allowlist 作品集

作品集——portfolio.json 中只放允许的五项摘要。 不得包含原始 header、token、私有 IP、内部 DNS、terminal 记录。

将 portfolio.json 固定为 backend-capstone-v2,只加入 test_summary、metric_names、deployment_summary、adr_summary、incident_summary。禁止原始 header、token、私有 IP、内部 DNS 和 terminal log。