67 KiB
title, date, draft, tags, categories
| title | date | draft | tags | categories | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案 | 2026-05-20 | false |
|
|
CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案
文档版本:v0.1 · 2026-05-19 承接设计:
README.md/00_overview.md/01_data_schema.md/02_api_contract.md/03_ios_app_plan.md/04_web_app_plan.md/09_privacy.md/10_governance.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 五条工程原则
- 开源优先 + 自托管兜底:每个云服务必须存在「同名开源项目自托管版」(例如 Supabase Cloud ↔
supabase/supabase自托管)。 - monorepo + workspace:iOS / Web / Worker / Schemas 同仓共版本,避免「契约漂移」。
- TypeScript 一统服务端:Edge Functions(Deno)+ Worker(Node)+ Web(Next.js)共享同一份
packages/shared-types/的 Zod schema。 - CDN-first:所有公开资产走 Cloudflare R2 + CDN,CrowdRoom 服务端永不承担静态分发流量。
- 可测试性即合规:CI 不绿不许合并;端到端测试覆盖
02_api_contract.md§8 的 11 条契约(X1-X5 + Y1-Y6)。
1.3 总体技术拓扑(每节点标注 GitHub repo)
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 |
≥ 1k | MIT | < 1 月 | 官方维护,覆盖 Auth / DB / Storage / Realtime / Functions 5 件套;与 Edge Function 鉴权同源 | 自己用 URLSession 包 PostgREST(不推荐) |
| HTTP 库(对比) | Alamofire/Alamofire |
≥ 40k | MIT | < 1 月 | 经典 HTTP 库;supabase-swift 已内置 URLSession,最终决策不引入 | 系统 URLSession |
| 崩溃 / 性能监控 | getsentry/sentry-cocoa |
≥ 1k | MIT | < 1 月 | 与 Web 端、Worker 共用 Sentry 项目,跨端 traceId 联查 | Bugsnag(闭源化风险) |
| 产品分析 | PostHog/posthog-ios |
≥ 1k | MIT | < 1 月 | 与 Web 端共享 funnel 定义;自托管可选 | Mixpanel(SaaS 锁定,pass) |
| Async 工具 | apple/swift-async-algorithms |
≥ 3k | Apache-2.0 | < 1 月 | AsyncSequence 组合子,处理 RoomPlan 增量回调流 |
Combine |
| 代码规范 | realm/SwiftLint |
≥ 18k | MIT | < 1 月 | 行业事实标准;CI 拒绝非 lint-clean 提交 | swift-format(规则较少) |
| 包管理(采用) | Swift Package Manager / apple/swift-package-manager |
≥ 9k | Apache-2.0 | < 1 月 | Xcode 原生支持;不引入 Carthage / CocoaPods | – |
| 包管理(对比) | Carthage/Carthage |
≥ 14k | MIT | < 6 月 | 仅作历史方案对比 | – |
| 持久化 | groue/GRDB.swift |
≥ 6k | MIT | < 1 月 | iOS 本地草稿 / 上传队列 SQLite 封装;崩溃恢复 | Core Data(学习曲线陡) |
| USD 解析(关键空白) | 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§1 PR-1「端侧脱敏不可降级」对齐:纹理(图像)端侧改,容器(USDZ 二进制)服务端重打。
2.2 Web App 层(16 个 repo)
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| Web 框架 | vercel/next.js |
≥ 100k | MIT | < 1 周 | App Router + SSR + OG endpoint + Edge runtime 一栈搞定 | remix-run/remix |
| React Three Fiber | pmndrs/react-three-fiber |
≥ 25k | MIT | < 1 月 | R3F 让「图层切换 / 换家具」用 React 思维表达 | 直接 mrdoob/three.js |
| Drei 工具集 | pmndrs/drei |
≥ 8k | MIT | < 1 月 | useGLTF / OrbitControls / Environment / Outline 全套现成 |
– |
| Three.js 底层 | mrdoob/three.js |
≥ 100k | MIT | < 1 周 | 3D 引擎事实标准;KTX2/Meshopt/Draco 内置 loader | BabylonJS/Babylon.js |
| 状态管理 | pmndrs/zustand |
≥ 45k | MIT | < 1 月 | 单 store + subscribeWithSelector 对 R3F 性能友好;无 Provider 树 |
pmndrs/jotai |
| Tailwind CSS | tailwindlabs/tailwindcss |
≥ 80k | MIT | < 1 周 | shadcn/ui 强绑定 | UnoCSS |
| shadcn/ui | shadcn-ui/ui |
≥ 70k | MIT | < 1 周 | Radix UI 封装,A11y 已做掉;可直接 copy 进项目无包锁定 | Mantine |
| Supabase SSR | supabase/auth-helpers(含 @supabase/ssr 子包) |
≥ 1k | MIT | < 1 月 | App Router 官方推荐;cookie 鉴权链路安全 | – |
| 服务端数据 | TanStack/query v5 |
≥ 40k | MIT | < 1 周 | 评论/点赞乐观更新;infinite query 用于瀑布流 | SWR |
| 图标 | lucide-icons/lucide |
≥ 11k | ISC | < 1 周 | shadcn/ui 默认同款;树摇彻底 | tabler/tabler-icons |
| 国际化 | amannn/next-intl |
≥ 3k | MIT | < 1 月 | App Router 友好,按路由段 /[locale]/... 切分 |
next-i18next |
| 产品分析 | PostHog/posthog-js |
≥ 1k | MIT | < 1 月 | 与 iOS 端共享同一份 funnel 定义 | Plausible |
| OG 卡片 | vercel/satori(含 @vercel/og) |
≥ 10k | MPL-2.0 | < 1 月 | SSR 阶段在 Edge runtime 生成 OG 图(非 3D 截图) | Resvg |
| 表单 + 校验 | react-hook-form/react-hook-form + colinhacks/zod |
≥ 39k / ≥ 30k | MIT | < 1 周 | Zod schema 可同时复用到 Edge Function 的入参校验 | yup / valibot |
| E2E 测试 | microsoft/playwright |
≥ 60k | Apache-2.0 | < 1 周 | 既做 E2E,又用于 OG 截图 worker;可跑 WebGL | cypress-io/cypress |
| AI 备选位 | vercel/ai |
≥ 8k | Apache-2.0 | < 1 月 | MVP 不引入;P2 做 AI 配色 / 风格推荐时启用 | – |
2.3 后端 / BaaS 层(8 个 repo)
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| Supabase 主仓 | supabase/supabase |
≥ 65k | Apache-2.0 | < 1 周 | 云 + 自托管同一份代码,这是云锁定逃生的根本保障 | appwrite/appwrite |
| Auth 服务 | supabase/gotrue |
≥ 1k | MIT | < 1 月 | Apple / Google / Email OAuth | keycloak/keycloak |
| Storage 服务 | supabase/storage-api |
≥ 1k | Apache-2.0 | < 1 月 | S3 兼容;可直接换底层为 R2 / MinIO | – |
| Realtime | supabase/realtime |
≥ 6k | Apache-2.0 | < 1 月 | Postgres LISTEN/NOTIFY + Phoenix Channels;转码进度推送 | socket.io |
| Postgres 元数据 | supabase/postgres-meta |
≥ 1k | Apache-2.0 | < 1 月 | Studio 后台依赖;Schema 浏览 | – |
| Edge Functions runtime | denoland/deno |
≥ 95k | MIT | < 1 周 | Supabase Edge Functions 底层;TypeScript 一等公民 | Cloudflare Workers |
| Supabase CLI | supabase/cli |
≥ 1k | MIT | < 1 月 | supabase init / start / db push / functions deploy 完整链路 |
– |
| ORM(备选) | prisma/prisma |
≥ 38k | Apache-2.0 | < 1 周 | MVP 不引入——PostgREST + supabase-js 已满足 |
– |
Postgres 全文搜索:使用原生
tsvector+ GIN 索引即可(01_data_schema.md§1 D5),不引入zombodb/zombodb(依赖 ES,运维负担过重)。
2.4 转码 Worker 层(12 个 repo · 重点章节)
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| glb 操作核心 | donmccurdy/glTF-Transform |
≥ 1k | MIT | < 1 月 | Worker 的根基:节点操作、材质提取、Draco / Meshopt 压缩 | 自写(劝退) |
| 几何压缩 | google/draco |
≥ 6k | Apache-2.0 | < 1 月 | gltf-transform 内调;50-90% 几何体积压缩 | – |
| Meshopt | zeux/meshoptimizer |
≥ 5k | MIT | < 1 月 | 与 Draco 互补:顶点流压缩 + 三角形优化 | – |
| glTF 校验 | KhronosGroup/glTF-Validator |
≥ 1k | Apache-2.0 | < 1 月 | Worker 产出 canonical.glb 必须通过该 validator 才能 transcode-done |
– |
| 测试样本 | KhronosGroup/glTF-Sample-Models |
≥ 2k | 多 CC | < 6 月 | CI 端到端测试夹具 | – |
| Sample Assets | KhronosGroup/glTF-Sample-Assets |
≥ 1k | 多 CC | < 1 月 | 子集化的资产库 | – |
| USD 工具链 | PixarAnimationStudios/OpenUSD |
≥ 6k | Apache-2.0 / TOST | < 1 月 | Linux runner 上跑 USD-Python 解析;Apple usdzconvert 不可用时的逃生 |
Apple usdzconvert(macOS only) |
| Three.js 服务端参考 | donmccurdy/three-gltf-viewer |
≥ 1k | MIT | < 1 月 | 借鉴其 headless 渲染配置 | – |
| Headless 渲染 | microsoft/playwright |
≥ 60k | Apache-2.0 | < 1 周 | 缩略图 / OG 截图;Docker 中跑 WebGL2 | puppeteer/puppeteer |
| 图像处理 | lovell/sharp |
≥ 28k | Apache-2.0 | < 1 周 | 缩略图 webp / avif 编码;libvips 底层最快 | imagemagick |
| 任务队列 | taskforcesh/bullmq |
≥ 5k | MIT | < 1 周 | Redis 队列 + 失败重试 + 优先级;Edge Function 跑不动时的后端 | OptimalBits/bull |
| 向量索引(P2) | pgvector/pgvector |
≥ 10k | PostgreSQL | < 1 月 | MVP 不启用;P2 做语义搜索时启用 | Qdrant |
2.5 内容审核 / 治理层(7 个 repo)
| 组件 | GitHub repo | Star | License | 最近 commit | 选型理由 | 备选方案 |
|---|---|---|---|---|---|---|
| NSFW 图像分类 | infinitered/nsfwjs |
≥ 7k | MIT | < 6 月 | TensorFlow.js 模型;MVP 放 Worker(见 §3.5) | OpenNSFW2 (Python only) |
| 中文敏感词 DFA | houbb/sensitive-word |
≥ 1k | Apache-2.0 | < 1 月 | Java DFA + 完整词库,作为 Node 版的词库源 | – |
| 中文敏感词 Node 版 | qieguo2016/sensi(或 sxei/mint-filter) |
≥ 1k | MIT | < 6 月 | 在 Edge Function (Deno) 中跑 DFA 过滤 | – |
| 英文脏词 | web-mech/badwords |
≥ 2k | MIT | < 6 月 | 海外 UGC 补全 | – |
| 人脸 / 视觉兜底 | google-ai-edge/mediapipe |
≥ 27k | Apache-2.0 | < 1 月 | Worker 端人脸二次检测兜底(不替代 iOS Vision) | OpenCV.js |
| OpenCV(通用图像处理) | opencv/opencv |
≥ 78k | Apache-2.0 | < 1 周 | Worker 端通用 fallback:人脸框 + 模糊兜底 | – |
| 评论系统(对比) | umputun/remark42 |
≥ 5k | MIT | < 1 月 | MVP 不引入——自建 comments 表更紧凑;列为撤退路径 |
Disqus(SaaS 锁定) |
2.6 公共资产库(6 个数据源 / 工具)
| 组件 | GitHub repo / 来源 | License | 选型理由 | 备选 |
|---|---|---|---|---|
| CC0 家具 mesh | Quaternius(多仓) |
CC0 | 高质量 CC0 家具集合(沙发 / 床 / 餐桌全套);MVP 资产库种子集来源 | KhronosGroup/glTF-Sample-Assets |
| glTF 测试资产 | KhronosGroup/glTF-Sample-Assets |
多 CC | 转码 Worker 单测 / CI 夹具 | – |
| CC0 材质 PBR | Poly Haven(Poly-Haven/asset-browser 社区 mirror) |
CC0 | 工业级 PBR 材质(木材 / 瓷砖 / 织物) | cgbookcase |
| CC0 材质(备选) | cgbookcase | CC0 | 补充 Poly Haven 盲区 | – |
| Free PBR(备选) | freepbr | 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 + actions/setup-node + actions/cache) |
≥ 5k / ≥ 4k / ≥ 1k | MIT | 与 GitHub 仓库零迁移成本;macOS runner 支持 usdzconvert |
– |
| Sentry 自托管 | getsentry/sentry |
≥ 38k | FSL(含 Apache 子模块) | 云锁定逃生:自托管 Sentry on-prem | glitchtip/glitchtip-backend |
| Grafana | grafana/grafana |
≥ 62k | AGPL-3.0 | Worker / Edge / Postgres 指标可视化 | – |
| Prometheus | prometheus/prometheus |
≥ 55k | Apache-2.0 | 时序指标采集;与 Grafana 标配 | – |
| 依赖安全扫描 | aquasecurity/trivy |
≥ 22k | Apache-2.0 | 容器镜像 + npm / cargo 漏洞扫描;CI fail-on-high | Snyk(SaaS 锁定) |
| Git hooks | pre-commit/pre-commit |
≥ 12k | MIT | 提交前 lint / format / commit-msg | husky |
| Conventional Commits | commitizen/cz-cli |
≥ 16k | MIT | 标准化提交信息;驱动 semantic-release | – |
| 自动发布 | semantic-release/semantic-release |
≥ 21k | MIT | tag + CHANGELOG 自动化(与 CHANGELOG.md 对齐) |
release-please |
| 依赖更新 | GitHub Dependabot(内置)+ renovatebot/renovate |
≥ 17k | AGPL-3.0 | Dependabot 自动 PR;Renovate monorepo 合并 | – |
| Monorepo 构建 | vercel/turborepo |
≥ 26k | MPL-2.0 | 远程缓存 + 受影响包检测;pnpm workspace 配套 | nrwl/nx |
| 本地编排 | docker/compose |
≥ 33k | Apache-2.0 | docker-compose up 一键启动 Supabase + Worker + Redis |
– |
2.8 自托管 / 撤退路径(5 个 repo · 重要)
本节回答:「如果云供应商变脸,2 周内我们能切到什么栈」。
| 撤退目标 | GitHub repo | Star | License | 触发条件 | 替代云组件 |
|---|---|---|---|---|---|
| Supabase 自托管 | supabase/supabase(Docker Compose) |
≥ 65k | Apache-2.0 | Supabase Cloud 被收购 / 涨价 / 关停 | Supabase Cloud(Auth + DB + Storage + Edge + Realtime) |
| S3 兼容对象存储 | minio/minio |
≥ 45k | AGPL-3.0 | Cloudflare R2 涨价 / 退出区域 | R2 / AWS S3 |
| 反向代理 + 自动 HTTPS | caddyserver/caddy |
≥ 56k | Apache-2.0 | Vercel 涨价 / 区域不可达 | Vercel Edge / Cloudflare |
| 反向代理(备选) | traefik/traefik |
≥ 49k | MIT | 需要 k8s 友好时 | – |
| Cloudflare 撤退口 | cloudflare/workers-sdk |
≥ 2k | MIT/Apache-2.0 | 不引入为主路径;仅作 Vercel → CF 迁移工具 | – |
核心承诺:本方案任意一个云组件失效,迁移到自托管栈所需的命令在
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 —— 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 §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 §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 + 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 §3.3 性能预算)。CrowdRoom 整个 LayerPanel + 编辑器场景图节点数 < 500,React 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 §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 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 —— iOS 端 BaaS 适配器
评估结论:推荐使用,但需关注 Realtime 后台稳定性。
功能覆盖:v2.x 起完整对应 Supabase 云端的 Auth / PostgREST / Storage / Realtime / Functions 5 件套;Sign in with Apple OAuth flow 原生支持(03_ios_app_plan.md §2 IA 强需求);presigned URL 用 Storage SDK 直签,与 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 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 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 —— 内容审核关键拼图
评估结论: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 人工二审(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 + pnpm workspace 而非多仓的 3 条理由:
- 契约即代码:
packages/shared-types/的 Zod schema 同时被 Web、Worker、Edge Function 导入;分仓会导致「客户端 PR 合了、服务端 PR 还没合」的 schema 漂移——而本设计02_api_contract.md§7 已有 40 条错误码,任何字段错位都会立即在 CI 端到端测试中爆掉。 - 统一构建缓存:Turborepo 的
--filter让pnpm turbo build --filter=web只构建被改动影响的 packages;CI 时间从 12 分钟降到 3 分钟。 - 撤退路径同栈一键起:
docker-compose.yml在 root 即可启动 Supabase 自托管 + Worker + Redis,不需要跨仓 git checkout——直接呼应 §2.8 自托管承诺。
4.3 Turborepo turbo.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,建立engineeringteam(writers)+dpoteam(readers),开启 SSO。 - Vercel Team 账号;绑定
crowdroomorg;启用 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自建 Mailcow):team@/privacy@/abuse@(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双向同步(可选)。
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 条):
# 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 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 条):
# 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/<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 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 条):
# 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 §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 条):
# 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 §4-§5)
本周目标:iOS App 通过 RoomPlan 扫房 + Vision 端侧人脸检测 + 三段式上传 + PrivacyManifest 全链路跑通。
关键交付物:
- TestFlight 内测包可下发给 ≥ 5 名 dogfooder
- 端侧脱敏 p95 ≤ 15 s(iPhone 12 Pro)
- iOS-X1~X5 五条契约自动化测试
命令骨架(≈ 15 条):
# 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'
<?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
# 编辑 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 §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 条):
# 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 <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 §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 条):
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 条):
# 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§4 风险登记册联动;本节聚焦「开源 / 云依赖层面」的逃生,不重复业务风险。
| # | 触发条件(依赖变脸) | 迁移路径 | 估计切换工时 |
|---|---|---|---|
| E-1 | Supabase 被收购 / 涨价 / 关停 Cloud | 启 docker-compose.yml 中的 supabase/supabase 自托管栈;DNS 切到自建 Caddy;Storage 底层 driver 从 R2 改 minio/minio。supabase-swift / @supabase/supabase-js SDK URL 仅改 base URL,业务代码 0 修改 |
2 人周 |
| E-2 | Vercel 涨价 / 区域不可达 | Web App 切到 Cloudflare Pages(Next.js 全功能支持)或自建 caddyserver/caddy + Node SSR server;Edge Functions(OG endpoint)迁到 denoland/deno Deno Deploy 或 Cloudflare Workers |
1.5 人周 |
| E-3 | gltf-transform 停更 | 直接 fork 到 crowdroom/glTF-Transform;核心需求(Draco / Meshopt / 节点重命名)只用到 ≤ 30% API,可自行维护;同时启动 google/draco + zeux/meshoptimizer 直接调用做 Plan B |
1 人周 |
| E-4 | PostHog Cloud 涨价 / 关停 | 自托管 PostHog/posthog(一行 helm install),或换 plausible/analytics(功能少但够用,funnel 需自建) |
1 人周(含数据迁移) |
| E-5 | Apple usdzconvert 在 GitHub Actions macOS runner 不可用 |
完全切到 PixarAnimationStudios/OpenUSD Linux 构建产物(pip install usd-core);Worker Dockerfile 已预装,无需新增运维 |
0.5 人周 |
| E-6 | Cloudflare R2 涨价 / 退出区域 | Storage 底层 driver 切 minio/minio 自托管 + Backblaze B2 异地灾备;CDN 切 bunnyway/bunny.net 或 Tencent EdgeOne(CN 用) |
1.5 人周 |
| E-7 | NSFWJS 模型 license 变更 / 不更新 | 换 bumble-tech/private-detector 或自训模型;短期内禁用自动审核、强化人工审核 /admin/reports 工作台 |
1 人周 |
| E-8 | Sentry FSL 协议恶化 / 价格暴涨 | 切到 glitchtip/glitchtip-backend(完全 MIT、Sentry SDK 协议兼容,0 代码改动) |
0.5 人周 |
| E-9 | Three.js / R3F 核心维护者跑路 | Three.js 已被 mrdoob / Don McCurdy 等多人维护,停更概率 < 1%;如发生则锁版本 + 自维护补丁;最终极方案降级到 google/model-viewer 提供静态房间预览(功能退化但不挂) |
2 人周(功能降级) |
| E-10 | GitHub Actions 价格暴涨 / 不可用 | 自托管 actions/runner on 自有 VPS(Hetzner / 腾讯云);macOS runner 改用真实 Mac mini in office |
1.5 人周 |
核心承诺再次声明:本方案中没有任何一个组件存在「无逃生通道」的硬依赖——10 条逃生路径均已识别,迁移工时合计 ≤ 13 人周,相当于「2 个工程师 6.5 周可完成全栈迁离」。
8. 成本预算(MVP 阶段,月度)
假设:单房间 source
.usdz30 MB / canonical.glb5 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§2.1 M1 DoD 的「10 项必须全过」对齐;任何角色缺位 ≥ 1 周即触发 M1 延期评估。
10. M1 验收标准(出口清单)
与
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.md19 个端点中 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§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/的 Zod schema 被 iOS / Web / Worker / Edge Function 4 端导入,无任何字段漂移 - G-09 semantic-release 自动 tag + 写
CHANGELOG.md;v0.5-mvp tag 上线 - G-10 所有依赖通过 Dependabot weekly 扫描;当前 0 个高危待修
10.3 合规验收(4 条)
- C-01
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 公开数据 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 §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 第一条命令。