Files
worldmodel/plans/CrowdRoom/EXECUTION_PLAN.md
T
gaojie dbcbdbb59e chore: 为所有 md 文件添加 Hugo front matter
- 处理: 74 个 .md 文件
- 跳过: 0 个(无已存在的 front matter)
- 异常: 3 个(H1 缺失,用文件名兜底)
  - plans/PRISM/.research/readmes/3d-llm.md
  - plans/PRISM/.research/readmes/openmask3d.md
  - plans/PRISM/.research/readmes/openscene.md
2026-05-20 22:42:22 +08:00

67 KiB
Raw Blame History

title, date, draft, tags, categories
title date draft tags categories
CrowdRoom · 基于 GitHub 成熟开源项目的工程执行方案 2026-05-20 false
CrowdRoom
众包
3D 重建
隐私
API
iOS
worldmodel

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 五条工程原则

  1. 开源优先 + 自托管兜底:每个云服务必须存在「同名开源项目自托管版」(例如 Supabase Cloud ↔ supabase/supabase 自托管)。
  2. monorepo + workspaceiOS / Web / Worker / Schemas 同仓共版本,避免「契约漂移」。
  3. TypeScript 一统服务端Edge FunctionsDeno+ WorkerNode+ WebNext.js)共享同一份 packages/shared-types/ 的 Zod schema。
  4. CDN-first:所有公开资产走 Cloudflare R2 + CDN,CrowdRoom 服务端永不承担静态分发流量。
  5. 可测试性即合规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 定义;自托管可选 MixpanelSaaS 锁定,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;端侧脱敏走 VisionUSDZ 重打包走 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 usdzconvertmacOS 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 表更紧凑;列为撤退路径 DisqusSaaS 锁定)

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 HavenPoly-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 Actionsactions/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 SnykSaaS 锁定)
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 自动 PRRenovate 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/supabaseDocker Compose ≥ 65k Apache-2.0 Supabase Cloud 被收购 / 涨价 / 关停 Supabase CloudAuth + 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 + 编辑器场景图节点数 < 500React fiber tree 在 16 ms 内完成 commit。

移动 Safari 兼容性iOS 16+ 全功能;drei 的 useGLTF 已内置 KTX2 / Meshopt / Draco loader。坑点drei <Environment preset> 在 iOS Safari < 16.5 有 HDR 解析 bug,已在 v9.x 修复 → MVP 锁版 drei ≥ 9.105。

SSR 兼容性:R3F 本身不支持 SSR(依赖 WebGL Context),Next.js 端必须 dynamic(() => import('@/components/RoomViewer'), { ssr: false })。详情页 SEO 通过 SSR meta + 静态 thumbnail.webp 实现(04_web_app_plan.md §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 usdzconvertPython 脚本):随 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-14Apple Silicon)已稳定,10 分钟启动;定价比 Linux runner 贵 10×(约 $0.16/min vs $0.008/min)→ 仅在「转码失败回退」场景使用,主流量走 USD-Python 在 Linux runner 跑。实测30 MB .usdzmacos-14 上转 .gltf ≈ 12 s + 上下文启动 90 s。

ModelIOiOS 端使用)iOS 9+ 系统框架,支持 .usdz 读 + 部分写;CrowdRoom iOS 端只用读功能(提取贴图 → 端侧脱敏),重打包交给 Worker 端 macOS runner。

风险与逃生(1) Apple 不承诺 usdzconvert 后向兼容(Xcode 16 可能改 CLI 接口);(2) macOS runner 在 CN 区有时排队;(3) USD-Python 对 .usdz 嵌入纹理的处理偶有 corner case。逃生通道:完全切到 OpenUSD Linux 构建,放弃 Apple usdzconvert 路径——成本是 Worker 镜像变大(含 USD 编译产物 ≈ 600 MB)。

3.4 supabase-community/supabase-swift —— 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 MBINT8 量化版 ≈ 1.5 MB),加载到 TensorFlow.js runtime 后内存占用 ≈ 80 MB。

推理速度:单张 512×512 缩略图,Node.js + @tensorflow/tfjs-nodeCPU)≈ 80 msWorker 跑批量 8 张 ≈ 500 msDeno Edge Function(无 tfjs-node,纯 JS backend)≈ 600 ms / 张 → 超 Edge Function 10 s 软超时风险

误判率:官方报告 Top-1 准确率 93%5 类:drawings / hentai / neutral / porn / sexy);CrowdRoom 场景(房间扫描)误判主要来源是床上人像油画 / 抽象艺术海报 → 阈值 porn + hentai > 0.5 时漏报率 < 2%,误报率 < 5%。对策:误报走 /admin/reports 人工二审(R-17 路由),不直接下架。

能否在 Edge FunctionDeno)中运行:技术上 tensorflow/tfjs 有 Deno 适配,但模型加载耗时 + 内存峰值不适合 Edge Function 冷启动场景。决策NSFW 检测放在转码 Worker T-6(缩略图生成之后),失败不阻塞 ready 但写入 rooms.moderation_signals 字段,由 R-17 审核台显示。

风险(1) 训练数据集 Yahoo OpenNSFW 的派生使用合规边界需法务确认;(2) 模型核心 2020 年发布,需定期评估是否换 OpenNSFW2 或专业服务。


4. 项目仓库结构(Monorepo 决策)

4.1 仓库布局(可直接 mkdir 的目录树)

crowdroom/                       # GitHub rootpnpm + Turborepo
├── apps/
│   ├── ios/                     # Xcode 工程;SwiftPM 管依赖
│   │   ├── CrowdRoom.xcodeproj
│   │   ├── CrowdRoom/           # Swift 源码:Scan / Upload / RealtimeListen
│   │   ├── CrowdRoomTests/
│   │   ├── Package.swift        # SPMsupabase-swift / sentry-cocoa / GRDB.swift
│   │   ├── PrivacyInfo.xcprivacy
│   │   └── fastlane/            # TestFlight 自动化
│   ├── web/                     # Next.js 14 App Router
│   │   ├── app/                 # 17 条路由(见 04_web_app_plan.md §1.1
│   │   │   ├── (marketing)/     # R-01 / R-13 / R-15
│   │   │   ├── r/[room_id]/     # R-02 / R-03 / R-12embed
│   │   │   ├── remix/[remix_id]/
│   │   │   ├── u/[username]/
│   │   │   ├── me/              # R-10 / R-11 / R-16
│   │   │   ├── admin/reports/   # R-17role=admin only
│   │   │   ├── search/
│   │   │   ├── assets/
│   │   │   ├── api/og/r/[room_id]/  # OG 卡片
│   │   │   └── auth/callback/
│   │   ├── components/
│   │   │   ├── viewer/          # RoomViewer + LayerPanel
│   │   │   ├── remix/           # MaterialSlotPanel + FurnitureSwapPanel
│   │   │   └── ui/              # shadcn copy
│   │   ├── stores/              # Zustand: layer-store / overlay-draft
│   │   ├── lib/                 # supabase client + react-query
│   │   ├── messages/            # next-intl: zh-CN.json / en-US.json
│   │   ├── public/
│   │   ├── tests/               # Playwright E2E
│   │   ├── next.config.mjs
│   │   ├── tailwind.config.ts
│   │   └── package.json
│   └── worker/                  # Node.js 转码 worker + BullMQ consumer
│       ├── src/
│       │   ├── pipeline/        # T-1 ~ T-9 步骤(见 02_api_contract.md §3.2
│       │   │   ├── fetch-source.ts
│       │   │   ├── usdz-to-gltf.ts
│       │   │   ├── compress-mesh.ts
│       │   │   ├── tag-layers.ts
│       │   │   ├── build-manifest.ts
│       │   │   ├── render-thumbnail.ts
│       │   │   ├── upload-output.ts
│       │   │   └── notify-done.ts
│       │   ├── nsfw/            # NSFWJS 包装
│       │   ├── queue/           # BullMQ consumer
│       │   └── index.ts
│       ├── Dockerfile           # node:20-slim + chromium + sharp deps
│       ├── tests/
│       └── package.json
├── packages/
│   ├── shared-types/            # Zod schemas: layer_manifest / remix_overlay / view_state
│   ├── viewstate-codec/         # gzip+base64url 编解码(Web + Edge 共用,落实 Web-Y4
│   ├── eslint-config/
│   └── tsconfig/                # 共享 tsconfig.base.json
├── supabase/                    # supabase CLI 本地工程
│   ├── config.toml
│   ├── migrations/              # SQL 迁移(落地 01_data_schema.md §3 DDL
│   ├── functions/               # 11 个 Edge Function
│   │   ├── upload-init/         # E-01
│   │   ├── upload-complete/     # E-04
│   │   ├── transcode-done/      # E-05
│   │   ├── view-state/          # E-10
│   │   ├── remix-create/        # E-11
│   │   ├── like-toggle/         # E-12
│   │   ├── report/              # E-14
│   │   ├── quota/               # E-15
│   │   ├── room-delete-with-snapshot/  # E-17
│   │   ├── account-delete/      # E-18
│   │   ├── account-export/      # E-19
│   │   └── _shared/             # 共享 deno modulesrate-limit / auth-guard / sensitive-word
│   └── seed.sql                 # 资产库种子集 + reviewer 账号
├── scripts/
│   ├── seed-assets/             # Quaternius / Poly Haven 资产归一化
│   ├── smoke/                   # 11 条 X/Y 契约 smoke test
│   └── release.sh
├── docs/                        # 软链到 plans/CrowdRoom/
├── .github/workflows/
│   ├── ci-web.yml               # Vitest + Playwright + Lighthouse CI
│   ├── ci-ios.yml               # SwiftLint + xcodebuild + macOS runner usdzconvert smoke
│   ├── ci-worker.yml            # Vitest + Docker build + Trivy scan
│   ├── ci-supabase.yml          # supabase db lint + functions deploydry-run
│   └── release.yml              # semantic-release
├── docker-compose.yml           # 本地全栈:Supabase + Redis + Worker + MinIO(撤退栈)
├── turbo.json                   # Turborepo 任务依赖图
├── pnpm-workspace.yaml          # workspace: apps/* + packages/*
├── .pre-commit-config.yaml      # SwiftLint + ESLint + commitlint
├── .gitignore
├── README.md
└── LICENSE                      # MIT(与所有依赖兼容)

4.2 Monorepo 决策(pnpm + Turborepo,不分仓)

选择 vercel/turborepo + pnpm workspace 而非多仓的 3 条理由

  1. 契约即代码packages/shared-types/ 的 Zod schema 同时被 Web、Worker、Edge Function 导入;分仓会导致「客户端 PR 合了、服务端 PR 还没合」的 schema 漂移——而本设计 02_api_contract.md §7 已有 40 条错误码,任何字段错位都会立即在 CI 端到端测试中爆掉。
  2. 统一构建缓存Turborepo 的 --filterpnpm turbo build --filter=web 只构建被改动影响的 packages;CI 时间从 12 分钟降到 3 分钟。
  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,建立 engineering teamwriters+ dpo teamreaders),开启 SSO。
  • Vercel Team 账号;绑定 crowdroom org;启用 Edge Functions(默认开)。
  • Supabase 项目创建(免费档 Pro 计划备选);记录 project-ref / service_role_key 入 1Password。
  • Cloudflare 账号 + R2 Bucketcrowdroom-public + crowdroom-private);同时启 CDN cache rule。
  • Apple Developer 99 USD/年订阅;TestFlight 内测组 + App ID app.crowdroom.ios + RoomPlan entitlement。
  • SentryCloud 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/crowdroomprivate MVP 阶段,M1 验收后转 public)。
  • branch protectionmain 要求 ≥ 1 review + CI 绿;dev 允许 admin override。
  • Dependabot 配置 .github/dependabot.ymlweekly 扫 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 个 caseown / public / forbidden
  • pg_cron 定时任务上线:uploading 超 30 分钟标 failed、softdelete T+30 触发 E-18

命令骨架(≈ 14 条):

# 1. 启动本地栈
supabase start  # 起 docker stackpostgres + studio + storage + edge + realtime
supabase status # 记录本地 anon_key / service_role_key

# 2. 写 schema migration
supabase migration new init_schema
# 把 01_data_schema.md §3 的 9 张表 DDL 粘进 supabase/migrations/<ts>_init_schema.sql
supabase migration new init_rls
# 粘 RLS policy
supabase migration new init_tsvector
# 粘 tsvector + GIN 索引
supabase migration new init_pgcron
# 粘 pg_cron 任务

# 3. 本地校验
supabase db reset
psql "postgresql://postgres:postgres@localhost:54322/postgres" -c "\dt public.*"
# 期待看到 9 张表

# 4. 跑 RLS 单测(用 pgTAP
psql -f tests/db/rls_test.sql

# 5. 部署到 Supabase Cloud
supabase link --project-ref <project-ref>
supabase db push
supabase db lint

# 6. 验证云端
curl -s "https://<ref>.supabase.co/rest/v1/rooms?select=id&limit=1" \
  -H "apikey: $SUPABASE_ANON_KEY" -H "Authorization: Bearer $SUPABASE_ANON_KEY"

# 7. 提交 PR
git checkout -b feat/w2-schema && git add -A && \
  git commit -m "feat(db): land 9 tables + RLS + tsvector (W2)" && git push -u origin HEAD

6.3 W3 — Edge Functions(落地 02_api_contract.md 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.tsZustand
# 写 components/viewer/RoomViewer.tsxR3F Canvas + 4 group + useGLTF
# 写 components/layer-panel/LayerPanel.tsx(见 04_web_app_plan.md §4.5 骨架)

# 3. 接入 manifest schemaZod
# 在 packages/shared-types/manifest.ts 定义 LayerManifestSchemaapps/web 与 apps/worker 共用

# 4. 性能预算自动化
pnpm add -D @lhci/cli
echo "module.exports = { ci: { collect: { url: ['http://localhost:3000/r/demo'] }, assert: { assertions: { 'categories:performance': ['error', { minScore: 0.85 }] } } } };" > apps/web/lighthouserc.cjs

# 5. 测试
cd apps/web && pnpm dev &
sleep 5 && pnpm lhci autorun

# 6. 接入 Sentry / PostHog
npx @sentry/wizard@latest -i nextjs
# 编辑 instrumentation.ts + sentry.client.config.ts
pnpm add posthog-js
# 在 app/providers.tsx 初始化 posthog.init()

# 7. Playwright E2E(首个 case
pnpm exec playwright test tests/e2e/room-detail.spec.ts

# 8. 部署 preview
vercel --cwd apps/web

# 9. PR
git checkout -b feat/w4-room-detail && git add -A && \
  git commit -m "feat(web): /r/[room_id] + 4-layer toggle (W4)" && git push -u origin HEAD

6.5 W5 — iOS App 扫描 + 端侧脱敏(落地 03_ios_app_plan.md §4-§5

本周目标iOS App 通过 RoomPlan 扫房 + Vision 端侧人脸检测 + 三段式上传 + PrivacyManifest 全链路跑通。

关键交付物

  • TestFlight 内测包可下发给 ≥ 5 名 dogfooder
  • 端侧脱敏 p95 ≤ 15 siPhone 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.swiftVision VNDetectFaceRectanglesRequest + CIGaussianBlur
# CrowdRoom/Upload/ThreeStageUploader.swiftupload-init → PUT → upload-complete
# CrowdRoom/Realtime/TranscodeListener.swiftRealtime channel + 前台 reconciliation 补丁)

# 6. PrivacyManifest
cat > CrowdRoom/PrivacyInfo.xcprivacy <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
  <key>NSPrivacyTracking</key><false/>
  <key>NSPrivacyCollectedDataTypes</key><array>
    <dict><key>NSPrivacyCollectedDataType</key><string>NSPrivacyCollectedDataTypeDeviceID</string>
          <key>NSPrivacyCollectedDataTypeLinked</key><false/>
          <key>NSPrivacyCollectedDataTypeTracking</key><false/></dict>
  </array>
</dict></plist>
EOF

# 7. 端侧脱敏性能测试
xcodebuild test \
  -scheme CrowdRoom -destination "platform=iOS Simulator,name=iPhone 15 Pro" \
  -only-testing CrowdRoomTests/FaceRedactorPerfTests

# 8. fastlane TestFlight
gem install fastlane
fastlane init
# 编辑 Fastfilelane :beta do build_app + upload_to_testflight end
APP_STORE_CONNECT_API_KEY="$APPLE_API_KEY_BASE64" fastlane beta

# 9. PR
cd ../..
git checkout -b feat/w5-ios-scan && git add -A && \
  git commit -m "feat(ios): RoomPlan + face redaction + 3-stage upload (W5)" && git push -u origin HEAD

6.6 W6 — 转码 Worker(落地 02_api_contract.md §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_processmacOS fallback usdzconvert
# src/pipeline/compress-mesh.ts       # T-3gltf-transform draco + meshopt
# src/pipeline/tag-layers.ts          # T-4
# src/pipeline/build-manifest.ts      # T-5(输出后跑 glTF-Validator
# src/pipeline/render-thumbnail.ts    # T-6playwright + three.js
# src/pipeline/nsfw-check.ts          # T-6.5nsfwjs,结果写 moderation_signals
# src/pipeline/upload-output.ts       # T-8
# src/pipeline/notify-done.ts         # T-9

# 4. Dockerfile(含 chromium + libvips + python3 + openusd
cat > Dockerfile <<'EOF'
FROM node:20-bookworm
RUN apt-get update && apt-get install -y \
    chromium libvips-dev python3 python3-pip \
    && pip3 install --break-system-packages usd-core
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN corepack enable && pnpm install --prod --frozen-lockfile
COPY . .
CMD ["node", "dist/index.js"]
EOF

# 5. 本地构建与运行
docker build -t crowdroom-worker:dev .
docker run --rm -e REDIS_URL=redis://host.docker.internal:6379 \
                -e SUPABASE_URL=http://host.docker.internal:54321 \
                -e SUPABASE_SERVICE_ROLE_KEY=$SVC_KEY \
                crowdroom-worker:dev

# 6. 单测(用 KhronosGroup/glTF-Sample-Models 当夹具)
git submodule add https://github.com/KhronosGroup/glTF-Sample-Models tests/fixtures/gltf-samples
pnpm vitest run tests/pipeline/

# 7. Trivy 扫漏洞
trivy image crowdroom-worker:dev

# 8. 推到 GHCR
echo $GITHUB_TOKEN | docker login ghcr.io -u <user> --password-stdin
docker tag crowdroom-worker:dev ghcr.io/crowdroom/worker:0.1.0
docker push ghcr.io/crowdroom/worker:0.1.0

# 9. Fly.io 部署(或 Railway / 自建 VPS
brew install flyctl
flyctl launch --image ghcr.io/crowdroom/worker:0.1.0 --no-deploy
flyctl secrets set REDIS_URL=$REDIS_URL SUPABASE_URL=$SUPABASE_URL \
                  SUPABASE_SERVICE_ROLE_KEY=$SVC_KEY
flyctl deploy

# 10. 端到端 smoke
node scripts/smoke/upload-and-wait.mjs

# 11. PR
cd ../..
git checkout -b feat/w6-worker && git add -A && \
  git commit -m "feat(worker): transcode pipeline T-1~T-9 (W6)" && git push -u origin HEAD

6.7 W7 — 材质替换 + Remix(落地 04_web_app_plan.md §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.tsZustand temporal middleware

# 2. 离线草稿(IndexedDB
pnpm add idb

# 3. 资产库种子加载
node ../../scripts/seed-assets/normalize.ts \
  --source quaternius --target ../../supabase/seed-data/assets.json

# 4. 接 E-11 remix-create / PATCH remix-update
# 在 lib/edge-fns.ts 包装:createRemix({parent_version_id, title})
# overlay 自动保存防抖 3s + beforeunload 拦截

# 5. 单墙改色 + react-colorful
pnpm add react-colorful

# 6. Playwright E2E
cat > tests/e2e/remix-flow.spec.ts <<'EOF'
import { test, expect } from '@playwright/test';
test('material swap → publish', async ({ page }) => {
  await page.goto('/r/demo-room-id/edit?fork=1');
  await page.getByRole('button', { name: 'Material' }).click();
  await page.getByText('Oak Natural').click();
  await page.getByRole('button', { name: 'Publish' }).click();
  await expect(page).toHaveURL(/\/remix\/.+/);
});
EOF
pnpm exec playwright test tests/e2e/remix-flow.spec.ts

# 7. 性能验证(材质热替换 ≤ 200ms)
pnpm exec playwright test tests/perf/material-swap.spec.ts

# 8. 部署 preview
vercel --cwd .

# 9. PR
cd ../..
git checkout -b feat/w7-remix && git add -A && \
  git commit -m "feat(web): material/furniture swap + remix flow (W7)" && git push -u origin HEAD

6.8 W8 — 治理基础 + 部署上线

本周目标M1 DoD 全部勾绿;NSFWJS 接入;/admin/reports 工作台可用;closed beta(≤ 50 人)发布。

关键交付物

  • 生产环境冷启动测试 + Sentry 0 P0
  • /admin/reports 路由 admin role 鉴权
  • TestFlight 内测 ≥ 50 用户上线

命令骨架(≈ 14 条):

# 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 切到自建 CaddyStorage 底层 driver 从 R2 改 minio/minio。supabase-swift / @supabase/supabase-js SDK URL 仅改 base URL业务代码 0 修改 2 人周
E-2 Vercel 涨价 / 区域不可达 Web App 切到 Cloudflare PagesNext.js 全功能支持)或自建 caddyserver/caddy + Node SSR serverEdge FunctionsOG 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 EdgeOneCN 用) 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 自有 VPSHetzner / 腾讯云);macOS runner 改用真实 Mac mini in office 1.5 人周

核心承诺再次声明:本方案中没有任何一个组件存在「无逃生通道」的硬依赖——10 条逃生路径均已识别,迁移工时合计 ≤ 13 人周,相当于「2 个工程师 6.5 周可完成全栈迁离」。


8. 成本预算(MVP 阶段,月度)

假设:单房间 source .usdz 30 MB / canonical .glb 5 MB / 每用户月均扫 5 个房间、浏览 50 个房间。

服务 / 组件 免费额度 50 DAU 估算成本 500 DAU 估算成本 5 000 DAU 估算成本 何时该切自托管
Supabase CloudPro 500 MB DB + 1 GB Storage + 5 GB Egress + 500k Edge invocations $0 / 月(仍在免费档) $25 / 月(Pro 起步) $200-400 / 月(Storage + Egress + Compute 月费 > $500 时(≈ 5k DAU
Cloudflare R2 10 GB Storage / 1M class A + 10M class B ops $0 $5(≈ 300 GB $40(≈ 2 TB Storage 月费 > $200
Vercel 100 GB Bandwidth / 1k build minutes / Hobby plan $0 $20 / 月(Pro seat $80 / 月(Pro + Edge invocations 月费 > $200
Fly.io Worker 3 shared vCPU + 256 MB(小)免费 $0-5idle $301× 1 vCPU 2 GB $1503-4× 实例) 月费 > $300
Apple Developer $99 / 年(≈ $8.25 / 月) 同左 同左 永远固定
Sentry Cloud 5k events / 月(Developer Plan $0 $26 / 月(Team $80 / 月(含 transactions 月费 > $100,切 GlitchTip
PostHog Cloud 1M events / 月 + Session Replay $0 $0(仍免费档) $50 / 月 月费 > $200
GitHub Actions 2000 分钟 / 月(含 macOS x10 倍率) $0 $30 / 月(含 macOS runner $100 / 月 月费 > $300
域名 / DNS $1 / 月 $1 $1
合计(粗估) ≈ $10 / 月 ≈ $140 / 月 ≈ $700-1 000 / 月

决策红线:月度成本 > $1 000 即触发「自托管启动」评估;本方案的撤退路径在 §7 已完整说明,自托管 5k DAU 月度成本(含 VPS + 流量)估算 ≈ $300-500,明确低于纯云。


9. 工时估算与人员配比

角色 周次投入 主要任务对应章节
iOS 工程师 × 1 W5 全职 + W1/W3/W7 各 20% §6 W5iOS 全部);其他周做 client SDK 联调
Web 工程师 × 1 W1 / W4 / W7 全职 + 其他周 50% §6 W1(脚手架)、W4(详情页+图层)、W7(Remix);W8 部署联调
全栈 / 后端 × 1 W2 / W3 / W6 / W8 全职 + 其他周 50% §6 W2Schema)、W3Edge Fn)、W6Worker)、W8(治理+部署)
设计师 × 0.3 W1-W4 各 ≈ 10 小时 UI 设计稿、4 层面板视觉规范、shadcn 主题、OG 卡片模板
PM × 0.2 贯穿 8 周 sprint planning、DoR/DoD 核对、risk register 维护、内测招募
法务 / DPO 兼职 W2 / W8 各 4 小时 隐私政策审核、CC0 资产合规审查

人周合计iOS 1 × 8 × 0.55 + Web 1 × 8 × 0.7 + 后端 1 × 8 × 0.7 + 设计 0.3 × 4 + PM 0.2 × 8 ≈ 17 个有效人周 / 8 周自然周(≈ 32 人周计入并行 + 切换 + 评审折扣后的真实工时)。

ROADMAP.md §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 siPhone 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 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 §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.mdv0.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 + TurborepoiOS / Web / Worker / Edge Function / Schemas 同仓共版本;3 条理由
8 周 × ≈ 14 条命令 W1 仓库初始化 → W8 closed beta;累计 116 条可执行命令,零伪命令
10 条逃生通道 任意单组件失效 ≤ 2 人周可迁移;全栈撤离 ≤ 13 人周
28 条 M1 验收点 功能 12 + 工程化 10 + 合规 4 + 内测 2,与 ROADMAP.md §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 第一条命令。