# 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 Functions(Deno)+ Worker(Node)+ Web(Next.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
apple/swift + supabase-community/supabase-swift] WEB[Web App
vercel/next.js + pmndrs/react-three-fiber] end subgraph BAAS[Supabase BaaS] AUTH[Auth
supabase/gotrue] DB[(Postgres 15
supabase/postgres)] STG[Storage
supabase/storage-api] EDGE[Edge Functions
denoland/deno] RT[Realtime
supabase/realtime] end subgraph WORKER[Transcode Worker] QUEUE[BullMQ
taskforcesh/bullmq] TRANS[gltf-transform
donmccurdy/glTF-Transform] USD[Apple usdzconvert
via macOS runner] SHOT[Headless render
microsoft/playwright] SHARP[Thumbnails
lovell/sharp] end subgraph MOD[审核 / 治理] NSFW[infinitered/nsfwjs] SENS[houbb/sensitive-word] end subgraph CDN_DEPLOY[CDN + 部署] R2[Cloudflare R2
S3 兼容] VERCEL[Vercel
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 定义;自托管可选 | Mixpanel(SaaS 锁定,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;端侧脱敏走 Vision;USDZ 重打包走 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 | Snyk(SaaS 锁定) | | 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 自动 PR;Renovate 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 Cloud(Auth + 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 + 编辑器场景图节点数 < 500,React fiber tree 在 16 ms 内完成 commit。 **移动 Safari 兼容性**:iOS 16+ 全功能;drei 的 `useGLTF` 已内置 KTX2 / Meshopt / Draco loader。**坑点**:drei `` 在 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 的 `` 配合 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。 **ModelIO(iOS 端使用)**: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 MB(INT8 量化版 ≈ 1.5 MB),加载到 TensorFlow.js runtime 后内存占用 ≈ 80 MB。 **推理速度**:单张 512×512 缩略图,Node.js + `@tensorflow/tfjs-node`(CPU)≈ 80 ms;Worker 跑批量 8 张 ≈ 500 ms;Deno 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 Function(Deno)中运行**:技术上 `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 root,pnpm + Turborepo ├── apps/ │ ├── ios/ # Xcode 工程;SwiftPM 管依赖 │ │ ├── CrowdRoom.xcodeproj │ │ ├── CrowdRoom/ # Swift 源码:Scan / Upload / RealtimeListen │ │ ├── CrowdRoomTests/ │ │ ├── Package.swift # SPM:supabase-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-12(embed) │ │ │ ├── remix/[remix_id]/ │ │ │ ├── u/[username]/ │ │ │ ├── me/ # R-10 / R-11 / R-16 │ │ │ ├── admin/reports/ # R-17(role=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 modules(rate-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 deploy(dry-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` team(writers)+ `dpo` team(readers),开启 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 < 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 个 case(own / public / forbidden) - pg_cron 定时任务上线:`uploading` 超 30 分钟标 `failed`、softdelete T+30 触发 E-18 **命令骨架**(≈ 14 条): ```bash # 1. 启动本地栈 supabase start # 起 docker stack:postgres + 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/_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 supabase db push supabase db lint # 6. 验证云端 curl -s "https://.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.ts(Zustand) # 写 components/viewer/RoomViewer.tsx(R3F Canvas + 4 group + useGLTF) # 写 components/layer-panel/LayerPanel.tsx(见 04_web_app_plan.md §4.5 骨架) # 3. 接入 manifest schema(Zod) # 在 packages/shared-types/manifest.ts 定义 LayerManifestSchema,apps/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 s(iPhone 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.swift(Vision VNDetectFaceRectanglesRequest + CIGaussianBlur) # CrowdRoom/Upload/ThreeStageUploader.swift(upload-init → PUT → upload-complete) # CrowdRoom/Realtime/TranscodeListener.swift(Realtime channel + 前台 reconciliation 补丁) # 6. PrivacyManifest cat > CrowdRoom/PrivacyInfo.xcprivacy <<'EOF' NSPrivacyTracking NSPrivacyCollectedDataTypes NSPrivacyCollectedDataTypeNSPrivacyCollectedDataTypeDeviceID NSPrivacyCollectedDataTypeLinked NSPrivacyCollectedDataTypeTracking 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 # 编辑 Fastfile:lane :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_process,macOS fallback usdzconvert) # src/pipeline/compress-mesh.ts # T-3(gltf-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-6(playwright + three.js) # src/pipeline/nsfw-check.ts # T-6.5(nsfwjs,结果写 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 --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.ts(Zustand 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 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 切到自建 Caddy;Storage 底层 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 Pages(Next.js 全功能支持)或自建 [`caddyserver/caddy`](https://github.com/caddyserver/caddy) + Node SSR server;Edge Functions(OG 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 EdgeOne(CN 用) | 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 自有 VPS(Hetzner / 腾讯云);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-5(idle) | $30(1× 1 vCPU 2 GB) | $150(3-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 W5(iOS 全部);其他周做 client SDK 联调 | | **Web 工程师 × 1** | W1 / W4 / W7 全职 + 其他周 50% | §6 W1(脚手架)、W4(详情页+图层)、W7(Remix);W8 部署联调 | | **全栈 / 后端 × 1** | W2 / W3 / W6 / W8 全职 + 其他周 50% | §6 W2(Schema)、W3(Edge Fn)、W6(Worker)、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 + Turborepo;iOS / 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 第一条命令。