📱 HOOP Apps 平台 · 系统设计图纸
四分天下的第四根柱子:Social Chat 是地基,Games / Music / Discover / Apps 并立。 本平台的职责不是开发某个 app,而是托管、维护、承载、扶持别人的 app。 本页 = 功能 / 操作 / 代码三层 as-built + 诚实复盘。Markdown 版:docs/MINIAPP_PLATFORM.md
架构线 Atlas · 2026-08-15 · 状态:开发者旅程七件套已全部在生产

① 我们对标谁,各学哪半

微信小程序(9 亿月活生态)Telegram Mini AppsHOOP 取哪边
技术专有 WXML/WXSS,学习曲线陡纯 HTML/CSS/JS,零专有语法Telegram 边:H5 游戏管线原样复用,AI 也最会写
平台独有能力支付 + 社交关系(开发者离不开它的根本原因)TON 支付 + 频道分发微信边:我们的杀手锏是关系链 / 六度 / 温度 —— 别人抄不走
数据库云开发(云数据库/云函数/云存储,2018 起,小开发者主流+平台收入)不提供,开发者自带服务器微信边:hoopApp.storage = 我们的"云开发"第一块
审核强制过审才上架基本不审微信边(还没做,复盘 🔴#1)
隐私openid 化名,但生态内数据打通拿真 username比两家都狠:影子身份数学隔离,见 ⑪
一句话结论:微信赢在「平台独有能力灌给 app」,Telegram 赢在「零门槛技术」。 两边的赢面我们都拿:技术零门槛(现成 H5 管线)+ 独有能力(关系链)

② 总架构(一张图)

┌─ 用户端(App)──────────────────────────────────────────────┐
│ 底栏第二格长按 → 「Apps」页(列表,本地优先缓存)               │
│   └→ 全屏壳 MiniappWebViewScreen                             │
│        ├ 先握手 POST /v1/apps/{id}/launch(影子身份+版本分流)  │
│        └ 注入 window.hoopApp 桥(init/user/storage,文档流之前)│
└──────────────────────────┬──────────────────────────────┘
                           │ 用户 JWT(app 永远拿不到)
┌─ 后端 miniapp 域(Go,独立于 games)────────────────────────┐
│ 目录 miniapps · KV miniapp_kv · 图像 miniapp_media            │
│ 版本双指针 live/staging · 留痕 miniapp_promotions · 一房一app   │
└───────┬───────────────────────────┬─────────────────────┘
   Postgres(云库)                  R2 对象存储
   目录/KV/图像账/留痕          miniapps/{app}/v{N}/index.html
                                miniapps/{app}/media/{uuid}
为什么 Apps 是独立的域、独立的表(不寄生 games) —— Jeff 拍板 + 数据检验: games 表有 36 处读方(游戏榜/商城/广告分账/社交条),寄生进去要每处都记得过滤, 漏一处 = Food Guide 出现在游戏榜里,而且不报错。真同构的是托管底座(版本/双指针/留痕/绑房), 不是目录 —— 终局是抽 hosted_bundles 底座表两边共用(记录在案,见 ⑫ 架构债)。

③ 开发者旅程(Jeff 2026-08-13 原话 = 产品流程定案)

「首先,我会到开发者中心去,创建我 apps 的项目,平台直接开辟我们 app 内部的空间。 然后,我会看到平台会提供我数据库、我可以上传图片的图像库、我的代码文件夹(可上传), 最后我还会看到版本管理,执行 live 与 staging 按钮。最重要是我有一个 App 工作房。」
状态落点
创建项目 = 开辟空间生产POST /v1/dev/apps(第 6 步)
数据库生产miniapp_kv + hoopApp.storage(第 4 步)
图像库生产miniapp_media + R2(第 5 步)
代码上传生产POST /v1/dev/apps/{id}/code(第 7 步)
live / staging 按钮生产双指针 + promote + 留痕(第 7 步)
App 工作房生产miniapps.workroom_id 一房一 app(第 7 步)
开发者中心界面(把按钮画出来)待做目前全靠 API;App UI 是下一批(⑬)

④ 目录与身份

功能

平台的「账本」:有哪些 app、归谁、什么状态。用户打开 app 的那一刻,拿到的是影子身份 —— app 永远不知道你是现实中的谁。

操作

用户侧:Apps 页(GET /v1/apps,只列 active)→ 点开 → POST /v1/apps/{id}/launch
launch 返回:{ app_id, app_user_id: "a_…", sdk_version, capabilities:[], entry_url(按身份分流好版本) }
draft 状态的 app:owner / AI 分身可打开(真机测试),其他人 404 当不存在

代码

东西位置关键判据
miniapps迁移 000218 / 225 / 230id·name·icon·tagline·entry_url·status(active/disabled/draft)·dev_id·live_version·staging_version·workroom_id
backend/internal/domain/miniapp/单文件 miniapp.go与 games 零共享代码(等底座抽取)
影子身份 appUserID()HMAC 双层派生域前缀 hoop:miniapp:identity:同一人在游戏(g_)和小程序(a_)里影子不同,两平台数据拼不上是数学事实
launch 版本分流miniapp.goowner/AI 分身/所挂工作房成员 → staging;路人 → live;canSeeStaging 查不出来按「看不到」处理(错放比错关贵)

⑤ 数据库(HOOP 云开发第一块 —— 微信云数据库的位置)

功能

每个 app × 每个影子用户,一个 KV 命名空间。小开发者/AI 造的 app 零服务器就能有"记忆"(收藏、进度、用户内容)。

操作(app 里的代码就这么写)

await hoopApp.storage.set('fav', 'Penang');   // 存
const v = await hoopApp.storage.get('fav');    // 取 → "Penang"
await hoopApp.storage.set('fav', null);        // 删

代码

东西位置关键判据
miniapp_kv迁移 000219;PK=(app_id, app_user_id, k)隔离是主键结构给的,不是代码自觉
端点 GET/PUT /v1/apps/{id}/kv/{key}用户 JWT 鉴权影子 id 服务器现算,桥上不传 —— app 连"报自己是谁"的机会都没有
配额单值 16KB · 每人每 app 128 键58MB 分身会话那课:不设限的存储都会涨到爆;已有键仍可覆盖,只挡开新键
hoopKv handlerminiapp_webview_screen.dartapp JS → 壳 → 带用户 JWT 调 API;断网回 {err},app 自己兜底(裸跑军规)

生产实测:写→读回 / 换人读同键=null(隔离)/ 删→null,四路全过。

⑥ 图像库

功能

每个 app 独立媒体库:传图 → 拿永久地址 → app 里 <img> 直接用。

操作

上传:POST /v1/dev/apps/{id}/media   (body=图片字节, Content-Type: image/*)
      → { id, url:"/miniapp-media/{app}/{id}", mime, size }
列表:GET  /v1/dev/apps/{id}/media
直出:GET  /miniapp-media/{app}/{mid}   (公开,无鉴权头 —— WebView 里的 img 用)

代码

东西关键判据
miniapp_media(000225)+ R2 miniapps/{app}/media/{uuid}登记失败即删 R2 对象 —— 不留没账的孤儿文件
权限 canManage = owner / AI 分身和发布按钮同族判据
配额:单张 2MB · 每 app 200 张同 ⑤ 的病,同一个药
缓存头 immutableid 即内容,永不变 → 可以放心让 CDN 缓一年

⑦ 代码上传与版本管理(live / staging 双指针)

功能

「修改的时候不影响线上」:每次上传产出一个不可变版本快照,库里两个整数决定谁看哪版。 上线/回退 = 改一个整数,零文件搬运、天然原子。这套双指针与游戏平台同构(games 000205 先行验证过)。

操作

传代码:POST /v1/dev/apps/{id}/code (body=index.html)
        → staging_version = max(live, staging)+1   ← 版本号服务器发,客户端不数
自己试:launch 自动把 owner 指到 staging 版
上线:  POST /v1/dev/apps/{id}/promote
        → live = staging;留痕(谁/何时/从几到几);首次上线自动 draft→active(=上架)
直出:  GET /miniapp-code/{app}/v{N}/   (immutable:版本内容永不变)

代码与判据

判据为什么(都是真踩过的)
上传只动 staging,live 一字不碰「上传即上线」正好推翻双轨的意义(games 踩过)
发布按钮绝不改 entry_urlg2048 那颗雷:promote 顺手改写入口 → Pages 游戏一按全球 404。同构照搬防御
没有测试版就报错,不静默成功假成功比报错贵:让人以为推上去了而线上没动
留痕进 miniapp_promotions,留痕失败整笔回退出事那一刻唯一想知道的是「谁推的、何时推的」
单文件 index.html(≤2MB)或 zip 多文件包(≤20MB 解压 / ≤200 文件 / 根必须 index.html)同一端点按 Content-Type 分流;zip 内文本文件逐个过 18+1 条安检(第 9 步已落地)

⑧ App 工作房(「在群里测」)

功能

一个 app 挂一间工作房;房里所有成员自动看到测试版 —— 名单不是名单,是房间成员本身,进人出人可见范围自动跟(不另建内测名单表,那种表必然和房间漂)。

操作

绑房:POST /v1/dev/apps/{id}/workroom {conversation_id}   (空串=解绑)
规则:必须是工作房 + 本人建的;一房一 app(唯一索引 uniq_miniapps_workroom 兜底)

⚠️ 复盘 🔴#3:games 的绑房还查「房里除系统消息一条正文都没有」(必须新房),Apps 这边我漏抄了 —— 见 ⑫。

⑨ 用户端(App 里长什么样)

东西文件说明
入口home_screen.dart 发现槽 mode 2底栏第二格长按菜单「Work/办公」→「Apps」(图标 squaresFour;办公占位页已删)
Apps 页apps_hub_screen.dart列表读 /v1/apps;本地优先(上次目录秒显,断网不白屏);失败态 ErrorRetry 不糊异常串
全屏壳miniapp_webview_screen.dart先握手后加载;launch 返回的 entry 优先(版本分流);hoopApp 桥在文档第一行 JS 前注入 → 无 init 竞态;握手失败裸跑降级
示范 app🍜 Malaysia Food Guide已认领给 Jeff;页脚显示影子身份 = SDK 桥的看得见的自证

⚠️ App 侧改动等下一班 TestFlight;后端全部即时生效。

⑩ SDK 能力面(window.hoopApp)—— 「扶持」的落点

能力状态说明
init() → {app_id, app_user_id, sdk_version, capabilities}生产影子身份,先握手后注入
user.profile()生产只有影子;真名/头像走 consent(000193 地基现成)
storage.get/set生产见 ⑤
social.friendsInApp() 你的好友里谁也在用规划只回影子 id 集合;必须过 is_shadowed 闸
social.degree() 六度距离规划交友类 app 杀手锏;只给数字不给路径
social.invite() 发 app 卡进聊天规划必经用户确认弹层,app 不能静默发
pay.charge() 绿能量支付规划钱包/流水表现成;先只开这一种
notify.push()规划平台节流+审核,绝不给裸推送权
能力三道门(从游戏 SDK 学来,每个能力都是服务端闸+用户可见,不是客户端君子协定): manifest 里声明 → 审核时人看过 → 运行时用户首次触发才授权。capabilities 现在恒空 = 形状先定死,以后逐个亮。

⑪ 隐私闸(和微信最大的不同,品牌线)

⑫ 复盘:不完善清单(按危险排序,诚实的)

✅ 1. 上架审核已落地(2026-08-15,第 8 步·定规矩第一条)。 draft --promote--> review(版本就绪但不进公开列表)--管理员批--> active;驳回退 draft 可改可重投。 已 active 的再 promote 直接切版本(只审"上架",不卡迭代)。 守门只归 ADMIN_UIDS,故意不给 AI 分身 —— AI 造完自己审自己 = 没有门(生产实测 AI 碰审核接口 403)。 审核队列 GET /v1/admin/apps/review 带 try_url(批前先玩到)。
✅ 2. staging 签名已落地(2026-08-15,第 9 步·定空间)。 非 live 版本一律要 ?t= 短时签名票(HMAC,1 小时,launch 时只发给 owner/分身/房成员); 没票或票过期 = 404 不解释(不暴露"这版存在")。live 版照旧公开 + immutable 缓存。 生产实测:owner 入口带票、撕票裸访问 404、带票 index/子文件都通。games 同病仍在(那边风险低一档,记在 GAME_STAGING)。
🔴 3. 绑房漏了「必须新房」检查。games 查「房里除系统消息一条正文都没有」,Apps 的 bindAppWorkroom 我简化时漏抄 —— 拿一间干着别的活的房来绑,房里人会莫名看到测试版。该补,一小时的活。
🟠 4. 没有 rollback 端点。上线炸了只能再传一版再 promote(能走通但慢)。games 有一键回滚,同构补上即可(注意 games 踩过的两课:回滚也要留痕、别碰 entry_url)。
✅ 5. zip 多文件 + 安检已落地(2026-08-15,第 9 步·定空间)。 同一个上传端点按 Content-Type 分流:单 HTML 照旧;zip 解包(≤200 文件 / ≤20MB 解压防 zip 炸弹 / 路径穿越拒收 / 根必须有 index.html), 文本文件逐个过 developer.ScanForbidden(和 AI 造游戏同一张 18+1 条规则表,单一出处铁律 23)。 接 zip 时把规则表修准了一处:<script src= 只禁外链(相对引用是多文件 app 的正当需求), 顺手补了 ESM import "https://…" 外链这条漏网。生产实测:恶意 fetch 422 / 路径穿越 400 / 外链 script 422 / 正当 3 文件包 200。
🟠 6. 开发者中心没有界面。七件套全是 API,人类开发者没法点 —— Jeff 旅程的"我会看到"还看不到。App 侧 hosting 工作台照游戏发布卡纹路镜像一份。
🟠 7. AI 造 app 管线未接 / hoop-app-kit 未建。「从创建第一步开始」的最后一环:/v1/dev/ai/build 换一套 app 版军规 prompt;共享库照 hoop-kit 烘焙模式出姐妹库。首批示范(新闻头条/交友)就用它产,顺便验收管线。
🟠 8. KV 没有键列表/导出;图像库没有总字节配额。开发者看不到自己 app 存了什么(调试盲区);200 张×2MB=400MB/app 上限其实存在但没按字节记账。
🟡 9. 零散欠账:app_card 聊天分享卡(分发闭环)· app 名/tagline 多语言(频道公告那套 i18n 可复用)· 删除/下架流程(照 games「下架=status 一行,可逆不删数据」)· 自带服务器的域名申报(微信经典模式,大 app 要)· KV/媒体随 app 删除的清理策略。
🏗️ 架构债(记录在案,不是忘了):hosted_bundles 托管底座 —— 现在 miniapps 和 games 各自一套同构的三列+留痕表,是有意的过渡:先各自干净,等两边都稳定后一次迁移共用底座,别在功能高速期动地基。判据写在 MINIAPP_PLATFORM.md ⑦。

⑬ 路线图(Jeff 2026-08-15 定框架重排:托盘 = 定规矩 + 定空间)

Jeff:「我们现在是搭建 apps 的托盘!最重要是定规矩、定空间。」 —— 工作房是协作工具,不是托盘地基:已做完(一列+一个端点),降级为边角,不再占路线图主位。
内容依赖
8规矩生产上架审核流:draft→review→active,守门只归管理员done 2026-08-15
9空间生产staging 签名(✅#2)+ zip 多文件 + 上传静态扫描(✅#5)done 2026-08-15
10规矩能力三道门:manifest 声明 → 审核看过 → 运行时授权(capabilities 从恒空开始逐个亮)8
11空间KV 键列表/导出 + 图像库字节配额(开发者看得见自己的空间)
12门面开发者中心界面:把七件套画成按钮(🟠#6)要出 TF 包
13生态AI 造 app 管线 + 首批示范(新闻头条/交友);SDK social/pay 随三道门开闸8/10
边角绑房新房检查(🔴#3 降级:工作房不在主线)· rollback 端点(🟠#4)—— 顺手清,不占主位

相关:MINIAPP_PLATFORM.md(判据与拍板过程)· GAME_STAGING.md(双轨先行者,含 8 条评审)· GAME_PLATFORM_SPEC.md(影子身份)· GAME_KIT.md(烘焙库模式)· DEVELOPER_PLATFORM.md(审核分成)。 本页由架构线维护;改了实现记得回来改这页 —— 图纸最坏的状态不是没有,是看起来像真相