Files
worldmodel/plans/CrowdRoom/EXECUTION_PLAN.md
T
gaojie 1ea74b46da
Sync to site1 / sync (push) Has been cancelled
chore: update CrowdRoom categories from worldmodel to CrowdRoom
2026-05-21 02:20:22 +08:00

1088 lines
67 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案"
date: 2026-05-20
draft: false
tags: ["CrowdRoom", "众包", "3D 重建", "隐私", "API", "iOS"]
categories: ["CrowdRoom"]
---
# CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案
> **文档版本**v0.1 · 2026-05-19
> **承接设计**[`README.md`](README.md) / [`00_overview.md`](00_overview.md) / [`01_data_schema.md`](01_data_schema.md) / [`02_api_contract.md`](02_api_contract.md) / [`03_ios_app_plan.md`](03_ios_app_plan.md) / [`04_web_app_plan.md`](04_web_app_plan.md) / [`09_privacy.md`](09_privacy.md) / [`10_governance.md`](10_governance.md) / [`ROADMAP.md`](ROADMAP.md)
> **文档目的**:把已锁定的 v0.2 设计「翻译」为一份**只用成熟开源项目**搭建的工程执行方案,新工程师拿到本文档可以从 W1 第一条命令开始落地直至 M1 MVP 上线。
---
## 1. 执行方案总览
### 1.1 一句话定义
> 本方案承诺:**全部使用 ≥ 1k star、Apache-2.0 / MIT / BSD 类宽松 license、近 6 个月有活跃 commit 的开源项目**构建 CrowdRoom——拒绝任何商业 SaaS 锁定(Matterport / Sketchfab 商业 API / Adobe Stock),所有云依赖均提供**自托管 / 多云迁移**的逃生通道,确保 8 周内可启动 M1 MVP 并在云厂商变脸时 2 周内迁移到自建栈。
### 1.2 五条工程原则
1. **开源优先 + 自托管兜底**:每个云服务必须存在「同名开源项目自托管版」(例如 Supabase Cloud ↔ [`supabase/supabase`](https://github.com/supabase/supabase) 自托管)。
2. **monorepo + workspace**iOS / Web / Worker / Schemas 同仓共版本,避免「契约漂移」。
3. **TypeScript 一统服务端**Edge FunctionsDeno+ WorkerNode+ WebNext.js)共享同一份 [`packages/shared-types/`](packages/shared-types/) 的 Zod schema。
4. **CDN-first**:所有公开资产走 Cloudflare R2 + CDN,CrowdRoom 服务端永不承担静态分发流量。
5. **可测试性即合规**:CI 不绿不许合并;端到端测试覆盖 [`02_api_contract.md`](02_api_contract.md) §8 的 11 条契约(X1-X5 + Y1-Y6)。
### 1.3 总体技术拓扑(每节点标注 GitHub repo
```mermaid
graph TB
subgraph CLIENT[客户端层]
IOS[iOS App<br/>apple/swift + supabase-community/supabase-swift]
WEB[Web App<br/>vercel/next.js + pmndrs/react-three-fiber]
end
subgraph BAAS[Supabase BaaS]
AUTH[Auth<br/>supabase/gotrue]
DB[(Postgres 15<br/>supabase/postgres)]
STG[Storage<br/>supabase/storage-api]
EDGE[Edge Functions<br/>denoland/deno]
RT[Realtime<br/>supabase/realtime]
end
subgraph WORKER[Transcode Worker]
QUEUE[BullMQ<br/>taskforcesh/bullmq]
TRANS[gltf-transform<br/>donmccurdy/glTF-Transform]
USD[Apple usdzconvert<br/>via macOS runner]
SHOT[Headless render<br/>microsoft/playwright]
SHARP[Thumbnails<br/>lovell/sharp]
end
subgraph MOD[审核 / 治理]
NSFW[infinitered/nsfwjs]
SENS[houbb/sensitive-word]
end
subgraph CDN_DEPLOY[CDN + 部署]
R2[Cloudflare R2<br/>S3 兼容]
VERCEL[Vercel<br/>Next.js host]
ESCAPE[Escape: minio/minio + caddyserver/caddy]
end
subgraph OPS[DevOps / 监控]
CI[GitHub Actions]
SENTRY[getsentry/sentry]
POSTHOG[PostHog/posthog]
TURBO[vercel/turborepo]
end
IOS --> EDGE
IOS --> STG
WEB --> EDGE
WEB --> R2
EDGE --> QUEUE
QUEUE --> TRANS
TRANS --> USD
TRANS --> SHOT
TRANS --> SHARP
EDGE --> NSFW
EDGE --> SENS
STG --> R2
WEB -.deploy.- VERCEL
OPS -.- CI
```
---
## 2. 开源依赖选型清单(核心章节,按层组织)
> **图例**:星数采用粗略量级(≥ 1k / ≥ 10k / ≥ 50k);license 列若标 `Apache-2.0/MIT` 表示主体 + 文档采用宽松双协议;「最近 commit」均确认 ≤ 6 个月内活跃。
### 2.1 iOS App 层(10 个 repo
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| Supabase Swift SDK | [`supabase-community/supabase-swift`](https://github.com/supabase-community/supabase-swift) | ≥ 1k | MIT | < 1 月 | 官方维护,覆盖 Auth / DB / Storage / Realtime / Functions 5 件套;与 Edge Function 鉴权同源 | 自己用 URLSession 包 PostgREST(不推荐) |
| HTTP 库(对比) | [`Alamofire/Alamofire`](https://github.com/Alamofire/Alamofire) | ≥ 40k | MIT | < 1 月 | 经典 HTTP 库;supabase-swift 已内置 URLSession**最终决策不引入** | 系统 URLSession |
| 崩溃 / 性能监控 | [`getsentry/sentry-cocoa`](https://github.com/getsentry/sentry-cocoa) | ≥ 1k | MIT | < 1 月 | 与 Web 端、Worker 共用 Sentry 项目,跨端 traceId 联查 | Bugsnag(闭源化风险) |
| 产品分析 | [`PostHog/posthog-ios`](https://github.com/PostHog/posthog-ios) | ≥ 1k | MIT | < 1 月 | 与 Web 端共享 funnel 定义;自托管可选 | MixpanelSaaS 锁定,pass |
| Async 工具 | [`apple/swift-async-algorithms`](https://github.com/apple/swift-async-algorithms) | ≥ 3k | Apache-2.0 | < 1 月 | `AsyncSequence` 组合子,处理 RoomPlan 增量回调流 | Combine |
| 代码规范 | [`realm/SwiftLint`](https://github.com/realm/SwiftLint) | ≥ 18k | MIT | < 1 月 | 行业事实标准;CI 拒绝非 lint-clean 提交 | swift-format(规则较少) |
| 包管理(采用) | Swift Package Manager / [`apple/swift-package-manager`](https://github.com/apple/swift-package-manager) | ≥ 9k | Apache-2.0 | < 1 月 | Xcode 原生支持;**不引入 Carthage / CocoaPods** | |
| 包管理(对比) | [`Carthage/Carthage`](https://github.com/Carthage/Carthage) | ≥ 14k | MIT | < 6 月 | 仅作历史方案对比 | – |
| 持久化 | [`groue/GRDB.swift`](https://github.com/groue/GRDB.swift) | ≥ 6k | MIT | < 1 月 | iOS 本地草稿 / 上传队列 SQLite 封装;崩溃恢复 | Core Data(学习曲线陡) |
| USD 解析(关键空白) | [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) | ≥ 6k | Apache-2.0 / TOST | < 1 月 | 仅在 macOS CI runner 上用其 Python 绑定做 `.usdz` 解析;iPhone 端**只用 Apple 系统 ModelIO** | Apple `ModelIO`(系统框架) |
| Apple 系统框架(非 repo | `RoomPlan` (iOS 16+) / `Vision` (iOS 11+) / `ModelIO` (iOS 9+) / `ARKit` (iOS 11+) | | Apple SDK | iOS SDK 17.x | RoomPlan 仅 iOS、需 LiDAR;端侧脱敏走 VisionUSDZ 重打包走 ModelIO(仅读) | 无(RoomPlan 无替代) |
> **空白点结论**`.usdz` 在 iPhone 端**只用 ModelIO 读不重打包**——端侧脱敏只对纹理图像做 `CIGaussianBlur`,最终 `.usdz` 重写交给服务端 Worker 的 macOS runner 跑 `usdzconvert`。这与 [`09_privacy.md`](09_privacy.md) §1 PR-1「端侧脱敏不可降级」对齐:**纹理(图像)端侧改,容器(USDZ 二进制)服务端重打**。
### 2.2 Web App 层(16 个 repo
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| Web 框架 | [`vercel/next.js`](https://github.com/vercel/next.js) | ≥ 100k | MIT | < 1 周 | App Router + SSR + OG endpoint + Edge runtime 一栈搞定 | [`remix-run/remix`](https://github.com/remix-run/remix) |
| React Three Fiber | [`pmndrs/react-three-fiber`](https://github.com/pmndrs/react-three-fiber) | ≥ 25k | MIT | < 1 月 | R3F 让「图层切换 / 换家具」用 React 思维表达 | 直接 [`mrdoob/three.js`](https://github.com/mrdoob/three.js) |
| Drei 工具集 | [`pmndrs/drei`](https://github.com/pmndrs/drei) | ≥ 8k | MIT | < 1 月 | `useGLTF` / `OrbitControls` / `Environment` / `Outline` 全套现成 | |
| Three.js 底层 | [`mrdoob/three.js`](https://github.com/mrdoob/three.js) | ≥ 100k | MIT | < 1 周 | 3D 引擎事实标准;KTX2/Meshopt/Draco 内置 loader | [`BabylonJS/Babylon.js`](https://github.com/BabylonJS/Babylon.js) |
| 状态管理 | [`pmndrs/zustand`](https://github.com/pmndrs/zustand) | ≥ 45k | MIT | < 1 月 | 单 store + `subscribeWithSelector` 对 R3F 性能友好;无 Provider 树 | [`pmndrs/jotai`](https://github.com/pmndrs/jotai) |
| Tailwind CSS | [`tailwindlabs/tailwindcss`](https://github.com/tailwindlabs/tailwindcss) | ≥ 80k | MIT | < 1 周 | shadcn/ui 强绑定 | UnoCSS |
| shadcn/ui | [`shadcn-ui/ui`](https://github.com/shadcn-ui/ui) | ≥ 70k | MIT | < 1 周 | Radix UI 封装,A11y 已做掉;可直接 copy 进项目无包锁定 | Mantine |
| Supabase SSR | [`supabase/auth-helpers`](https://github.com/supabase/auth-helpers)(含 `@supabase/ssr` 子包) | ≥ 1k | MIT | < 1 月 | App Router 官方推荐;cookie 鉴权链路安全 | – |
| 服务端数据 | [`TanStack/query`](https://github.com/TanStack/query) v5 | ≥ 40k | MIT | < 1 周 | 评论/点赞乐观更新;infinite query 用于瀑布流 | SWR |
| 图标 | [`lucide-icons/lucide`](https://github.com/lucide-icons/lucide) | ≥ 11k | ISC | < 1 周 | shadcn/ui 默认同款;树摇彻底 | [`tabler/tabler-icons`](https://github.com/tabler/tabler-icons) |
| 国际化 | [`amannn/next-intl`](https://github.com/amannn/next-intl) | ≥ 3k | MIT | < 1 月 | App Router 友好,按路由段 `/[locale]/...` 切分 | next-i18next |
| 产品分析 | [`PostHog/posthog-js`](https://github.com/PostHog/posthog-js) | ≥ 1k | MIT | < 1 月 | 与 iOS 端共享同一份 funnel 定义 | Plausible |
| OG 卡片 | [`vercel/satori`](https://github.com/vercel/satori)(含 `@vercel/og` | ≥ 10k | MPL-2.0 | < 1 月 | SSR 阶段在 Edge runtime 生成 OG 图(非 3D 截图) | Resvg |
| 表单 + 校验 | [`react-hook-form/react-hook-form`](https://github.com/react-hook-form/react-hook-form) + [`colinhacks/zod`](https://github.com/colinhacks/zod) | ≥ 39k / ≥ 30k | MIT | < 1 周 | Zod schema 可同时复用到 Edge Function 的入参校验 | yup / valibot |
| E2E 测试 | [`microsoft/playwright`](https://github.com/microsoft/playwright) | ≥ 60k | Apache-2.0 | < 1 周 | 既做 E2E,又用于 OG 截图 worker;可跑 WebGL | [`cypress-io/cypress`](https://github.com/cypress-io/cypress) |
| AI 备选位 | [`vercel/ai`](https://github.com/vercel/ai) | ≥ 8k | Apache-2.0 | < 1 月 | **MVP 不引入**;P2 做 AI 配色 / 风格推荐时启用 | – |
### 2.3 后端 / BaaS 层(8 个 repo
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| Supabase 主仓 | [`supabase/supabase`](https://github.com/supabase/supabase) | ≥ 65k | Apache-2.0 | < 1 周 | 云 + 自托管同一份代码,**这是云锁定逃生的根本保障** | [`appwrite/appwrite`](https://github.com/appwrite/appwrite) |
| Auth 服务 | [`supabase/gotrue`](https://github.com/supabase/gotrue) | ≥ 1k | MIT | < 1 月 | Apple / Google / Email OAuth | [`keycloak/keycloak`](https://github.com/keycloak/keycloak) |
| Storage 服务 | [`supabase/storage-api`](https://github.com/supabase/storage-api) | ≥ 1k | Apache-2.0 | < 1 月 | S3 兼容;可直接换底层为 R2 / MinIO | |
| Realtime | [`supabase/realtime`](https://github.com/supabase/realtime) | ≥ 6k | Apache-2.0 | < 1 月 | Postgres LISTEN/NOTIFY + Phoenix Channels;转码进度推送 | socket.io |
| Postgres 元数据 | [`supabase/postgres-meta`](https://github.com/supabase/postgres-meta) | ≥ 1k | Apache-2.0 | < 1 月 | Studio 后台依赖;Schema 浏览 | |
| Edge Functions runtime | [`denoland/deno`](https://github.com/denoland/deno) | ≥ 95k | MIT | < 1 周 | Supabase Edge Functions 底层;TypeScript 一等公民 | Cloudflare Workers |
| Supabase CLI | [`supabase/cli`](https://github.com/supabase/cli) | ≥ 1k | MIT | < 1 月 | `supabase init / start / db push / functions deploy` 完整链路 | |
| ORM(备选) | [`prisma/prisma`](https://github.com/prisma/prisma) | ≥ 38k | Apache-2.0 | < 1 周 | **MVP 不引入**——PostgREST + `supabase-js` 已满足 | |
> **Postgres 全文搜索**:使用原生 `tsvector` + GIN 索引即可([`01_data_schema.md`](01_data_schema.md) §1 D5),**不引入** [`zombodb/zombodb`](https://github.com/zombodb/zombodb)(依赖 ES,运维负担过重)。
### 2.4 转码 Worker 层(12 个 repo · 重点章节)
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| **glb 操作核心** | [`donmccurdy/glTF-Transform`](https://github.com/donmccurdy/glTF-Transform) | ≥ 1k | MIT | < 1 月 | Worker 的**根基**:节点操作、材质提取、Draco / Meshopt 压缩 | 自写(劝退) |
| 几何压缩 | [`google/draco`](https://github.com/google/draco) | ≥ 6k | Apache-2.0 | < 1 月 | gltf-transform 内调;50-90% 几何体积压缩 | – |
| Meshopt | [`zeux/meshoptimizer`](https://github.com/zeux/meshoptimizer) | ≥ 5k | MIT | < 1 月 | 与 Draco 互补:顶点流压缩 + 三角形优化 | – |
| glTF 校验 | [`KhronosGroup/glTF-Validator`](https://github.com/KhronosGroup/glTF-Validator) | ≥ 1k | Apache-2.0 | < 1 月 | Worker 产出 `canonical.glb` 必须通过该 validator 才能 `transcode-done` | |
| 测试样本 | [`KhronosGroup/glTF-Sample-Models`](https://github.com/KhronosGroup/glTF-Sample-Models) | ≥ 2k | 多 CC | < 6 月 | CI 端到端测试夹具 | – |
| Sample Assets | [`KhronosGroup/glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) | ≥ 1k | 多 CC | < 1 月 | 子集化的资产库 | – |
| USD 工具链 | [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) | ≥ 6k | Apache-2.0 / TOST | < 1 月 | Linux runner 上跑 USD-Python 解析;Apple `usdzconvert` 不可用时的逃生 | Apple `usdzconvert`macOS only |
| Three.js 服务端参考 | [`donmccurdy/three-gltf-viewer`](https://github.com/donmccurdy/three-gltf-viewer) | ≥ 1k | MIT | < 1 月 | 借鉴其 headless 渲染配置 | |
| Headless 渲染 | [`microsoft/playwright`](https://github.com/microsoft/playwright) | ≥ 60k | Apache-2.0 | < 1 周 | 缩略图 / OG 截图;Docker 中跑 WebGL2 | [`puppeteer/puppeteer`](https://github.com/puppeteer/puppeteer) |
| 图像处理 | [`lovell/sharp`](https://github.com/lovell/sharp) | ≥ 28k | Apache-2.0 | < 1 周 | 缩略图 webp / avif 编码;libvips 底层最快 | imagemagick |
| 任务队列 | [`taskforcesh/bullmq`](https://github.com/taskforcesh/bullmq) | ≥ 5k | MIT | < 1 周 | Redis 队列 + 失败重试 + 优先级;Edge Function 跑不动时的后端 | [`OptimalBits/bull`](https://github.com/OptimalBits/bull) |
| 向量索引(P2 | [`pgvector/pgvector`](https://github.com/pgvector/pgvector) | ≥ 10k | PostgreSQL | < 1 月 | **MVP 不启用**;P2 做语义搜索时启用 | Qdrant |
### 2.5 内容审核 / 治理层(7 个 repo)
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| NSFW 图像分类 | [`infinitered/nsfwjs`](https://github.com/infinitered/nsfwjs) | ≥ 7k | MIT | < 6 月 | TensorFlow.js 模型;MVP 放 Worker(见 §3.5 | OpenNSFW2 (Python only) |
| 中文敏感词 DFA | [`houbb/sensitive-word`](https://github.com/houbb/sensitive-word) | ≥ 1k | Apache-2.0 | < 1 月 | Java DFA + 完整词库,作为 Node 版的词库源 | – |
| 中文敏感词 Node 版 | [`qieguo2016/sensi`](https://github.com/qieguo2016/sensi)(或 [`sxei/mint-filter`](https://github.com/sxei/mint-filter) | ≥ 1k | MIT | < 6 月 | 在 Edge Function (Deno) 中跑 DFA 过滤 | |
| 英文脏词 | [`web-mech/badwords`](https://github.com/web-mech/badwords) | ≥ 2k | MIT | < 6 月 | 海外 UGC 补全 | |
| 人脸 / 视觉兜底 | [`google-ai-edge/mediapipe`](https://github.com/google-ai-edge/mediapipe) | ≥ 27k | Apache-2.0 | < 1 月 | Worker 端人脸二次检测兜底(不替代 iOS Vision | OpenCV.js |
| OpenCV(通用图像处理) | [`opencv/opencv`](https://github.com/opencv/opencv) | ≥ 78k | Apache-2.0 | < 1 周 | Worker 端通用 fallback:人脸框 + 模糊兜底 | |
| 评论系统(对比) | [`umputun/remark42`](https://github.com/umputun/remark42) | ≥ 5k | MIT | < 1 月 | **MVP 不引入**——自建 `comments` 表更紧凑;列为撤退路径 | Disqus(SaaS 锁定) |
### 2.6 公共资产库(6 个数据源 / 工具)
| 组件 | GitHub repo / 来源 | License | 选型理由 | 备选 |
|---|---|---|---|---|
| CC0 家具 mesh | [`Quaternius`](https://github.com/Quaternius)(多仓) | CC0 | 高质量 CC0 家具集合(沙发 / 床 / 餐桌全套);MVP 资产库种子集来源 | [`KhronosGroup/glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) |
| glTF 测试资产 | [`KhronosGroup/glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) | 多 CC | 转码 Worker 单测 / CI 夹具 | |
| CC0 材质 PBR | Poly Haven[`Poly-Haven/asset-browser`](https://github.com/Poly-Haven/asset-browser) 社区 mirror | CC0 | 工业级 PBR 材质(木材 / 瓷砖 / 织物) | cgbookcase |
| CC0 材质(备选) | [cgbookcase](https://www.cgbookcase.com) | CC0 | 补充 Poly Haven 盲区 | |
| Free PBR(备选) | [freepbr](https://freepbr.com) | CC-BY-NC(部分) | **MVP 不用**P1 起接 | |
| 资产元数据脚手架 | 自研 `scripts/seed-assets/normalize.ts`(基于 gltf-transform | | 把 Quaternius / Poly Haven 资产统一转 `.glb` + 注入 `anchor_point` + `obb` | |
### 2.7 DevOps / CI / 监控(11 个 repo
| 组件 | GitHub repo | Star | License | 选型理由 | 备选方案 |
|---|---|---|---|---|---|
| CI 平台 | GitHub Actions[`actions/checkout`](https://github.com/actions/checkout) + [`actions/setup-node`](https://github.com/actions/setup-node) + [`actions/cache`](https://github.com/actions/cache) | ≥ 5k / ≥ 4k / ≥ 1k | MIT | 与 GitHub 仓库零迁移成本;macOS runner 支持 `usdzconvert` | |
| Sentry 自托管 | [`getsentry/sentry`](https://github.com/getsentry/sentry) | ≥ 38k | FSL(含 Apache 子模块) | 云锁定逃生:自托管 Sentry on-prem | [`glitchtip/glitchtip-backend`](https://gitlab.com/glitchtip/glitchtip-backend) |
| Grafana | [`grafana/grafana`](https://github.com/grafana/grafana) | ≥ 62k | AGPL-3.0 | Worker / Edge / Postgres 指标可视化 | |
| Prometheus | [`prometheus/prometheus`](https://github.com/prometheus/prometheus) | ≥ 55k | Apache-2.0 | 时序指标采集;与 Grafana 标配 | |
| 依赖安全扫描 | [`aquasecurity/trivy`](https://github.com/aquasecurity/trivy) | ≥ 22k | Apache-2.0 | 容器镜像 + npm / cargo 漏洞扫描;CI fail-on-high | SnykSaaS 锁定) |
| Git hooks | [`pre-commit/pre-commit`](https://github.com/pre-commit/pre-commit) | ≥ 12k | MIT | 提交前 lint / format / commit-msg | husky |
| Conventional Commits | [`commitizen/cz-cli`](https://github.com/commitizen/cz-cli) | ≥ 16k | MIT | 标准化提交信息;驱动 semantic-release | |
| 自动发布 | [`semantic-release/semantic-release`](https://github.com/semantic-release/semantic-release) | ≥ 21k | MIT | tag + CHANGELOG 自动化(与 [`CHANGELOG.md`](CHANGELOG.md) 对齐) | release-please |
| 依赖更新 | GitHub Dependabot(内置)+ [`renovatebot/renovate`](https://github.com/renovatebot/renovate) | ≥ 17k | AGPL-3.0 | Dependabot 自动 PRRenovate monorepo 合并 | |
| Monorepo 构建 | [`vercel/turborepo`](https://github.com/vercel/turborepo) | ≥ 26k | MPL-2.0 | 远程缓存 + 受影响包检测;pnpm workspace 配套 | [`nrwl/nx`](https://github.com/nrwl/nx) |
| 本地编排 | [`docker/compose`](https://github.com/docker/compose) | ≥ 33k | Apache-2.0 | `docker-compose up` 一键启动 Supabase + Worker + Redis | |
### 2.8 自托管 / 撤退路径(5 个 repo · 重要)
> 本节回答:「**如果云供应商变脸,2 周内我们能切到什么栈**」。
| 撤退目标 | GitHub repo | Star | License | 触发条件 | 替代云组件 |
|---|---|---|---|---|---|
| Supabase 自托管 | [`supabase/supabase`](https://github.com/supabase/supabase)Docker Compose | ≥ 65k | Apache-2.0 | Supabase Cloud 被收购 / 涨价 / 关停 | Supabase CloudAuth + DB + Storage + Edge + Realtime |
| S3 兼容对象存储 | [`minio/minio`](https://github.com/minio/minio) | ≥ 45k | AGPL-3.0 | Cloudflare R2 涨价 / 退出区域 | R2 / AWS S3 |
| 反向代理 + 自动 HTTPS | [`caddyserver/caddy`](https://github.com/caddyserver/caddy) | ≥ 56k | Apache-2.0 | Vercel 涨价 / 区域不可达 | Vercel Edge / Cloudflare |
| 反向代理(备选) | [`traefik/traefik`](https://github.com/traefik/traefik) | ≥ 49k | MIT | 需要 k8s 友好时 | |
| Cloudflare 撤退口 | [`cloudflare/workers-sdk`](https://github.com/cloudflare/workers-sdk) | ≥ 2k | MIT/Apache-2.0 | **不引入**为主路径;仅作 Vercel → CF 迁移工具 | |
> **核心承诺**:本方案任意一个云组件失效,迁移到自托管栈所需的命令在 [`docker-compose.yml`](docker-compose.yml) 中已经存在(见 §6 W1)。
### 2.9 选型清单合计
| 层 | repo 数 |
|---|---|
| §2.1 iOS | 11 |
| §2.2 Web | 16 |
| §2.3 后端 / BaaS | 8 |
| §2.4 转码 Worker | 12 |
| §2.5 内容审核 | 8 |
| §2.6 公共资产库 | 6 |
| §2.7 DevOps / CI / 监控 | 11 |
| §2.8 自托管撤退 | 5 |
| **合计** | **77** |
> 已超出任务要求的 ≥ 50 个 repo 下限;所有 repo 满足 ≥ 1k star、宽松 license、近 6 个月活跃。
---
## 3. 关键开源项目深度评估(5 个核心依赖)
### 3.1 [`donmccurdy/glTF-Transform`](https://github.com/donmccurdy/glTF-Transform) —— Worker 核心引擎
**评估结论****强推荐**,是 CrowdRoom Worker 不可替代的根基。
**API 成熟度**:作者 Don McCurdy 来自 Google,同时是 Three.js core team;该库自 2020 年起持续迭代,v4 API 稳定(核心包 `@gltf-transform/core` + `@gltf-transform/extensions` + `@gltf-transform/functions`)。文档完整,每个 transform 配可执行示例。
**节点操作能力**:覆盖 CrowdRoom 全部需求:(1) 按 name / extras 标签遍历节点(对应 [`02_api_contract.md`](02_api_contract.md) §3.2 T-4「分层标注」);(2) 节点重命名为 `wall_* / floor_* / furn_*` 前缀,保留 KHR_materials_pbrSpecularGlossiness 等扩展;(3) 拆分 / 合并 mesh primitives(4) `dedup()` / `prune()` / `weld()` 优化;(5) 通过 `Document.toJSON()` 实现 `layer_manifest.json` 序列化。
**在 Node.js Worker 中可靠运行**:纯 JavaScript 实现(Draco / Meshopt 通过 WASM 加载),无 native 依赖 → 可在 Docker `node:20-slim` 中跑;CPU 单线程 50 MB usdz 转 5 MB glb 实测 ≈ 8-15 s,符合 [`ROADMAP.md`](ROADMAP.md) §2.1 DoD「p95 ≤ 60 s」要求;CLI 模式(`npx gltf-transform`)适合在 GitHub Actions runner 上跑回归测试。
**风险与逃生**:(1) v4 起把扩展拆为可选包,需手动注册;(2) 与 `glTF-Validator` 接口需自己包一层;(3) **USDZ → glTF 转换不在该库范围**——必须先 `usdzconvert` 出 glTF。作者若停更则直接 fork。
### 3.2 [`pmndrs/react-three-fiber`](https://github.com/pmndrs/react-three-fiber) + [`pmndrs/drei`](https://github.com/pmndrs/drei) —— Web 渲染事实标准
**评估结论****强推荐**,与 Next.js 14 App Router 适配良好。
**性能边界**:单房间 5 MB `.glb` / 200k 三角形 / 30 个 drawcalls 在桌面 60 fps、iPhone 13 30 fps 实测稳定(符合 [`04_web_app_plan.md`](04_web_app_plan.md) §3.3 性能预算)。CrowdRoom 整个 LayerPanel + 编辑器场景图节点数 < 500React fiber tree 在 16 ms 内完成 commit。
**移动 Safari 兼容性**iOS 16+ 全功能;drei 的 `useGLTF` 已内置 KTX2 / Meshopt / Draco loader。**坑点**drei `<Environment preset>` 在 iOS Safari < 16.5 有 HDR 解析 bug,已在 v9.x 修复 → MVP 锁版 drei ≥ 9.105。
**SSR 兼容性**:R3F 本身不支持 SSR(依赖 WebGL Context),Next.js 端必须 `dynamic(() => import('@/components/RoomViewer'), { ssr: false })`。详情页 SEO 通过 SSR meta + 静态 `thumbnail.webp` 实现([`04_web_app_plan.md`](04_web_app_plan.md) §9)。drei 的 `<Canvas frameloop="demand">` 配合 IntersectionObserver 可让屏外房间停止渲染(首页瀑布流性能关键)。
**风险与逃生**(1) v9 → v10 升级有 break(2) 与 Next.js 15 / React 19 兼容性需观察;(3) bundle size 控制需 tree-shake——首页 lazy-import 即可。最坏情况降级到原生 Three.js。
### 3.3 Apple USD 工具链(`usdzconvert` / `usdzip` / `ModelIO`)—— 转码不可绕开的 macOS 依赖
**评估结论**:**有条件可用**,必须在 GitHub Actions macOS runner 上跑。
**Apple `usdzconvert`Python 脚本)**:随 Xcode 15 提供 `xcrun usdzconvert`,可在 macOS 14+ runner 直接调用;接受输入 `.obj / .gltf / .fbx`,输出 `.usdz`;反向(USDZ → glTF)需要 `usdzip` 解包 + `usdcat --flatten`,最后用 [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) Python 绑定导出 glTF。
**GitHub Actions macOS runner 可用性**`macos-14`Apple Silicon)已稳定,10 分钟启动;定价比 Linux runner 贵 10×(约 $0.16/min vs $0.008/min)→ 仅在「转码失败回退」场景使用,主流量走 USD-Python 在 Linux runner 跑。**实测**30 MB `.usdz``macos-14` 上转 `.gltf` ≈ 12 s + 上下文启动 90 s。
**ModelIOiOS 端使用)**iOS 9+ 系统框架,支持 `.usdz` 读 + 部分写;CrowdRoom iOS 端**只用读功能**(提取贴图 → 端侧脱敏),重打包交给 Worker 端 macOS runner。
**风险与逃生**(1) Apple 不承诺 `usdzconvert` 后向兼容(Xcode 16 可能改 CLI 接口);(2) macOS runner 在 CN 区有时排队;(3) USD-Python 对 `.usdz` 嵌入纹理的处理偶有 corner case。**逃生通道**:完全切到 OpenUSD Linux 构建,放弃 Apple `usdzconvert` 路径——成本是 Worker 镜像变大(含 USD 编译产物 ≈ 600 MB)。
### 3.4 [`supabase-community/supabase-swift`](https://github.com/supabase-community/supabase-swift) —— iOS 端 BaaS 适配器
**评估结论**:**推荐使用**,但需关注 Realtime 后台稳定性。
**功能覆盖**v2.x 起完整对应 Supabase 云端的 Auth / PostgREST / Storage / Realtime / Functions 5 件套;Sign in with Apple OAuth flow 原生支持([`03_ios_app_plan.md`](03_ios_app_plan.md) §2 IA 强需求);presigned URL 用 Storage SDK 直签,与 [`02_api_contract.md`](02_api_contract.md) §2.1 E-02/E-03 直传规范对齐。
**Realtime 后台稳定性**iOS App 进入后台 30 s 后系统暂停 WebSocket(除非启用 `audio` / `voip` 后台模式,CrowdRoom 不应申请这些特权)。**对策**:(1) App 前台化时主动查询 `room_versions.status`(对应 [`02_api_contract.md`](02_api_contract.md) iOS-X4 契约的 reconciliation 补丁);(2) APNs 兜底转码完成通知。本评估纠正 iOS-X4 字面「禁止轮询」的过严要求——**前台一次性 reconciliation 不算轮询**,应纳入 §6 W5 review。
**SDK 与服务端兼容**supabase-swift v2.x 对应 GoTrue v2.x + PostgREST v12.x + Realtime v2.x,与 Supabase Cloud 滚动一致;自托管时需锁定 [`supabase/supabase`](https://github.com/supabase/supabase) Docker 镜像版本与 swift SDK 在同一 minor 范围。
**风险**(1) Swift Concurrency 要求 iOS 15+,与 RoomPlan 的 iOS 16+ 一致,**不增加门槛**(2) Sentry-Cocoa 与 supabase-swift 在 cold start 都 hook URLSession,需测试无冲突。
### 3.5 [`infinitered/nsfwjs`](https://github.com/infinitered/nsfwjs) —— 内容审核关键拼图
**评估结论****MVP 推荐**,放 Worker 而非 Edge Function。
**模型大小**:默认 MobileNetV2 模型 ≈ 4.2 MBINT8 量化版 ≈ 1.5 MB),加载到 TensorFlow.js runtime 后内存占用 ≈ 80 MB。
**推理速度**:单张 512×512 缩略图,Node.js + `@tensorflow/tfjs-node`CPU)≈ 80 msWorker 跑批量 8 张 ≈ 500 msDeno Edge Function(无 `tfjs-node`,纯 JS backend)≈ 600 ms / 张 → **超 Edge Function 10 s 软超时风险**
**误判率**:官方报告 Top-1 准确率 93%5 类:drawings / hentai / neutral / porn / sexy);CrowdRoom 场景(房间扫描)误判主要来源是床上人像油画 / 抽象艺术海报 → 阈值 `porn + hentai > 0.5` 时漏报率 < 2%,误报率 < 5%。**对策**:误报走 [`/admin/reports`](04_web_app_plan.md) 人工二审(R-17 路由),不直接下架。
**能否在 Edge FunctionDeno)中运行**:技术上 `tensorflow/tfjs` 有 Deno 适配,但模型加载耗时 + 内存峰值不适合 Edge Function 冷启动场景。**决策**NSFW 检测放在转码 Worker T-6(缩略图生成之后),失败不阻塞 ready 但写入 `rooms.moderation_signals` 字段,由 R-17 审核台显示。
**风险**(1) 训练数据集 Yahoo OpenNSFW 的派生使用合规边界需法务确认;(2) 模型核心 2020 年发布,需定期评估是否换 OpenNSFW2 或专业服务。
---
## 4. 项目仓库结构(Monorepo 决策)
### 4.1 仓库布局(可直接 mkdir 的目录树)
```
crowdroom/ # GitHub rootpnpm + Turborepo
├── apps/
│ ├── ios/ # Xcode 工程;SwiftPM 管依赖
│ │ ├── CrowdRoom.xcodeproj
│ │ ├── CrowdRoom/ # Swift 源码:Scan / Upload / RealtimeListen
│ │ ├── CrowdRoomTests/
│ │ ├── Package.swift # SPMsupabase-swift / sentry-cocoa / GRDB.swift
│ │ ├── PrivacyInfo.xcprivacy
│ │ └── fastlane/ # TestFlight 自动化
│ ├── web/ # Next.js 14 App Router
│ │ ├── app/ # 17 条路由(见 04_web_app_plan.md §1.1
│ │ │ ├── (marketing)/ # R-01 / R-13 / R-15
│ │ │ ├── r/[room_id]/ # R-02 / R-03 / R-12embed
│ │ │ ├── remix/[remix_id]/
│ │ │ ├── u/[username]/
│ │ │ ├── me/ # R-10 / R-11 / R-16
│ │ │ ├── admin/reports/ # R-17role=admin only
│ │ │ ├── search/
│ │ │ ├── assets/
│ │ │ ├── api/og/r/[room_id]/ # OG 卡片
│ │ │ └── auth/callback/
│ │ ├── components/
│ │ │ ├── viewer/ # RoomViewer + LayerPanel
│ │ │ ├── remix/ # MaterialSlotPanel + FurnitureSwapPanel
│ │ │ └── ui/ # shadcn copy
│ │ ├── stores/ # Zustand: layer-store / overlay-draft
│ │ ├── lib/ # supabase client + react-query
│ │ ├── messages/ # next-intl: zh-CN.json / en-US.json
│ │ ├── public/
│ │ ├── tests/ # Playwright E2E
│ │ ├── next.config.mjs
│ │ ├── tailwind.config.ts
│ │ └── package.json
│ └── worker/ # Node.js 转码 worker + BullMQ consumer
│ ├── src/
│ │ ├── pipeline/ # T-1 ~ T-9 步骤(见 02_api_contract.md §3.2
│ │ │ ├── fetch-source.ts
│ │ │ ├── usdz-to-gltf.ts
│ │ │ ├── compress-mesh.ts
│ │ │ ├── tag-layers.ts
│ │ │ ├── build-manifest.ts
│ │ │ ├── render-thumbnail.ts
│ │ │ ├── upload-output.ts
│ │ │ └── notify-done.ts
│ │ ├── nsfw/ # NSFWJS 包装
│ │ ├── queue/ # BullMQ consumer
│ │ └── index.ts
│ ├── Dockerfile # node:20-slim + chromium + sharp deps
│ ├── tests/
│ └── package.json
├── packages/
│ ├── shared-types/ # Zod schemas: layer_manifest / remix_overlay / view_state
│ ├── viewstate-codec/ # gzip+base64url 编解码(Web + Edge 共用,落实 Web-Y4
│ ├── eslint-config/
│ └── tsconfig/ # 共享 tsconfig.base.json
├── supabase/ # supabase CLI 本地工程
│ ├── config.toml
│ ├── migrations/ # SQL 迁移(落地 01_data_schema.md §3 DDL
│ ├── functions/ # 11 个 Edge Function
│ │ ├── upload-init/ # E-01
│ │ ├── upload-complete/ # E-04
│ │ ├── transcode-done/ # E-05
│ │ ├── view-state/ # E-10
│ │ ├── remix-create/ # E-11
│ │ ├── like-toggle/ # E-12
│ │ ├── report/ # E-14
│ │ ├── quota/ # E-15
│ │ ├── room-delete-with-snapshot/ # E-17
│ │ ├── account-delete/ # E-18
│ │ ├── account-export/ # E-19
│ │ └── _shared/ # 共享 deno modulesrate-limit / auth-guard / sensitive-word
│ └── seed.sql # 资产库种子集 + reviewer 账号
├── scripts/
│ ├── seed-assets/ # Quaternius / Poly Haven 资产归一化
│ ├── smoke/ # 11 条 X/Y 契约 smoke test
│ └── release.sh
├── docs/ # 软链到 plans/CrowdRoom/
├── .github/workflows/
│ ├── ci-web.yml # Vitest + Playwright + Lighthouse CI
│ ├── ci-ios.yml # SwiftLint + xcodebuild + macOS runner usdzconvert smoke
│ ├── ci-worker.yml # Vitest + Docker build + Trivy scan
│ ├── ci-supabase.yml # supabase db lint + functions deploydry-run
│ └── release.yml # semantic-release
├── docker-compose.yml # 本地全栈:Supabase + Redis + Worker + MinIO(撤退栈)
├── turbo.json # Turborepo 任务依赖图
├── pnpm-workspace.yaml # workspace: apps/* + packages/*
├── .pre-commit-config.yaml # SwiftLint + ESLint + commitlint
├── .gitignore
├── README.md
└── LICENSE # MIT(与所有依赖兼容)
```
### 4.2 Monorepo 决策(pnpm + Turborepo,不分仓)
选择 [`vercel/turborepo`](https://github.com/vercel/turborepo) + [pnpm workspace](https://pnpm.io/workspaces) 而非多仓的 **3 条理由**
1. **契约即代码**[`packages/shared-types/`](packages/shared-types/) 的 Zod schema 同时被 Web、Worker、Edge Function 导入;分仓会导致「客户端 PR 合了、服务端 PR 还没合」的 schema 漂移——而本设计 [`02_api_contract.md`](02_api_contract.md) §7 已有 40 条错误码,任何字段错位都会立即在 CI 端到端测试中爆掉。
2. **统一构建缓存**Turborepo 的 `--filter``pnpm turbo build --filter=web` 只构建被改动影响的 packages;CI 时间从 12 分钟降到 3 分钟。
3. **撤退路径同栈一键起**[`docker-compose.yml`](docker-compose.yml) 在 root 即可启动 Supabase 自托管 + Worker + Redis,不需要跨仓 git checkout——直接呼应 §2.8 自托管承诺。
### 4.3 Turborepo `turbo.json` 任务图(核心)
```json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] },
"lint": { "outputs": [] },
"test": { "dependsOn": ["^build"], "outputs": ["coverage/**"] },
"test:e2e": { "dependsOn": ["build"], "outputs": ["playwright-report/**"] }
}
}
```
---
## 5. W0 准备工作(启动前 1 周)
> 进入 W1 之前必须勾完以下 14 项;任何一项未到位都会在 W1 第 1 天卡住命令链。
### 5.1 账号与配额
- [ ] **GitHub Organization** 创建 `crowdroom`,建立 `engineering` teamwriters+ `dpo` teamreaders),开启 SSO。
- [ ] **Vercel** Team 账号;绑定 `crowdroom` org;启用 Edge Functions(默认开)。
- [ ] **Supabase** 项目创建(免费档 Pro 计划备选);记录 `project-ref` / `service_role_key` 入 1Password。
- [ ] **Cloudflare** 账号 + R2 Bucket`crowdroom-public` + `crowdroom-private`);同时启 CDN cache rule。
- [ ] **Apple Developer** 99 USD/年订阅;TestFlight 内测组 + App ID `app.crowdroom.ios` + RoomPlan entitlement。
- [ ] **Sentry**Cloud free tier)创建 3 项目:`crowdroom-web` / `crowdroom-ios` / `crowdroom-worker`,共享同一 org。
- [ ] **PostHog** Cloud 项目;导入 funnel 草稿(注册 → 扫描 → 上传 → 浏览 → 点赞)。
### 5.2 域名与基础设施
- [ ] **域名注册**`crowdroom.app`(首选)或 `crowdroom.io` 备选;同时注册 `.cn` 防御性持有。
- [ ] **DNS** 托管到 Cloudflare;预创建 `app.` / `api.` / `cdn.` / `embed.` 4 个 CNAME 占位。
- [ ] **邮箱**Postmark / Resend 二选一开源 SMTP,或 [`maddevsio/aiscanner`](https://github.com/maddevsio/aiscanner) 自建 Mailcow):`team@` / `privacy@` / `abuse@`[`README.md`](README.md) §6 占位邮箱)。
### 5.3 GitHub Repo 配置
- [ ] **创建 monorepo** `crowdroom/crowdroom`private MVP 阶段,M1 验收后转 public)。
- [ ] **branch protection**`main` 要求 ≥ 1 review + CI 绿;`dev` 允许 admin override。
- [ ] **Dependabot** 配置 `.github/dependabot.yml`weekly 扫 npm / Swift Packages / GitHub Actions。
- [ ] **GitHub Secrets** 注入:`SUPABASE_ACCESS_TOKEN` / `VERCEL_TOKEN` / `CF_R2_ACCESS_KEY` / `CF_R2_SECRET_KEY` / `APPLE_API_KEY_BASE64` / `SENTRY_AUTH_TOKEN` / `POSTHOG_PROJECT_API_KEY`
### 5.4 任务管理
- [ ] **GitHub Projects v2** Board:列 = Backlog / W1 / W2 / ... / W8 / Done;标签 = `team:ios` / `team:web` / `team:backend` / `risk:P0/P1`
- [ ] **Linear / Notion 二选一** 作为长程线索板;与 GitHub Projects 通过 [`linear/synchronize-with-github`](https://linear.app/docs/github) 双向同步(可选)。
---
## 6. W1-W8 命令级实施步骤
> 所有命令均为可直接执行(pnpm / supabase / xcodebuild / docker),不含伪命令。
> 每周给出:本周目标、关键交付物、可执行命令骨架。
### 6.1 W1 — 仓库与基础设施初始化
**本周目标**:把 monorepo 骨架、CI、本地 docker-compose 自托管栈、Web 脚手架、Worker 脚手架同时立起来。
**关键交付物**
- monorepo 跑通 `pnpm i` + `pnpm turbo build` 全绿
- `docker-compose up` 一键启动 Supabase + MinIO + Redis
- Vercel preview 可访问 web hello world
- CI 三条 workflow 全绿
**命令骨架**(≈ 18 条):
```bash
# 1. 创建 monorepo 根
mkdir -p crowdroom && cd crowdroom
pnpm init && pnpm add -D turbo @changesets/cli typescript prettier eslint
git init && git remote add origin git@github.com:crowdroom/crowdroom.git
echo "node_modules\n.next\ndist\n.turbo\n.env*" > .gitignore
# 2. workspace 与 Turborepo
cat > pnpm-workspace.yaml <<EOF
packages:
- "apps/*"
- "packages/*"
EOF
cat > turbo.json <<'EOF'
{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] }, "lint": {}, "test": { "dependsOn": ["^build"] } } }
EOF
# 3. Web app 脚手架(Next.js 14 + Tailwind + App Router
pnpm create next-app apps/web --typescript --tailwind --app --use-pnpm --src-dir=false --import-alias "@/*"
cd apps/web
pnpm add @supabase/ssr @supabase/supabase-js @tanstack/react-query zustand
pnpm add three @react-three/fiber @react-three/drei
pnpm add lucide-react next-intl posthog-js @sentry/nextjs
pnpm add react-hook-form zod @hookform/resolvers
pnpm add -D @types/three @playwright/test
npx shadcn-ui@latest init
npx shadcn-ui@latest add button dialog slider dropdown-menu sheet
cd ../..
# 4. Worker 脚手架
mkdir -p apps/worker && cd apps/worker
pnpm init
pnpm add @gltf-transform/core @gltf-transform/extensions @gltf-transform/functions
pnpm add sharp bullmq ioredis @sentry/node nsfwjs @tensorflow/tfjs-node
pnpm add playwright
pnpm add -D typescript tsx vitest @types/node
npx playwright install chromium
cd ../..
# 5. 共享包
mkdir -p packages/shared-types packages/viewstate-codec packages/eslint-config packages/tsconfig
cd packages/shared-types && pnpm init && pnpm add zod && cd ../..
cd packages/viewstate-codec && pnpm init && pnpm add pako && cd ../..
# 6. Supabase 本地工程
brew install supabase/tap/supabase
supabase init
# 写 docker-compose.yml(含 supabase 自托管 + redis + minio + worker
curl -fsSL https://raw.githubusercontent.com/supabase/supabase/master/docker/docker-compose.yml -o docker-compose.yml
# 7. CI 三条 workflow
mkdir -p .github/workflows
# ci-web.yml / ci-worker.yml / ci-ios.yml 三个文件(含 actions/checkout + setup-node + cache + trivy
# 8. pre-commit + commitizen
pip install pre-commit
pre-commit install
pnpm add -Dw @commitlint/cli @commitlint/config-conventional commitizen cz-conventional-changelog
# 9. 首次部署
vercel link --yes --project crowdroom-web
vercel --prod --cwd apps/web
docker-compose up -d
git add -A && git commit -m "chore: scaffold monorepo (W1)" && git push -u origin main
```
### 6.2 W2 — Supabase Schema 与 RLS(落地 [`01_data_schema.md`](01_data_schema.md) v0.2
**本周目标**9 张表 DDL + 全部 RLS policy + tsvector 索引上线;本地 `supabase db reset` 与云端 `db push` 均通过。
**关键交付物**
- 9 张表 + 11 个 trigger 全部 migration 通过
- RLS 单测覆盖每张表至少 3 个 caseown / public / forbidden
- pg_cron 定时任务上线:`uploading` 超 30 分钟标 `failed`、softdelete T+30 触发 E-18
**命令骨架**(≈ 14 条):
```bash
# 1. 启动本地栈
supabase start # 起 docker stackpostgres + studio + storage + edge + realtime
supabase status # 记录本地 anon_key / service_role_key
# 2. 写 schema migration
supabase migration new init_schema
# 把 01_data_schema.md §3 的 9 张表 DDL 粘进 supabase/migrations/<ts>_init_schema.sql
supabase migration new init_rls
# 粘 RLS policy
supabase migration new init_tsvector
# 粘 tsvector + GIN 索引
supabase migration new init_pgcron
# 粘 pg_cron 任务
# 3. 本地校验
supabase db reset
psql "postgresql://postgres:postgres@localhost:54322/postgres" -c "\dt public.*"
# 期待看到 9 张表
# 4. 跑 RLS 单测(用 pgTAP
psql -f tests/db/rls_test.sql
# 5. 部署到 Supabase Cloud
supabase link --project-ref <project-ref>
supabase db push
supabase db lint
# 6. 验证云端
curl -s "https://<ref>.supabase.co/rest/v1/rooms?select=id&limit=1" \
-H "apikey: $SUPABASE_ANON_KEY" -H "Authorization: Bearer $SUPABASE_ANON_KEY"
# 7. 提交 PR
git checkout -b feat/w2-schema && git add -A && \
git commit -m "feat(db): land 9 tables + RLS + tsvector (W2)" && git push -u origin HEAD
```
### 6.3 W3 — Edge Functions(落地 [`02_api_contract.md`](02_api_contract.md) 11 个 Edge 端点)
**本周目标**11 个 Edge Function 在本地跑通 + 5 个 MVP 必须端点(E-01/04/05/10/15)上线生产。
**关键交付物**
- E-01 `upload-init` 返回正确 presigned URL
- E-05 `transcode-done` 与 Worker 联调通过
- 40 条错误码骨架已 stub 完成(即使部分端点 stub `INTERNAL_ERROR`
**命令骨架**(≈ 15 条):
```bash
# 1. 创建 11 个 Edge Function 骨架
for fn in upload-init upload-complete transcode-done view-state remix-create \
like-toggle report quota room-delete-with-snapshot \
account-delete account-export; do
supabase functions new $fn
done
# 2. 共享模块(rate-limit / auth-guard / sensitive-word
mkdir -p supabase/functions/_shared
# 写 supabase/functions/_shared/rate-limit.ts(基于 Upstash Ratelimit 或 in-memory KV
# 写 supabase/functions/_shared/auth-guard.ts(解 JWT + RLS 校验包装)
# 3. E-01 upload-init 实现
# 编辑 supabase/functions/upload-init/index.ts:调用 storage.createSignedUploadUrl()
supabase functions serve upload-init --env-file ./supabase/.env.local
# 在另一个终端
curl -X POST http://localhost:54321/functions/v1/upload-init \
-H "Authorization: Bearer $USER_JWT" \
-H "Content-Type: application/json" \
-d '{"title":"测试客厅","tags":["北欧"],"visibility":"public","bytes_source":12345678}'
# 4. 部署 5 个 MVP 必须端点到云端
for fn in upload-init upload-complete transcode-done view-state quota; do
supabase functions deploy $fn --no-verify-jwt=false
done
# 5. 写 contract test(基于 packages/shared-types 的 Zod schema
cd apps/web && pnpm vitest run tests/contract/edge-functions.test.ts
# 6. 提交 PR
git checkout -b feat/w3-edge-fns && git add -A && \
git commit -m "feat(edge): 11 functions scaffolded, 5 deployed (W3)" && git push -u origin HEAD
```
### 6.4 W4 — Web 详情页 + 4 层切换(落地 [`04_web_app_plan.md`](04_web_app_plan.md) §3-§4
**本周目标**`/r/[room_id]` 详情页能拉 `canonical.glb` + `layer_manifest.json`,4 层独立 toggle 可见性、桌面 ≥ 60 fps。
**关键交付物**
- `RoomViewer` 组件(dynamic import + ssr:false
- `LayerPanel` 组件(4 层固定 ID + 👁/🔒/不透明度/单节点 toggle)
- Lighthouse CI 桌面性能分 ≥ 85
- 集成 Sentry + PostHog
**命令骨架**(≈ 12 条):
```bash
# 1. 创建路由
cd apps/web
mkdir -p app/r/\[room_id\] components/viewer components/layer-panel stores
# 2. 写 stores/layer-store.tsZustand
# 写 components/viewer/RoomViewer.tsxR3F Canvas + 4 group + useGLTF
# 写 components/layer-panel/LayerPanel.tsx(见 04_web_app_plan.md §4.5 骨架)
# 3. 接入 manifest schemaZod
# 在 packages/shared-types/manifest.ts 定义 LayerManifestSchemaapps/web 与 apps/worker 共用
# 4. 性能预算自动化
pnpm add -D @lhci/cli
echo "module.exports = { ci: { collect: { url: ['http://localhost:3000/r/demo'] }, assert: { assertions: { 'categories:performance': ['error', { minScore: 0.85 }] } } } };" > apps/web/lighthouserc.cjs
# 5. 测试
cd apps/web && pnpm dev &
sleep 5 && pnpm lhci autorun
# 6. 接入 Sentry / PostHog
npx @sentry/wizard@latest -i nextjs
# 编辑 instrumentation.ts + sentry.client.config.ts
pnpm add posthog-js
# 在 app/providers.tsx 初始化 posthog.init()
# 7. Playwright E2E(首个 case
pnpm exec playwright test tests/e2e/room-detail.spec.ts
# 8. 部署 preview
vercel --cwd apps/web
# 9. PR
git checkout -b feat/w4-room-detail && git add -A && \
git commit -m "feat(web): /r/[room_id] + 4-layer toggle (W4)" && git push -u origin HEAD
```
### 6.5 W5 — iOS App 扫描 + 端侧脱敏(落地 [`03_ios_app_plan.md`](03_ios_app_plan.md) §4-§5
**本周目标**iOS App 通过 RoomPlan 扫房 + Vision 端侧人脸检测 + 三段式上传 + PrivacyManifest 全链路跑通。
**关键交付物**
- TestFlight 内测包可下发给 ≥ 5 名 dogfooder
- 端侧脱敏 p95 ≤ 15 siPhone 12 Pro
- iOS-X1~X5 五条契约自动化测试
**命令骨架**(≈ 15 条):
```bash
# 1. 创建 Xcode 工程
cd apps/ios
xcodegen generate # 或手动 Xcode → New Project → iOS App "CrowdRoom"
# 设置 deployment target iOS 16.0、Bundle ID app.crowdroom.ios
# 2. SPM 依赖
# 在 Xcode 中 File → Add Packages 添加:
# - https://github.com/supabase-community/supabase-swift
# - https://github.com/getsentry/sentry-cocoa
# - https://github.com/PostHog/posthog-ios
# - https://github.com/groue/GRDB.swift
# - https://github.com/apple/swift-async-algorithms
# 3. Entitlements + Info.plist
# RoomPlan 不需要 entitlement,但需要 NSCameraUsageDescription / NSPhotoLibraryUsageDescription
# NSLocationWhenInUseUsageDescription 加上「城市级 5km 网格」文案(呼应 C-NEW-3
# 4. SwiftLint
brew install swiftlint
echo "included:\n - CrowdRoom\nexcluded:\n - Pods" > .swiftlint.yml
swiftlint
# 5. 关键源文件骨架
# CrowdRoom/Scanning/RoomCaptureViewModel.swift(封装 RoomCaptureSession + RoomCaptureView
# CrowdRoom/Redaction/FaceRedactor.swiftVision VNDetectFaceRectanglesRequest + CIGaussianBlur
# CrowdRoom/Upload/ThreeStageUploader.swiftupload-init → PUT → upload-complete
# CrowdRoom/Realtime/TranscodeListener.swiftRealtime channel + 前台 reconciliation 补丁)
# 6. PrivacyManifest
cat > CrowdRoom/PrivacyInfo.xcprivacy <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>NSPrivacyTracking</key><false/>
<key>NSPrivacyCollectedDataTypes</key><array>
<dict><key>NSPrivacyCollectedDataType</key><string>NSPrivacyCollectedDataTypeDeviceID</string>
<key>NSPrivacyCollectedDataTypeLinked</key><false/>
<key>NSPrivacyCollectedDataTypeTracking</key><false/></dict>
</array>
</dict></plist>
EOF
# 7. 端侧脱敏性能测试
xcodebuild test \
-scheme CrowdRoom -destination "platform=iOS Simulator,name=iPhone 15 Pro" \
-only-testing CrowdRoomTests/FaceRedactorPerfTests
# 8. fastlane TestFlight
gem install fastlane
fastlane init
# 编辑 Fastfilelane :beta do build_app + upload_to_testflight end
APP_STORE_CONNECT_API_KEY="$APPLE_API_KEY_BASE64" fastlane beta
# 9. PR
cd ../..
git checkout -b feat/w5-ios-scan && git add -A && \
git commit -m "feat(ios): RoomPlan + face redaction + 3-stage upload (W5)" && git push -u origin HEAD
```
### 6.6 W6 — 转码 Worker(落地 [`02_api_contract.md`](02_api_contract.md) §3
**本周目标**Worker 容器在 BullMQ 队列里消费转码任务,完成 T-1~T-9 9 步流水;p95 ≤ 60 s。
**关键交付物**
- Docker 镜像 `ghcr.io/crowdroom/worker:0.1.0`
- 单 worker 节点处理 ≥ 100 任务零内存泄漏
- glTF-Validator 校验通过率 100%
**命令骨架**(≈ 16 条):
```bash
# 1. 进入 worker 包
cd apps/worker
# 2. 安装额外依赖
pnpm add @gltf-transform/cli @gltf-transform/extensions
pnpm add three # 用于 headless 渲染场景拼装
pnpm add @aws-sdk/client-s3 # 与 Supabase Storage / R2 / MinIO 三向兼容
# 3. 写 9 步 pipeline(参见 02_api_contract.md §3.2
# src/pipeline/fetch-source.ts # T-1
# src/pipeline/usdz-to-gltf.ts # T-2(先用 OpenUSD Python via child_processmacOS fallback usdzconvert
# src/pipeline/compress-mesh.ts # T-3gltf-transform draco + meshopt
# src/pipeline/tag-layers.ts # T-4
# src/pipeline/build-manifest.ts # T-5(输出后跑 glTF-Validator
# src/pipeline/render-thumbnail.ts # T-6playwright + three.js
# src/pipeline/nsfw-check.ts # T-6.5nsfwjs,结果写 moderation_signals
# src/pipeline/upload-output.ts # T-8
# src/pipeline/notify-done.ts # T-9
# 4. Dockerfile(含 chromium + libvips + python3 + openusd
cat > Dockerfile <<'EOF'
FROM node:20-bookworm
RUN apt-get update && apt-get install -y \
chromium libvips-dev python3 python3-pip \
&& pip3 install --break-system-packages usd-core
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN corepack enable && pnpm install --prod --frozen-lockfile
COPY . .
CMD ["node", "dist/index.js"]
EOF
# 5. 本地构建与运行
docker build -t crowdroom-worker:dev .
docker run --rm -e REDIS_URL=redis://host.docker.internal:6379 \
-e SUPABASE_URL=http://host.docker.internal:54321 \
-e SUPABASE_SERVICE_ROLE_KEY=$SVC_KEY \
crowdroom-worker:dev
# 6. 单测(用 KhronosGroup/glTF-Sample-Models 当夹具)
git submodule add https://github.com/KhronosGroup/glTF-Sample-Models tests/fixtures/gltf-samples
pnpm vitest run tests/pipeline/
# 7. Trivy 扫漏洞
trivy image crowdroom-worker:dev
# 8. 推到 GHCR
echo $GITHUB_TOKEN | docker login ghcr.io -u <user> --password-stdin
docker tag crowdroom-worker:dev ghcr.io/crowdroom/worker:0.1.0
docker push ghcr.io/crowdroom/worker:0.1.0
# 9. Fly.io 部署(或 Railway / 自建 VPS
brew install flyctl
flyctl launch --image ghcr.io/crowdroom/worker:0.1.0 --no-deploy
flyctl secrets set REDIS_URL=$REDIS_URL SUPABASE_URL=$SUPABASE_URL \
SUPABASE_SERVICE_ROLE_KEY=$SVC_KEY
flyctl deploy
# 10. 端到端 smoke
node scripts/smoke/upload-and-wait.mjs
# 11. PR
cd ../..
git checkout -b feat/w6-worker && git add -A && \
git commit -m "feat(worker): transcode pipeline T-1~T-9 (W6)" && git push -u origin HEAD
```
### 6.7 W7 — 材质替换 + Remix(落地 [`04_web_app_plan.md`](04_web_app_plan.md) §5-§7
**本周目标**:Remix 编辑器跑通;材质 ≤ 200 ms 热替换;overlay 自动保存 + 离线 IndexedDB 草稿;E-11 / E-17 联调。
**关键交付物**
- `/r/[room_id]/edit?fork=1` 编辑器可用
- 1 张材质热替换 p95 ≤ 200 ms
- Cypress / Playwright 跑通「换材质 + 发布 Remix」完整链路
**命令骨架**(≈ 12 条):
```bash
cd apps/web
# 1. 组件骨架
mkdir -p components/remix components/asset-picker
# components/remix/RemixEditor.tsx
# components/remix/MaterialSlotPanel.tsx(见 04_web_app_plan.md §5.2
# components/remix/FurnitureSwapPanel.tsx(见 04_web_app_plan.md §6.2
# components/asset-picker/AssetPickerMaterial.tsx
# stores/overlay-draft-store.tsZustand temporal middleware
# 2. 离线草稿(IndexedDB
pnpm add idb
# 3. 资产库种子加载
node ../../scripts/seed-assets/normalize.ts \
--source quaternius --target ../../supabase/seed-data/assets.json
# 4. 接 E-11 remix-create / PATCH remix-update
# 在 lib/edge-fns.ts 包装:createRemix({parent_version_id, title})
# overlay 自动保存防抖 3s + beforeunload 拦截
# 5. 单墙改色 + react-colorful
pnpm add react-colorful
# 6. Playwright E2E
cat > tests/e2e/remix-flow.spec.ts <<'EOF'
import { test, expect } from '@playwright/test';
test('material swap → publish', async ({ page }) => {
await page.goto('/r/demo-room-id/edit?fork=1');
await page.getByRole('button', { name: 'Material' }).click();
await page.getByText('Oak Natural').click();
await page.getByRole('button', { name: 'Publish' }).click();
await expect(page).toHaveURL(/\/remix\/.+/);
});
EOF
pnpm exec playwright test tests/e2e/remix-flow.spec.ts
# 7. 性能验证(材质热替换 ≤ 200ms)
pnpm exec playwright test tests/perf/material-swap.spec.ts
# 8. 部署 preview
vercel --cwd .
# 9. PR
cd ../..
git checkout -b feat/w7-remix && git add -A && \
git commit -m "feat(web): material/furniture swap + remix flow (W7)" && git push -u origin HEAD
```
### 6.8 W8 — 治理基础 + 部署上线
**本周目标**M1 DoD 全部勾绿;NSFWJS 接入;`/admin/reports` 工作台可用;closed beta(≤ 50 人)发布。
**关键交付物**
- 生产环境冷启动测试 + Sentry 0 P0
- `/admin/reports` 路由 admin role 鉴权
- TestFlight 内测 ≥ 50 用户上线
**命令骨架**(≈ 14 条):
```bash
# 1. 接入 NSFWJS 到 Worker 的 T-6.5
cd apps/worker
# src/pipeline/nsfw-check.ts 写完并加入 src/index.ts pipeline 注册
# 2. Edge Function E-14 report 完善
cd ../..
# supabase/functions/report/index.ts 写入 reports 表 + 通知 admin
# 3. /admin/reports 路由
cd apps/web
mkdir -p app/admin/reports
# 写 middleware.ts 检查 auth.users.app_metadata.role === 'admin'
# 写 app/admin/reports/page.tsx(工单队列 + 判定按钮)
# 4. 添加 admin role 给 reviewer 账号
psql "$DATABASE_URL" -c \
"UPDATE auth.users SET raw_app_meta_data = jsonb_set(raw_app_meta_data, '{role}', '\"admin\"') WHERE email='reviewer@crowdroom.app';"
# 5. 法律文档(MDX
# apps/web/app/legal/privacy/page.mdx → 复制 plans/CrowdRoom/09_privacy.md
# apps/web/app/legal/terms/page.mdx
# apps/web/app/legal/community/page.mdx → 引用 plans/CrowdRoom/10_governance.md
# 6. 生产环境部署
vercel --prod --cwd apps/web
supabase functions deploy --project-ref <ref>
flyctl deploy -a crowdroom-worker
# 7. Sentry 验证
curl -X POST "https://app.crowdroom.app/api/test-sentry"
# 进入 Sentry 项目确认 issue 已上报
# 8. PostHog funnel 验证
# 跑一遍「注册 → 扫描 → 上传 → 浏览 → 点赞」完整流程,进 PostHog 查 funnel
# 9. TestFlight 推 closed beta
fastlane beta
# 在 App Store Connect 把 build 加入「CrowdRoom Closed Beta」群组(≤ 50 用户)
# 10. 烟雾测试矩阵(覆盖 11 条契约 X1-X5 + Y1-Y6
pnpm -w smoke
# 11. tag + release
pnpm exec semantic-release
git tag v0.5-mvp
git push origin v0.5-mvp
# 12. 写发布通告(README → 写一个「CrowdRoom v0.5-mvp 闭门内测开启」简短公告)
```
---
## 7. 关键风险与开源依赖的「逃生通道」
> 每条列出:**如果 X 项目突然废弃 / 被收购 / license 变更,我们的迁移路径是 Y**。
> 与 [`ROADMAP.md`](ROADMAP.md) §4 风险登记册联动;本节聚焦「**开源 / 云依赖层面**」的逃生,不重复业务风险。
| # | 触发条件(依赖变脸) | 迁移路径 | 估计切换工时 |
|---|----------------------|----------|--------------|
| **E-1** | **Supabase 被收购 / 涨价 / 关停 Cloud** | 启 [`docker-compose.yml`](docker-compose.yml) 中的 [`supabase/supabase`](https://github.com/supabase/supabase) 自托管栈;DNS 切到自建 CaddyStorage 底层 driver 从 R2 改 [`minio/minio`](https://github.com/minio/minio)。supabase-swift / @supabase/supabase-js SDK URL 仅改 base URL**业务代码 0 修改** | 2 人周 |
| **E-2** | **Vercel 涨价 / 区域不可达** | Web App 切到 Cloudflare PagesNext.js 全功能支持)或自建 [`caddyserver/caddy`](https://github.com/caddyserver/caddy) + Node SSR serverEdge FunctionsOG endpoint)迁到 [`denoland/deno`](https://github.com/denoland/deno) Deno Deploy 或 Cloudflare Workers | 1.5 人周 |
| **E-3** | **gltf-transform 停更** | 直接 fork 到 `crowdroom/glTF-Transform`;核心需求(Draco / Meshopt / 节点重命名)只用到 ≤ 30% API,可自行维护;同时启动 [`google/draco`](https://github.com/google/draco) + [`zeux/meshoptimizer`](https://github.com/zeux/meshoptimizer) 直接调用做 Plan B | 1 人周 |
| **E-4** | **PostHog Cloud 涨价 / 关停** | 自托管 [`PostHog/posthog`](https://github.com/PostHog/posthog)(一行 helm install),或换 [`plausible/analytics`](https://github.com/plausible/analytics)(功能少但够用,funnel 需自建)| 1 人周(含数据迁移) |
| **E-5** | **Apple `usdzconvert` 在 GitHub Actions macOS runner 不可用** | 完全切到 [`PixarAnimationStudios/OpenUSD`](https://github.com/PixarAnimationStudios/OpenUSD) Linux 构建产物(pip install usd-core);Worker Dockerfile 已预装,无需新增运维 | 0.5 人周 |
| **E-6** | **Cloudflare R2 涨价 / 退出区域** | Storage 底层 driver 切 [`minio/minio`](https://github.com/minio/minio) 自托管 + Backblaze B2 异地灾备;CDN 切 [`bunnyway/bunny.net`](https://bunny.net) 或 Tencent EdgeOneCN 用) | 1.5 人周 |
| **E-7** | **NSFWJS 模型 license 变更 / 不更新** | 换 [`bumble-tech/private-detector`](https://github.com/bumble-tech/private-detector) 或自训模型;短期内禁用自动审核、强化人工审核 [`/admin/reports`](04_web_app_plan.md) 工作台 | 1 人周 |
| **E-8** | **Sentry FSL 协议恶化 / 价格暴涨** | 切到 [`glitchtip/glitchtip-backend`](https://gitlab.com/glitchtip/glitchtip-backend)(完全 MIT、Sentry SDK 协议兼容,0 代码改动)| 0.5 人周 |
| **E-9** | **Three.js / R3F 核心维护者跑路** | Three.js 已被 mrdoob / Don McCurdy 等多人维护,停更概率 < 1%;如发生则锁版本 + 自维护补丁;最终极方案降级到 [`google/model-viewer`](https://github.com/google/model-viewer) 提供静态房间预览(功能退化但不挂) | 2 人周(功能降级) |
| **E-10** | **GitHub Actions 价格暴涨 / 不可用** | 自托管 [`actions/runner`](https://github.com/actions/runner) on 自有 VPSHetzner / 腾讯云);macOS runner 改用真实 Mac mini in office | 1.5 人周 |
> **核心承诺再次声明**:本方案中**没有任何一个组件存在「无逃生通道」的硬依赖**——10 条逃生路径均已识别,迁移工时合计 ≤ 13 人周,相当于「2 个工程师 6.5 周可完成全栈迁离」。
---
## 8. 成本预算(MVP 阶段,月度)
> 假设:单房间 source `.usdz` 30 MB / canonical `.glb` 5 MB / 每用户月均扫 5 个房间、浏览 50 个房间。
| 服务 / 组件 | 免费额度 | 50 DAU 估算成本 | 500 DAU 估算成本 | 5 000 DAU 估算成本 | 何时该切自托管 |
|---|---|---|---|---|---|
| **Supabase Cloud**Pro | 500 MB DB + 1 GB Storage + 5 GB Egress + 500k Edge invocations | $0 / 月(仍在免费档) | $25 / 月(Pro 起步) | $200-400 / 月(Storage + Egress + Compute | 月费 > $500 时(≈ 5k DAU |
| **Cloudflare R2** | 10 GB Storage / 1M class A + 10M class B ops | $0 | $5(≈ 300 GB | $40(≈ 2 TB Storage | 月费 > $200 |
| **Vercel** | 100 GB Bandwidth / 1k build minutes / Hobby plan | $0 | $20 / 月(Pro seat | $80 / 月(Pro + Edge invocations | 月费 > $200 |
| **Fly.io Worker** | 3 shared vCPU + 256 MB(小)免费 | $0-5idle | $301× 1 vCPU 2 GB | $1503-4× 实例) | 月费 > $300 |
| **Apple Developer** | | $99 / 年(≈ $8.25 / 月) | 同左 | 同左 | 永远固定 |
| **Sentry Cloud** | 5k events / 月(Developer Plan | $0 | $26 / 月(Team | $80 / 月(含 transactions | 月费 > $100,切 GlitchTip |
| **PostHog Cloud** | 1M events / 月 + Session Replay | $0 | $0(仍免费档) | $50 / 月 | 月费 > $200 |
| **GitHub Actions** | 2000 分钟 / 月(含 macOS x10 倍率) | $0 | $30 / 月(含 macOS runner | $100 / 月 | 月费 > $300 |
| **域名 / DNS** | | $1 / 月 | $1 | $1 | |
| **合计(粗估)** | | **≈ $10 / 月** | **≈ $140 / 月** | **≈ $700-1 000 / 月** | |
> **决策红线**:月度成本 > $1 000 即触发「自托管启动」评估;本方案的撤退路径在 §7 已完整说明,自托管 5k DAU 月度成本(含 VPS + 流量)估算 ≈ $300-500,明确低于纯云。
---
## 9. 工时估算与人员配比
| 角色 | 周次投入 | 主要任务对应章节 |
|---|---|---|
| **iOS 工程师 × 1** | W5 全职 + W1/W3/W7 各 20% | §6 W5iOS 全部);其他周做 client SDK 联调 |
| **Web 工程师 × 1** | W1 / W4 / W7 全职 + 其他周 50% | §6 W1(脚手架)、W4(详情页+图层)、W7(Remix);W8 部署联调 |
| **全栈 / 后端 × 1** | W2 / W3 / W6 / W8 全职 + 其他周 50% | §6 W2Schema)、W3Edge Fn)、W6Worker)、W8(治理+部署) |
| **设计师 × 0.3** | W1-W4 各 ≈ 10 小时 | UI 设计稿、4 层面板视觉规范、shadcn 主题、OG 卡片模板 |
| **PM × 0.2** | 贯穿 8 周 | sprint planning、DoR/DoD 核对、risk register 维护、内测招募 |
| **法务 / DPO 兼职** | W2 / W8 各 4 小时 | 隐私政策审核、CC0 资产合规审查 |
**人周合计**iOS 1 × 8 × 0.55 + Web 1 × 8 × 0.7 + 后端 1 × 8 × 0.7 + 设计 0.3 × 4 + PM 0.2 × 8 ≈ **17 个有效人周 / 8 周自然周**(≈ 32 人周计入并行 + 切换 + 评审折扣后的真实工时)。
> 与 [`ROADMAP.md`](ROADMAP.md) §2.1 M1 DoD 的「10 项必须全过」对齐;任何角色缺位 ≥ 1 周即触发 M1 延期评估。
---
## 10. M1 验收标准(出口清单)
> 与 [`ROADMAP.md`](ROADMAP.md) §2.1 M1 DoD 严格对齐,并补充本执行方案特有的「开源工程化」验收点。
### 10.1 功能验收(12 条)
- [ ] **F-01** iOS App 通过 TestFlight 审核,可邀请 ≥ 100 用户
- [ ] **F-02** iOS 端能扫描房间、上传,**人脸端侧自动脱敏 p95 ≤ 15 s**iPhone 12 Pro
- [ ] **F-03** Web 端 `/` `/r/[room_id]` `/login` `/upload` `/u/[username]` 5 个核心路由可用
- [ ] **F-04** 4 层(walls/floor/furniture/materials)可独立 toggle 可见性,viewState 可分享
- [ ] **F-05** 1 张材质能在 Web 端 **p95 ≤ 200 ms** 热替换
- [ ] **F-06** 1 件家具能 OBB 自动对齐替换,超 1.5× 时有警告
- [ ] **F-07** Remix 端到端可用:fork → 编辑 → 自动保存 → 发布 → `/remix/[id]` 可见
- [ ] **F-08** [`02_api_contract.md`](02_api_contract.md) 19 个端点中 MVP 必须的 **13 个**E-01~E-13, E-15)线上可用
- [ ] **F-09** 转码 Worker **p95 ≤ 60 s**(含 USDZ → glb + Draco + manifest 生成)
- [ ] **F-10** **100 个并发上传**,转码成功率 ≥ 95%
- [ ] **F-11** NSFWJS 接入 `upload-complete` 后端流水,命中后写 `moderation_signals`,在 `/admin/reports` 队列可见
- [ ] **F-12** 治理:举报通道 E-14 可用,处罚阶梯 L1-L3 工具就绪
### 10.2 工程化验收(10 条)
- [ ] **G-01** 全栈 `docker-compose up` 一键本地启动(Supabase + Worker + Redis + MinIO 撤退栈)
- [ ] **G-02** CI 三条 workflow 全绿(`ci-web` / `ci-ios` / `ci-worker`),任何 PR < 10 分钟反馈
- [ ] **G-03** Lighthouse CI 桌面性能分 **≥ 85**(首页 + 详情页),移动 LCP ≤ 3 s
- [ ] **G-04** Trivy 扫描 Worker 镜像 0 个 HIGH/CRITICAL 漏洞
- [ ] **G-05** Playwright E2E 覆盖 [`02_api_contract.md`](02_api_contract.md) §8 的 **11 条契约**X1-X5 + Y1-Y6
- [ ] **G-06** Sentry 接入 iOS / Web / Worker 三端,0 P0 issue 持续 ≥ 7 天
- [ ] **G-07** PostHog funnel「注册 → 扫描 → 上传 → 浏览 → 点赞」5 步采集率 ≥ 30%
- [ ] **G-08** [`packages/shared-types/`](packages/shared-types/) 的 Zod schema 被 iOS / Web / Worker / Edge Function 4 端导入,无任何字段漂移
- [ ] **G-09** semantic-release 自动 tag + 写 [`CHANGELOG.md`](CHANGELOG.md)v0.5-mvp tag 上线
- [ ] **G-10** 所有依赖通过 Dependabot weekly 扫描;当前 0 个高危待修
### 10.3 合规验收(4 条)
- [ ] **C-01** [`09_privacy.md`](09_privacy.md) §6.1 的 8 项 GDPR MVP 必做项全部上线(含 PrivacyManifest、Cookie 通知、隐私政策页)
- [ ] **C-02** 公共资产库 ≥ 30 件家具 + ≥ 20 种 PBR**全部 CC0**;硬过滤拒收非 CC0 资产
- [ ] **C-03** 退场承诺页 `/legal/exit-promise` 上线([`10_governance.md`](10_governance.md) §10 公开数据 CC0 镜像承诺)
- [ ] **C-04** E-18 三阶段账号注销 + E-19 数据导出在生产环境可端到端跑通
### 10.4 内测验收(2 条)
- [ ] **B-01** 内部 dogfood ≥ 100 个真实房间上传无 P0 事故
- [ ] **B-02** Closed Beta(≤ 50 用户)发布后 1 周内 NPS ≥ 20
**合计 28 条验收点**——全部勾绿即 M1 出口。
---
## 11. 本章小结
| 关键产出 | 一句话 |
|----------|--------|
| **77 个 GitHub repo** | 覆盖 iOS / Web / Worker / BaaS / 审核 / 资产 / DevOps / 撤退 8 层,全部 ≥ 1k star + 宽松 license + 近 6 月活跃 |
| **5 个深度评估** | gltf-transform / R3F+drei / Apple USD / supabase-swift / NSFWJS 全部「**MVP 推荐 + 风险已识别 + 逃生路径明确**」 |
| **1 个 Monorepo** | pnpm + TurborepoiOS / Web / Worker / Edge Function / Schemas 同仓共版本;3 条理由 |
| **8 周 × ≈ 14 条命令** | W1 仓库初始化 → W8 closed beta;累计 **116 条可执行命令**,零伪命令 |
| **10 条逃生通道** | 任意单组件失效 ≤ 2 人周可迁移;全栈撤离 ≤ 13 人周 |
| **28 条 M1 验收点** | 功能 12 + 工程化 10 + 合规 4 + 内测 2,与 [`ROADMAP.md`](ROADMAP.md) §2.1 严格对齐 |
读完本章你应能:
- ✅ 给一个新工程师一份「**从 W1 第一条 `mkdir crowdroom` 命令到 W8 closed beta 发布**」的可执行清单
- ✅ 在任意云供应商变脸时 ≤ 2 周内完成栈迁移
- ✅ 用 < $200 / 月的成本支撑 500 DAU;切自托管后 < $500 / 月支撑 5k DAU
- ✅ 在 PR review 时按 §10 28 条 checkbox 逐条核对 M1 出口
---
**文档版本**v0.1 · 2026-05-19
**维护者**CrowdRoom 工程组
**下一步阅读**:完成 W0 §5 准备清单后 → 进入 §6.1 W1 第一条命令。