毕业项目:守住组织边界的幂等订单 API
目标
完成一个订单 API,并让它具备生产环境所需的全部责任:数据契约、事务幂等性、组织边界授权、可运行的 HTTP、三类可观测信号、安全的部署对象,以及可以交接给他人的文档,都在一个项目中连成整体。
八个步骤分别从不同维度评分:正确性、安全、问题解决、运维和文档。
关于时间与存储的警告——请先阅读
- session 从 60 分钟开始,可通过
+시간延长,但总计最多 180 分钟。 - 实验 Pod 没有 volume。session 结束后,整个
/root/work都会消失,下次 session 会从空 Pod 开始。
因此,本实验按一次坐下完成来设计。如果感觉时间不够,先浅层完成到第 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__ 不计入。
步骤
- 数据契约——
orders表包含 identity PK、amount_cents > 0检查、owner_id·org_id、created_at timestamptz、unique(org_id, idempotency_key),以及(org_id, owner_id)查询索引。整体用BEGIN;/COMMIT;包裹。 - 事务幂等性——使用
ON CONFLICT (org_id, idempotency_key)吸收重试,并在异常时 rollback。值必须通过参数绑定。 - 授权——
authenticate创建 identity,authorize(identity, order)先检查组织,再检查 owner 或 admin。仅 import 此文件时不得输出任何内容。 - 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。 - 可观测性——
orders_created_total、http_request_duration_seconds,以及包含event·route·status·duration_ms·trace_id的 JSON 日志。不得记录authorizationheader 和内部 DNS。 - 部署——在
deploy/deployment.yaml中配置 non-root、seccompProfile: RuntimeDefault、allowPrivilegeEscalation: false、只读 root、requests/limits、两个 probe、secretKeyRef,以及固定 digest 的 image。 - 文档——ADR 与事故报告。事故报告中的每项措施都要写明负责人和截止日期。
- 作品集——
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。