DIP1 技术基础设施设计¶
文档编号:DOC-D01-D3 / 简称:DIP1-P3 版本:V1.1 创建日期:2026-08-09 最近更新:2026-08-09 维护人:TL / DT 关联文档:PJM、DIP1-ARC 架构设计 V2.0、DIP1-P1、DIP1-P2 V2.0
V1.1 变更(对齐 DIP1 原生 App 方案 ADR-002/006/007/008):① §1.1 工具链新增 Expo CLI / EAS CLI / watchman / Xcode / Android Studio;② §1.2 Windows 初始化脚本增加 Expo/EAS 安装与登录步骤;③ §1.3
.env.example新增 EXPO/EAS/推送/移动端认证变量组,飞书变量标注⏸ 暂不实施;④ §三 CI/CD 新增 EAS Build/Submit/Update Job + 移动端流水线子图;⑤ §四 测试策略新增 Detox RN E2E + Jest RN 单测;⑥ §五 部署架构新增 App Store Connect / Google Play Console 发布通道 + Expo Updates OTA 热更新 + 推送服务(APNs+FCM);⑦ §六 可观测新增 Sentry RN SDK + 移动端 APM(启动时间/崩溃率/屏幕 FPS)+ 推送送达率指标;⑧ ADR 表新增 ADR-011 Expo managed workflow、ADR-012 NativeWind、ADR-013 EAS Build 云端构建。
一、开发环境配置¶
1.1 工具链版本¶
| 工具 | 版本 | 安装方式 | 说明 |
|---|---|---|---|
| Python | 3.12.x | pyenv / 官网 | 最低 3.11,推荐 3.12(性能 + 类型提示优化) |
| Node.js | 20 LTS | fnm | 与 Next.js 14 + Expo SDK 51 兼容 |
| pnpm | 9.x | corepack enable | 前端 monorepo 包管理器(比 npm/yarn 快) |
| PostgreSQL | 16.x | Docker | 官方镜像,JSONB 性能最佳 |
| Redis | 7.2 | Docker | 缓存 + 队列 + 会话 |
| Docker Desktop | 4.x | 官网 | Windows/macOS 本地开发容器 |
| Direnv | 2.x | winget / brew | 自动加载 .envrc 环境变量 |
| 【V1.1 新增】Expo CLI | 0.18.x | npm i -g expo-cli |
React Native + Expo managed workflow 开发工具 |
| 【V1.1 新增】EAS CLI | 13.x | npm i -g eas-cli |
Expo Application Services 云端构建/提审/OTA |
| 【V1.1 新增】watchman | 4.9.x | brew / 官网 | RN 文件监听(macOS/Linux 必需,Windows 可选) |
| 【V1.1 新增】Xcode | 15.4+ | Mac App Store | iOS 构建(仅 macOS;CI 走 EAS Build 云端,本地非必需) |
| 【V1.1 新增】Android Studio | Hedgehog+ | 官网 | Android 构建(仅模拟器/真机调试用;CI 走 EAS Build 云端) |
| 【V1.1 新增】Apple Developer 账号 | — | TL 提供 | App Store 上架(年费 $99;TL 持有,Code Agent 用 EAS Submit 调用凭据) |
| 【V1.1 新增】Google Play Console | — | TL 提供 | Google Play 上架(一次费 $25;TL 持有 Service Account JSON) |
1.2 Windows 本机开发一键初始化¶
文件:dip1/scripts/dev/setup-dev.ps1(PowerShell)
<#
.SYNOPSIS DIP1 开发环境初始化(Windows,管理员权限运行一次)
#>
# 1. 安装工具链(winget 前提)
winget install -e --id Python.Python.3.12
winget install -e --id CoreyButler.NVMforWindows
winget install -e --id Docker.DockerDesktop
winget install -e --id direnv.direnv
# 2. 启用 pnpm corepack
corepack enable
corepack prepare pnpm@9.15.0 --activate
# 3. Python venv 创建
cd dip1/backend
py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt -r requirements-dev.txt
# 4. 前端依赖安装(V1.1:含 wkr-app + cst-app RN 包)
cd ../frontend
pnpm install
# 5. 【V1.1 新增】Expo/EAS CLI 全局安装 + 账户登录
npm install -g expo-cli eas-cli
expo login # TL 提供 DIP1 Expo 账户凭据
# 6. 【V1.1 新增】配置 EAS 项目(双端各一份 eas.json)
cd apps/wkr-app && eas build:configure && cd ../..
cd apps/cst-app && eas build:configure && cd ../..
# 7. 预提交钩子
cd ../..
pre-commit install
Write-Host "✅ DIP1 开发环境初始化完成,请重启终端后运行:docker compose up -d"
Write-Host "✅ RN 开发:cd frontend/apps/wkr-app && pnpm start(Expo DevTools)"
1.3 环境变量模板(.env.example)¶
# ====== 基础 ======
ENV=dev # dev / staging / prod
TZ=Asia/Hong_Kong
APP_SECRET_KEY=change-me-to-32bytes-random
# ====== PostgreSQL ======
DATABASE_URL=postgresql+asyncpg://dip1:dip1pass@localhost:5432/dip1_dev
# ====== Redis ======
REDIS_URL=redis://localhost:6379/0
# ====== Cloudflare R2(对象存储,S3 兼容)======
R2_ACCOUNT_ID=xxxxxxxxxxxxxxxx
R2_ACCESS_KEY_ID=xxxxxxxxxxxxxxxx
R2_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxx
R2_BUCKET=dip1-assets-dev
R2_PUBLIC_URL=https://assets-dev.weavely.hk
# ====== ⏸ 飞书开放平台(V1.1:阶段 1 暂不实施,保留占位)======
# FEISHU_APP_ID=cli_xxxxxxxxxxxx
# FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# FEISHU_BITABLE_APP_TOKEN=bascnXXXXXXXXXXXX
# FEISHU_WEBHOOK_VERIFY_TOKEN=xxxxxxxxxxxx
# FEISHU_WEBHOOK_ENCRYPT_KEY=xxxxxxxxxxxxxxxx
# ====== 【V1.1 新增】Expo / EAS(TL 提供 DIP1 Expo 账户)======
EXPO_USERNAME=weavely-dip1
EXPO_PROJECT_ID_WKR=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
EXPO_PROJECT_ID_CST=yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
EAS_PROJECT_ACCOUNT=weavely-dip1
# ====== 【V1.1 新增】推送服务(APNs + FCM via Expo Notifications)======
EXPO_PUSH_AUTH_TOKEN=ExponentPushToken_dev_only_xxxxxxxxxxxxx
# 生产环境由 EAS 自动注入 Expo push 凭据;开发环境用 ExpoPushToken[xxx] 格式即可测试
# ====== 【V1.1 新增】移动端认证 ======
JWT_ACCESS_TTL_SECONDS=900 # access token 15 分钟
JWT_REFRESH_TTL_SECONDS=2592000 # refresh token 30 天
OTP_TTL_SECONDS=300 # WhatsApp OTP 5 分钟
OTP_RATE_LIMIT_PER_HOUR=5 # OTP 限流 5 次/小时
BIOMETRIC_REQUIRED=false # 是否强制生物识别(生产可设 true)
# ====== AI 听写助手(OPS-D 用)======
DT_API_BASE=https://api.trae.ai
DT_API_KEY=sk-xxxxxxxxxxxx
DT_MODEL=trae-gpt
# ====== 可观测 ======
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # OpenTelemetry
SENTRY_DSN=https://xxx@sentry.io/xxx # 后端 + RN 双端共用
SENTRY_DSN_WKR=https://xxx@sentry.io/xxx # 【V1.1】wkr-app RN 专用(可同 SENTRY_DSN)
SENTRY_DSN_CST=https://xxx@sentry.io/xxx # 【V1.1】cst-app RN 专用(可同 SENTRY_DSN)
LARK_WEBHOOK_ALERT=https://open.feishu.cn/open-apis/bot/v2/hook/xxx # 飞书告警群机器人
1.4 本地一键启动(docker-compose.yml)¶
文件:dip1/docker-compose.yml
services:
postgres:
image: postgres:16-alpine
ports: ["5432:5432"]
environment:
POSTGRES_USER: dip1
POSTGRES_PASSWORD: dip1pass
POSTGRES_DB: dip1_dev
volumes:
- pgdata:/var/lib/postgresql/data
- ./scripts/db/init-dev.sql:/docker-entrypoint-initdb.d/10-init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dip1"]
interval: 5s
redis:
image: redis:7.2-alpine
ports: ["6379:6379"]
command: redis-server --save 60 1 --loglevel warning
# 邮件/短信本地捕获(dev 用,避免发真消息)
mailpit:
image: axllent/mailpit:latest
ports: ["1025:1025", "8025:8025"] # SMTP / Web UI
# 本地 OpenTelemetry + Jaeger(trace 调试)
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "4317:4317" # OTLP gRPC
- "16686:16686" # Jaeger UI
volumes:
pgdata:
二、Git 分支策略(对齐 PJM §4.3 Git Flow Lite)¶
2.1 分支模型¶
与主项目(hk2026)策略一致,但 dip1/ 子目录采用更细的粒度:
gitGraph
commit id: "init" tag: "v0.0.0"
branch dev
commit id: "dev-start"
branch feat/qsv-engine
commit id: "qsv-1"
commit id: "qsv-2"
checkout dev
merge feat/qsv-engine id: "merge-qsv"
branch feat/cpt-lifecycle
commit id: "cpt-1"
commit id: "cpt-2"
checkout dev
merge feat/cpt-lifecycle id: "merge-cpt"
branch release/v0.1.0
commit id: "rc1" tag: "v0.1.0-rc1"
commit id: "fix-rc1-bug"
checkout main
merge release/v0.1.0 id: "release-0.1" tag: "v0.1.0"
branch hotfix/auth-security
commit id: "security-patch"
checkout main
merge hotfix/auth-security id: "merge-hotfix" tag: "v0.1.1"
checkout dev
merge hotfix/auth-security id: "backport-hotfix"
| 分支 | 命名 | 生命周期 | 保护规则 |
|---|---|---|---|
main |
固定 | 永久 | ✅ 保护:仅 PR squash merge,CI 全绿 |
dev |
固定 | 永久 | ✅ 保护:仅 PR merge,CI 全绿 |
feat/* |
feat/jira-id-简述 或 feat/领域-功能 |
开发期 < 2 周,合并即删 | ❌ 无保护,可强制推送 |
fix/* |
fix/缺陷编号-简述 |
同上 | ❌ 同上 |
release/vX.Y.Z |
语义化版本 | RC → 发布期间 | ✅ 发布期保护,仅修 bug |
hotfix/* |
hotfix/vX.Y.Z+1-简述 |
紧急修复,合并双端(main+dev) | ❌ 无保护 |
2.2 提交规范(Conventional Commits)¶
<type>(<scope>): <subject>
<body>
<footer>
type:feat 新功能 / fix 修复 / docs 文档 / refactor 重构 / perf 性能 / test 测试 / chore 构建工具 / migrate 数据库迁移
scope(可选):qsv / cpt / mds / ops / auth / ops-admin / wkr-app / infra
示例:
feat(qsv): 新增 QMD 8 大类参数自动计算
- 实现 base_fee × 8 因子 = subtotal 的公式(对齐 QMD V2.1 §3)
- 新增封顶价保护:final_amount = MIN(subtotal - discount, cap_price)
- 新增 QuoteParamProvider 端口对接 MDS SCL/BPL
Closes: DIP1-42
2.3 PR 准入 Checklist(合并 gate)¶
## ✅ PR Checklist
- [ ] 已通过 `pre-commit run --all-files`(ruff/mypy/black)
- [ ] 单测覆盖率 ≥ 新增代码的 80%(`pytest --cov-report=term-missing`)
- [ ] API 变更已同步更新 OpenAPI spec 并上传 Swagger UI 预览
- [ ] DB 迁移已生成 Alembic 脚本,且 upgrade/downgrade 双向验证通过
- [ ] 文档 README / ADR 已更新(涉及架构决策时)
- [ ] 权限矩阵已更新(涉及 auth/role 变更时)
三、CI/CD 流水线¶
3.1 流水线架构(V1.1:后端 Web + 移动端 RN 三轨)¶
采用 GitHub Actions(主)+ 自建 Runner(香港低延迟)+ EAS Build(云端 RN 构建) 三模式:
flowchart LR
subgraph Local["开发者本地"]
PRE["pre-commit<br/>ruff/mypy/black/eslint"]
end
subgraph GHA["GitHub Actions CI(后端 + Web)"]
LINT["Lint Check<br/>ruff + eslint"]
TEST_BE["后端测试<br/>pytest (Testcontainers)"]
TEST_FE["Web 测试<br/>vitest + playwright"]
MIG["迁移校验<br/>alembic up+down"]
BUILD_BE["后端镜像<br/>docker build"]
BUILD_FE["Web 产物<br/>next build"]
SCAN["安全扫描<br/>trivy + npm audit"]
end
subgraph EAS["【V1.1 新增】EAS Build(移动端 RN 云端构建)"]
RN_TEST["RN 单测<br/>Jest wkr-app + cst-app"]
RN_DETOX["RN E2E<br/>Detox 双端主流程"]
EAS_BUILD["EAS Build<br/>iOS + Android × 双端 = 4 artifact"]
EAS_SUBMIT["EAS Submit<br/>App Store + Google Play 提审"]
EAS_UPDATE["EAS Update<br/>OTA JS Bundle 热更新"]
end
subgraph Staging["Staging 香港 VPS"]
DEPLOY_S["Fly.io / 自建<br/>docker compose up"]
SMOKE["冒烟测试<br/>50+ E2E API"]
end
subgraph Prod["生产环境"]
DEPLOY_P["蓝绿部署<br/>10%/50%/100% 灰度"]
APP_STORE["App Store + Google Play<br/>上架发布"]
end
Local -->|"push feat/*"| LINT
LINT --> TEST_BE & TEST_FE & MIG & RN_TEST
TEST_BE --> BUILD_BE
TEST_FE --> BUILD_FE
RN_TEST --> RN_DETOX
BUILD_BE --> SCAN
BUILD_FE --> SCAN
SCAN -->|"merge dev"| DEPLOY_S
DEPLOY_S --> SMOKE
SMOKE -->|"tag release"| DEPLOY_P
SMOKE -->|"tag release"| EAS_BUILD
RN_DETOX --> EAS_BUILD
EAS_BUILD --> EAS_SUBMIT
EAS_SUBMIT --> APP_STORE
EAS_BUILD -.->|"JS 变更 only"| EAS_UPDATE
EAS_UPDATE -.->|"OTA 推送"| APP_STORE
3.2 CI 工作流文件(dip1/.github/workflows/ci.yml + dip1/.github/workflows/eas.yml)¶
核心步骤清单(完整 YAML 见实际文件):
| Job | 触发 | 耗时 | 关键动作 |
|---|---|---|---|
lint |
每次 push PR | 1min | ruff check backend/;eslint frontend/;hadolint Dockerfile |
test-backend |
每次 push PR | 8min | Testcontainers 起 PG+Redis → pytest → 上传 coverage 到 Codecov |
test-frontend-web |
每次 push PR | 10min | pnpm install → vitest(opr-admin)→ Playwright 10 条 E2E 冒烟 |
【V1.1】test-rn |
每次 push PR | 6min | pnpm install → Jest wkr-app + cst-app 单测 → 覆盖率 ≥ 70% |
【V1.1】e2e-detox |
修改 frontend/apps/* RN 代码时 | 12min | Detox iOS 模拟器跑 wkr-app + cst-app 各 1 条 L1→L8 主流程 |
db-migrate-check |
修改 alembic/versions 时 | 3min | 创建临时空库 → upgrade head(含 0001+0002)→ downgrade base → 二次 upgrade 无错 |
security-scan |
main/dev push + 每日 | 5min | Trivy 镜像漏洞 + pip-audit + pnpm audit → 飞书告警高危 |
build-and-push |
tag v* + main push | 12min | docker buildx 多架构 → 推送到阿里云香港 ACR |
【V1.1】eas-build |
tag v* + main push | 25min | eas build --platform ios --profile production × 2(wkr+cst)+ --platform android × 2 = 4 artifact(云端并行约 25 分钟) |
【V1.1】eas-submit |
eas-build 成功后 | 10min | eas submit -p ios(App Store Connect API Key)+ eas submit -p android(Google Play Service Account JSON)→ 自动提审 |
【V1.1】eas-update |
dev push + 仅 JS 变更时 | 3min | eas update --branch production 推送 JS Bundle → App 自动检测 OTA 更新 |
deploy-staging |
dev merge + schedule(每日 09:00) | 5min | SSH 到 staging 机 → docker compose pull && up -d |
smoke-staging |
staging deploy 后 | 3min | newman 跑 Postman 50 条 API 冒烟集 + Lighthouse 分数 |
OTA 热更新策略(V1.1):仅 JS Bundle 变更走 OTA(无需重新提审);原生模块变更(如新增/升级 expo-camera 版本、修改 app.json 中 ios/android 配置)必须 EAS Build 重新构建并提审。GitHub Actions 中通过
git diff检测是否触及ios/、android/、app.json、原生模块依赖,自动选择 OTA 或 Build 通道。
3.3 版本号与发布节奏¶
- 语义化版本:
vMAJOR.MINOR.PATCH(v2.0.0 / v2.0.1 ... v2.1.0 含原生模块升级) - 发布节奏:
dev:每次 PR 合并 → 自动部署 staging;JS 变更 → EAS Update OTA 推送 staging 通道release/*:每 2 周一个 release candidate,UAT 3 天;EAS Build 生产 profile 构建双端main:每月一个生产版本,第二周周二生产发布;App Store + Google Play 同步上架- Changelog:
git-cliff基于 Conventional Commits 自动生成 CHANGELOG.md - 【V1.1】App 审核节奏:App Store 审核 24-48h;Google Play 几小时~1 天;提审被拒记录到 DWLG(CROSS 类型 Issue 给 TL)
四、测试策略¶
4.1 测试金字塔(V1.1:三端分离)¶
▲
/ \
/E2E\ 顶层:
/ \ · Web:10~20 条 Playwright(opr-admin 11 屏基线,5 分钟)
/-------\ · 【V1.1】RN:Detox wkr-app + cst-app 各 1 条 L1→L8 主流程(10 分钟/端)
/ \
/Integration\ 中层:
/-------------\ · 50~100 条 API 集成测试(Testcontainers,8 分钟)
/ \ · 【V1.1】6 域 × ≥15 条 + 144 RBAC 越权 = ≥240 条
/ Unit \ 底层:
/-------------------\ · 后端:300+ 条领域/应用层单元测试(< 2 分钟)
/ \ · 【V1.1】RN:Jest wkr-app + cst-app hooks/组件单测(< 1 分钟)
/-----------------------\
4.2 各层测试要求¶
| 测试层 | 框架 | 覆盖率目标 | 示例 |
|---|---|---|---|
| 单元测试(Domain + Application) | pytest + hypothesis | 80% | test_quote_recalculate_cap_price()、test_order_transition_l4_ok() |
| 单元测试(纯函数) | parametrize | 100% | Money.__add__、Factor 数学运算 |
| 仓储集成测试 | pytest + Testcontainers PG | 90% | test_quote_repo_add_and_get()、filter/分页场景 |
| API 集成测试 | FastAPI TestClient + Testcontainers | 90% | POST /api/v1/qsv/quotes 全参数组合;RBAC 越权 403;CST RLS 18 条 |
| 【V1.1】RN 单元测试 | Jest + @testing-library/react-native | 70% | useOfflineQueue hook、QuotePreview 组件渲染、OTPInput 交互 |
| 【V1.1】RN 组件快照 | Jest snapshot | N/A | wkr-app 8+ 页 + cst-app 8 页关键组件快照(防 UI 回归) |
| 【V1.1】飞书对接契约测试 | pytest + respx mock | ⏸ 暂不实施 | 阶段 1 启动时启用;DIP1-P1 所有 API 响应体 mock |
| E2E 冒烟(Web) | Playwright + 10 条核心路径 | N/A | L1 新建 → L3 报价 → L4 派单 → L6 安装 → L8 回访 NPS(opr-admin) |
| 【V1.1】E2E 冒烟(RN) | Detox + iOS/Android 模拟器 | N/A | wkr-app:L4→L6(6S 勾选+原生相机拍照)→L7→L8;cst-app:询价→报价→下单→L8 评价→推荐分享 |
| 性能基准 | locust 50 VU + Detox 性能模式 | 吞吐量 ≥ 200 RPS;启动 ≤ 2s | GET /api/v1/cpt/orders 分页;POST /qsv/quotes 计算;RN 冷启动时间 |
| 安全回归 | OWASP ZAP 被动扫描 | 高危 0 个 | SQLi / XSS / JWT 伪造越权;【V1.1】refresh token 轮换漏洞、OTP 暴力破解 |
4.3 数据库迁移回滚测试¶
强制规则:每个 Alembic 迁移脚本必须通过:
# 脚本自动验证 3 轮循环:base → head → base → head → base → head
python scripts/migration/verify_up_down_loop.py --revision <REV_ID> --loops 3
# 任何一轮失败 → 不允许合并 PR
4.4 测试命令速查¶
# 仅单测(<2min,开发时高频跑)
cd backend && pytest tests/domain tests/application -x --tb=short
# 集成测试(<8min,PR 前跑)
pytest tests/infrastructure tests/interfaces --testcontainers
# 覆盖率报告
pytest --cov=app --cov-report=html:htmlcov --cov-report=term-missing
# 前端 Web 测试
cd frontend && pnpm vitest run # opr-admin 单测
pnpm playwright test --project=chromium # Web E2E
# 【V1.1】RN 测试
cd frontend/apps/wkr-app && pnpm test # Jest 单测
cd frontend/apps/cst-app && pnpm test # Jest 单测
cd frontend/apps/wkr-app && pnpm detox:test:ios # Detox E2E(iOS 模拟器)
cd frontend/apps/cst-app && pnpm detox:test:ios # Detox E2E(iOS 模拟器)
# 全量 CI 模拟(本地提交前最后一道门)
make ci-local # ≈ ruff + mypy + 单测 + 迁移校验 + RN Jest
五、部署架构¶
5.1 生产部署拓扑(V1.1:香港后端 + App Store/Google Play + Expo Updates)¶
flowchart TB
subgraph CF["Cloudflare CDN"]
WAF["WAF / Bot Management"]
CDN["静态资源缓存<br/>JS/CSS/图片(R2)"]
end
subgraph HK["香港阿里云 VPC(可用区 B+C 双 AZ)"]
LB["ALB 负载均衡(HTTPS 卸载)"]
subgraph App["应用集群(2-4 台 ECS,按需弹性)"]
API1["FastAPI worker x2<br/>(gunicorn + uvicorn)"]
API2["FastAPI worker x2"]
WORKER["Celery Beat + Worker x1<br/>(异步任务/推送发送)"]
end
subgraph Data["数据层高可用"]
PG["PostgreSQL 16<br/>主从半同步<br/>(跨 AZ)"]
REDIS["Redis 7 哨兵<br/>3 节点<br/>(跨 AZ)"]
end
end
subgraph R2["Cloudflare R2 Storage"]
ASSETS["附件/照片<br/>L6 现场/签字照"]
end
subgraph PUSH["【V1.1】推送服务"]
EXPO_PUSH["Expo Notifications<br/>统一 APNs + FCM"]
end
subgraph APPS["【V1.1】App 发布与热更新"]
APPSTORE["App Store Connect<br/>wkr-app + cst-app iOS"]
GOOGLEPLAY["Google Play Console<br/>wkr-app + cst-app Android"]
EXPO_UPDATE["Expo Updates<br/>OTA JS Bundle 热更新"]
end
USER_WEB["运营后台用户<br/>(PC Web)"] --> CF
USER_WKR["【V1.1】师傅<br/>wkr-app RN"] --> APPSTORE
USER_WKR --> GOOGLEPLAY
USER_WKR -.->|"OTA 检查"| EXPO_UPDATE
USER_CST["【V1.1】客户<br/>cst-app RN"] --> APPSTORE
USER_CST --> GOOGLEPLAY
USER_CST -.->|"OTA 检查"| EXPO_UPDATE
CF --> LB
LB --> API1 & API2
API1 --> PG & REDIS & R2
API2 --> PG & REDIS & R2
WORKER --> PG & REDIS
WORKER --> EXPO_PUSH
EXPO_PUSH --> USER_WKR
EXPO_PUSH --> USER_CST
APPSTORE --> USER_WKR
APPSTORE --> USER_CST
GOOGLEPLAY --> USER_WKR
GOOGLEPLAY --> USER_CST
EXPO_UPDATE --> USER_WKR
EXPO_UPDATE --> USER_CST
5.2 环境规格¶
| 环境 | 计算 | 数据库 | 缓存 | 域名 / App 通道 |
|---|---|---|---|---|
| 本地 dev | 1 Docker(单容器) | PG 16 单节点 | Redis 单节点 | localhost:3000(Web)/ 8000(API)/ Expo Go(RN) |
| staging | 2C4G × 1 ECS | PG 16 单节点 40G SSD | Redis 单节点 1G | stg-api.weavely.hk / stg-admin.weavely.hk;【V1.1】Expo staging channel + TestFlight 内测 + Google Play 内部测试轨道 |
| prod | 4C8G × 2~4 ECS(弹性) | PG 16 主从 200G SSD + PITR | Redis 哨兵 3 节点 4G | api.weavely.hk / admin.weavely.hk;【V1.1】App Store 正式版 + Google Play 正式版 + Expo production channel OTA |
5.3 【V1.1】移动端发布流程(EAS Build → Submit → Update)¶
flowchart LR
DEV["开发者提交 PR"] --> MERGE["合并 dev 分支"]
MERGE -->|"JS 变更 only"| OTA["EAS Update<br/>推送 staging channel"]
MERGE -->|"含原生模块变更"| BUILD_S["EAS Build<br/>development profile"]
BUILD_S --> TESTFLIGHT["TestFlight + Google Play 内部测试<br/>(FL 5 师傅 + CUS 10 客户内测 7 天)"]
OTA --> TESTFLIGHT
TESTFLIGHT -->|"UAT 通过"| TAG["TL 打 tag v2.x.0"]
TAG --> BUILD_P["EAS Build<br/>production profile<br/>iOS + Android × 双端 = 4 artifact"]
BUILD_P --> SUBMIT["EAS Submit<br/>App Store Connect + Google Play"]
SUBMIT --> REVIEW["审核 24-48h"]
REVIEW -->|"通过"| LIVE["App Store + Google Play 正式上架"]
REVIEW -->|"被拒"| REJECT["记录 DWLG + 修正 + 重新提交"]
LIVE --> OTA_P["EAS Update<br/>production channel 后续 OTA"]
关键凭据(TL 持有,Code Agent 通过 EAS CLI 调用,禁止直接接触明文):
- Apple Developer 账号 + App Store Connect API Key(.eas/credentials/ios.json 加密存储)
- Google Play Service Account JSON(.eas/credentials/android-service-account.json)
- Expo Access Token(CI 环境变量 EXPO_TOKEN,TL 在 GitHub Actions Secrets 配置)
5.4 容器化方案(Dockerfile 多阶段)¶
后端 dip1/backend/Dockerfile:
# --- Builder:编译依赖 ---
FROM python:3.12-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends build-essential
COPY requirements.txt requirements-dev.txt ./
RUN pip install --user --no-cache-dir -r requirements.txt
# --- Runtime:最小化镜像 ---
FROM python:3.12-slim AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH="/root/.local/bin:$PATH"
EXPOSE 8000
HEALTHCHECK --interval=10s CMD curl -f http://localhost:8000/health || exit 1
CMD ["gunicorn", "app.main:create_app()", "-b", "0.0.0.0:8000", \
"-k", "uvicorn.workers.UvicornWorker", "-w", "2", "--threads", "4"]
【V1.1】RN App 不走 Docker 容器化(Expo managed workflow 由 EAS 云端构建);opr-admin Next.js 走 Cloudflare Pages 静态部署(
next build产物)。
5.5 蓝绿部署与回滚¶
# Step 1: 发布绿色环境(v2.0.0),不切流量
docker compose up -d --scale api-green=2 api-green
# Step 2: 冒烟绿色环境
curl -H "Host: api.weavely.hk" http://green-internal/health
newman run smoke-tests.postman_collection.json --env-var baseUrl=http://green-internal
# Step 3: 逐步切流量 10% → 50% → 100%(ALB 权重调整)
# Step 4: 观察 30 分钟,指标异常 → 一键回滚
make rollback-blue # ALB 权重切回蓝色 100%,绿色停服
# Step 5: 成功 → 旧环境保留 24 小时后清理
# 【V1.1】App 回滚(不同于后端):
# App Store:通过 phased release(7 天 1%/2%/5%/10%/20%/50%/100%)中途暂停或完全停止
# Google Play:staged rollout(0.1%/1%/10%/50%/100%)中途可 halt
# OTA 回滚:eas update --branch production --rev <previous-update-id>(即时回滚 JS Bundle)
5.6 备份与容灾¶
| 组件 | 策略 | RPO | RTO |
|---|---|---|---|
| PostgreSQL | pg_basebackup 每日全量 + WAL 归档每 5 分钟到 R2 |
5 分钟 | 30 分钟 |
| Redis | AOF 每 1 秒 fsync + 每日 RDB 快照到 R2 | 1 秒 | 10 分钟 |
| R2 附件 | 自带 11 9 耐用性 + 跨区域复制 | N/A | N/A |
| 【V1.1】Expo Updates | JS Bundle 版本历史自动保留(最近 30 个) | N/A | 即时(eas update --rev 回滚) |
| 【V1.1】App Store / Google Play | 历史版本保留(App Store 最近 30 个;Google Play 无限) | N/A | 24h(重新上架旧版本) |
| 配置/密钥 | Vault + GitOps 加密存储 | N/A | 5 分钟 |
六、可观测与告警¶
6.1 三支柱(V1.1:后端 + 移动端双视角)¶
| 支柱 | 工具 | 关键指标/查询 |
|---|---|---|
| Logs 日志(后端) | Python structlog → Loki → Grafana | trace_id 串联;ERROR 级自动入告警 |
| 【V1.1】Logs 日志(RN) | Sentry RN SDK → Sentry Dashboard | 崩溃 stack trace + 设备信息(iOS/Android/version); breadcrumbs 自动记录用户操作 |
| Metrics 指标(后端) | Prometheus(/metrics)→ Grafana | HTTP 4xx/5xx 率、P95 延迟、DB 连接池、QSV 计算耗时、推送送达率 |
| 【V1.1】Metrics 指标(RN) | Sentry Performance + Expo APM | App 启动时间(冷启 ≤ 2s 热启 ≤ 0.5s)、屏幕 FPS、操作响应时间 |
| Traces 链路(后端) | OpenTelemetry → Jaeger | 单请求跨域耗时分布:API → App → Domain → Repo → DB/Redis |
| 【V1.1】推送监控 | push_delivery_logs 表 + Grafana |
8 类推送 24h 送达率、点击率、失败重试队列长度 |
6.2 告警阈值(飞书机器人推送到 TL/OL 群,V1.1 扩展至 12 项)¶
| 告警项 | 阈值 | 级别 | 通知对象 |
|---|---|---|---|
| API P95 延迟 > 1s,连续 5 分钟 | 阈值 + 持续 | P1 | TL |
| HTTP 5xx 率 > 3% | 阈值 | P1 | TL + OL |
| DB 连接池使用率 > 85% | 阈值 | P2 | TL |
| Redis 可用内存 < 20% | 阈值 | P2 | TL |
| ⏸ 飞书 DLQ 队列长度 > 10 条(阶段 1 启动时启用) | 阈值 | P1 | TL + DT |
| PG 复制延迟 > 30s | 阈值 | P1 | TL |
| 定时巡检同步脚本 2 次未执行 | Cron 失败 | P2 | TL |
| 每日 23:59 当日无异常报告 | 心跳 | 信息 | BO |
| 【V1.1】RN 崩溃率 > 0.5%(wkr-app + cst-app 合并) | 阈值 + 1h 持续 | P1 | TL |
| 【V1.1】App 启动时间 P95 > 3s | 阈值 + 24h 持续 | P2 | TL |
| 【V1.1】推送送达率 < 95%(24h 窗口) | 阈值 | P2 | TL + OL |
| 【V1.1】App Store / Google Play 审核被拒 | 事件触发 | P2 | TL + DT |
七、ADR(架构决策记录)摘要(V1.1:新增 ADR-011/012/013)¶
| ADR | 决策 | 替代方案 | 原因 |
|---|---|---|---|
| ADR-001 | 模块化单体优先,阶段 2 不拆微服务 | 直接微服务 | DDD 模块边界清晰后才拆;团队规模 < 10 人,单体效率高 |
| ADR-002 | Python FastAPI 后端 | Go Gin / Node.js NestJS | 现有团队 Python 熟练;AI 听写助手 Python 生态无缝 |
| ADR-003 | Next.js App Router 前端(opr-admin Web) | Vue Nuxt | React 生态成熟 + TL 熟悉;Web 运营后台 PC 场景体验佳 |
| ADR-004 | PostgreSQL + JSONB 存储灵活字段 | MongoDB | 事务一致性(报价/支付)+ JSONB 性能够,单库上手快 |
| ADR-005 | ULID 主键(非自增 ID) | UUID v4 / Snowflake | 可排序 + 时间前缀 + 无中心节点,比 UUID4 索引紧凑 |
| ADR-006 | Testcontainers 集成测试 | SQLite / H2 | 贴近 PG 真实行为(JSONB/Rls/索引),避免"本地过 CI 挂" |
| ADR-007 | Redis Stream 轻量消息总线 | RabbitMQ / Kafka | 阶段 2 日吞吐 < 1 万条,Redis 已部署,零额外依赖 |
| ADR-008 | Cloudflare R2 存附件(零流出费) | 阿里云 OSS / 自建 MinIO | 香港 CDN 天然近 + R2 零 egress 费,月省 30% 成本 |
| ADR-009 | GitHub Actions + 自建 Runner + EAS Build | GitLab CI | 主项目已在 Gitee/GitHub;Runner 香港部署低延迟;EAS 云端构建免本地 Xcode/Android Studio |
| ADR-010 | PostgreSQL RLS 行级权限 | 应用层 WHERE 过滤 | 物理隔离不可绕过;审计合规(师傅只能看自己订单;客户只能看自己订单) |
| 【V1.1】ADR-011 | Expo managed workflow(RN 双端) | 纯 RN CLI / Flutter | EAS Build 云端构建免本地原生工具链;Expo Updates OTA 一站式;Expo Notifications 统一 APNs+FCM;SDK 统一依赖版本减少冲突 |
| 【V1.1】ADR-012 | NativeWind v4(Tailwind for RN) | Tamagui / RN Paper | 与 opr-admin 的 Tailwind 语法一致,降低切换成本;V2.0 重写 ADR-002 后前端团队维护负担最小 |
| 【V1.1】ADR-013 | EAS Build 云端构建 + EAS Submit 自动提审 | 本地 Xcode + Fastlane + 手动提审 | 香港团队 Windows 为主无 macOS;云端构建 25min/4 artifact 远优于本地;EAS Submit 自动化提审减少人工失误 |
修订记录¶
| 版本 | 日期 | 修订人 | 修订内容 |
|---|---|---|---|
| V1.0 | 2026-08-09 | DT | 初始版本:工具链 · Windows 一键初始化 · .env.example · docker-compose · Git Flow Lite · Conventional Commits · CI/CD 8 Job · 测试金字塔 · 香港双 AZ 部署 · 三支柱可观测 · 10 条 ADR |
| V1.1 | 2026-08-09 | DT | 原生 App 方案升级(对齐 ADR-002/006/007/008):① §1.1 工具链新增 6 项(Expo CLI / EAS CLI / watchman / Xcode / Android Studio / Apple Developer + Google Play Console 账号);② §1.2 Windows 初始化脚本新增步骤 5-6(Expo/EAS 安装登录 + 双端 eas.json 配置);③ §1.3 .env.example 新增 4 组变量(Expo/EAS / 推送 / 移动端认证 / Sentry RN DSN 双端);飞书变量注释为⏸ 暂不实施;④ §三 CI/CD 流水线架构图重写为三轨(后端 + Web + RN),新增 4 Job(test-rn / e2e-detox / eas-build / eas-submit / eas-update),OTA 热更新策略说明;⑤ §四 测试金字塔重写为三端分离(Web Playwright + RN Detox + 后端 Testcontainers),各层测试要求新增 RN Jest/快照/Detox E2E;测试命令速查新增 RN 4 条命令;⑥ §五 部署架构拓扑图新增推送服务 + App Store/Google Play + Expo Updates 三个子图;环境规格表新增 App 通道列;§5.3 新增移动端发布流程 Mermaid(EAS Build → Submit → Update)+ 关键凭据说明;§5.5 蓝绿部署新增 App 回滚策略(phased release / staged rollout / OTA rev);§5.6 备份容灾表新增 Expo Updates + App Store/Google Play 历史版本;⑦ §六 可观测三支柱表新增 RN Sentry + Expo APM + 推送监控;告警阈值从 8 项扩展为 12 项(新增 RN 崩溃率/启动时间/推送送达率/审核被拒);⑧ §七 ADR 表新增 ADR-011 Expo managed workflow / ADR-012 NativeWind / ADR-013 EAS Build 云端构建;ADR-009 扩展为含 EAS Build;⑨ 新增修订记录 |
本文件为 DIP1 技术基础设施设计(DOC-D01-D3 V1.1,对齐原生 App 方案 V2.0),与 DIP1-P1 V1.1(⏸ 暂不实施)、DIP1-P2 V2.0、DIP1-ARC V2.0 共同构成完整技术方案。