diff --git a/.agents/skills/architecture-diagram-atlas.md b/.agents/skills/architecture-diagram-atlas.md index 0288b947..b677d220 100644 --- a/.agents/skills/architecture-diagram-atlas.md +++ b/.agents/skills/architecture-diagram-atlas.md @@ -9,7 +9,7 @@ ## 产物形态 - **混合**:复杂/跨域图 = 手绘 **SVG** 大图(高保真、矢量、GitHub 直渲);简单/线性图 = **Mermaid** 内联。 - 每张 SVG 另在 **mini-desktop** 批量转 PNG 入仓(6c6g 禁 chrome、无转换器)。SVG 只在 6c6g 出。 -- 每域一份 `<域>图说.md` + 同级 `assets/`;总图叙 = `docs/architecture/系统全景图说.md` + `docs/architecture/assets/`。 +- **最终产物 = 单一全量主档** `docs/architecture/系统全图说.md`(总图 + 目录 + 全部图说 inline 在一篇)。**为什么不分多文件**(2026-06-22 创始人反馈纠偏):多文件形态下,顶层/某文档跨子目录引用另一子目录里的 SVG,在部分渲染环境**不显图**;故合并成一份顶层文档。SVG 仍按域留各子目录 `assets/`(产出阶段如 `运维/assets/`、`架构/assets/`),主档经**顶层相对路径**(`运维/assets/xx.svg`、`架构/assets/arch-xx.svg`,apex 系统级图在 `assets/xx.svg`)引用,全部 inline 渲染。各域 `README.md` 是既有设计档、不动。 ## SVG house style(逐条照做,违一条图废) - 头:``,H 按内容 700~960。 @@ -35,7 +35,7 @@ ## 编排配方(按域并行) - **每域一个 opus agent**(`agentType: general-purpose`,可 Write),克隆金样板:读 计划稿+金样板 md+2 张金样板 SVG+本域源档 → 逐图产出(SVG/Mermaid/引用)→ 自检 → 汇编 md。结构化返回 `{docFile, svgFiles, figureCount, svgOk, statusDisciplineNote, singleSourceNote, selfCheck, flagsForReview}`。 - 多域 = `parallel()` 扇出(不同域不同文件夹、零碰撞,无需 worktree 隔离)。同域两份图说共享 `assets/` 时用文件名前缀区分(如 `arch-` / `sbx-`)。 -- **总图叙 apex 单独产**(导览要引用已建成的各域图说,放最后做)。 +- **合并成单一全量主档**(放最后,关键收尾):各域产出 + 系统级图 → 合进 `系统全图说.md`。规则:逐字保留各域散文、SVG 改顶层相对路径、「引」图 inline 其 README 的 Mermaid(带「(图源:…)」小注)、顶部生成目录锚(GitHub-slug 算法,逐个核命中零断链)。生成引擎子树只链接不并入。**各域中间产物(per-domain `<域>图说.md`)是临时件,合并后删除**(避免与全量主档双份漂移),SVG 留子目录;主档防漂移门改记真实设计档 hash(非中间图说)。 - **分两期降风险**:先六域(自包含、可立全局轮廓),后生成引擎子树(最厚、且常被另一 session 在飞建——**第二期前必先跨 session 对齐边界、只链不双写**)。 ## 复验脚本(主会话亲验,不轻信 agent 自检) diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 5900f8a6..0cdfc7b4 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -3,6 +3,7 @@ > **这是什么**:绘境AI 全部设计文档的**唯一入口**。无论你是产品、工程师、运营,还是外部评审,都从这里开始。 > **读它 = 当前真相**。这套文档是项目的策展层(curation layer)——经过收敛、只保留一份真值;过程稿、评审记录、历史留痕在别处(见文末"其它去哪找")。 > **怎么用**:先看下面这张全景图认门,再按你的角色跳到对应领域的主文档,需要细节时层层下钻。 +> **看图入口(架构图集)**:全部架构图(系统级 + 七领域 ≈ 71 张图 + 讲解)已合并为一篇 → **[系统全图说](系统全图说.md)**——打开一篇、看图加讲解读懂整个系统、据图 review 设计有没有缝。 --- diff --git a/docs/architecture/产品/产品图说.md b/docs/architecture/产品/产品图说.md deleted file mode 100644 index 0c1c6ba5..00000000 --- a/docs/architecture/产品/产品图说.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -date: 2026-06-22 -topic: 产品域图说——把「绘境AI 对用户提供什么」画成一套图 + 讲解(架构图集第一期·产品域) -status: 全图状态 = 现(产品定义层;个别元素含 future-state 注脚,已就元素级标注)· 防漂移门记五份源档 commit hash -映射源档: - - docs/architecture/产品/README.md @ 366a44b4 - - docs/architecture/产品/需求清单.md @ 15b707fd - - docs/architecture/产品/需求模块映射.md @ 7ecd616e - - docs/architecture/产品/商业定位.md @ f4ee2310 - - docs/architecture/产品/护城河话术.md @ 5cadf504 ---- - -# 产品域图说 - -> **这是什么**:绘境AI 产品域的「看图入口」。产品域只回答一件事——**这个产品对用户提供什么**(WHAT),不碰「用什么技术实现」(HOW,那部分在[架构域](../架构/README.md))。这份图说把产品域五份 canonical 设计档画成一套图:从最顶层的「做得出 → 有人玩 → 赚到钱」闭环,到 19 个产品域如何归拢成五大块,到 155 条需求怎样收敛成 55 条 P0,到产品需求与技术模块之间那张唯一的多对多映射表,再到创作者与玩家两条旅程,最后落到商业定位与护城河。复杂、跨块的用手绘 SVG 大图,简单、线性的用 Mermaid,已经内嵌在 README 里的图一律单源引用、绝不复制。 -> -> **给谁看**:产品经理、新加入的同事、外部评审、投资人尽调,以及任何想先建立「这个产品到底要给谁、给什么」全局认知的人。判断「这套产品方案是不是想要的那个」看这份图就够建立全局轮廓;逐条需求的字句仍去[需求清单](需求清单.md),每条需求由哪些技术模块实现仍去[需求模块映射](需求模块映射.md)。 - ---- - -## 0. 阅读约定与同步纪律 - -- **映射的设计档**:本图说不另立设计,只把产品域五份 canonical 设计档画出来——产品闭环与 19 域归类出自 [`产品/README.md`](README.md),155 条需求与编号体系出自 [`需求清单.md`](需求清单.md),需求↔模块的多对多映射出自 [`需求模块映射.md`](需求模块映射.md),商业定位与竞争格局出自 [`商业定位.md`](商业定位.md),对外护城河口径出自 [`护城河话术.md`](护城河话术.md)。frontmatter 里记了这五份的当前 commit hash,作为**防漂移门**:源档一旦变更、hash 对不上,这份图说与对应 SVG 即标「待复核」,由收口脚本比对。 -- **同步纪律**:**设计一变动,本图说与对应 SVG 必须同步更新**,每张 SVG 脚注注明映射源档与状态。已并入 wave 收口清单。 -- **本域状态分布**:产品域是**定义层**而非建设层——它描述的是「产品要提供什么」,不是「代码建到了哪一步」,所以**九张图整体状态都是「现」**(即「这是当前认定的产品定义」),与运维域那种「现行/待接/缓做」三态并存的情况不同。但有两处必须用元素级虚线标清楚、绝不能画成已落地的 MVP 范围:一是**第二条生成轨(tier2 富游戏轨)**,它是经营/合成/挂机等 premium 品类的承载,目前是待 0 号 spike 验证的假设、未落代码、不在 MVP 的 P0 清单、也不单列正式 P-id;二是**控制面 / 管理面治理层**,两条生成线共用、部分有地基、整体待建。这两处在图 2 与图 5 里都用紫色虚线框 + 「待建 / post-spike」小标与「现行已认定」的实心块区分开。**至于这 55 条 P0 各自的真实代码完成度**(哪些真端到端、哪些半真、哪些还是桩),那是建设进度、不是产品定义,以 [`需求模块映射.md`](需求模块映射.md) 的现状快照与 [`docs/mvp/MVP进度总账`](../../mvp/MVP进度总账.md) §2 矩阵为准,本图说不重复承载、不画成产品图的一部分。 -- **产物形式**:SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 转(6c6g 禁 chrome、无转换器)。本图说只出 SVG;Mermaid 图 GitHub 直接渲染、无需转换;引用类图只给链接、不产新文件。 - -## 1. 全图通用图例 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上)。产品域是定义层,不直接谈安全/可观测/可靠这些运行时维度,所以本域只点到其中两个与「产品价值」最相关的: - -- **数据**(`#475569`):19 域全景与需求映射本身就是产品的数据底座;护城河四层里最硬的第一层也是数据壁垒——玩家行为数据 + 质量评分 + 推荐信号。本域里凡是讲「数据回流 / 越用越聪明 / 飞轮」的地方都点这个维度。 -- **质量**(`#15803d`):⭐ 三个 P0 最密集的域(自定义创作 / 发布分发 / 游戏信息流)串起的正是「能生成、能发、能刷到」的质量闭环;这条闭环每一环都真实跑通,是 MVP 不追求功能多、只追求闭环跑通的体现。 - -(其余五个维度——安全 `#dc2626`、可观测 `#0ea5e9`、可靠 `#16a34a`、成本 `#16a34a`、伸缩 `#7c3aed`——属于运行时与工程视角,在架构域 / 后端域 / 运维域的图说里点到;产品域刻意不碰,以守住「只讲用户能感知什么」的纪律。唯一例外是图 9 的商业定位,那里「资本效率」天然对应**成本**维度,故在该图点到。) - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现行已认定的产品定义、块间产物的正向流转、★主映射(主要实现一条需求的技术功能)。 -- **虚线**(`#64748b`,`stroke-dasharray="6 4"`;待建项用紫色 `#7c3aed`):辅映射(支撑/兜底)、收益与数据的回流路径、以及 future-state(第二条生成轨 / 治理层 / 要点燃的护城河)。 -- **双线 / 加粗框**:跨侧共享或需要强调的边界——典型是图 5 中间那张 RTM 表,它是产品侧与技术侧之间唯一的耦合隔离面,用加粗框强调「改一侧只动这一张表」。 - -> 产品域图说里,虚线额外承担一层**诚实纪律**:凡是「未来式 / 待建」的东西(护城河飞轮、第二条生成轨、治理层),一律虚线 + 小标画出,与「现在真有的」实心块一眼区分。这是产品域最该守的纪律点——护城河话术的核心就是「不假装有墙」,任何一处把未来式画成已有,都会击穿信任。 - ---- - -## 2. 图集 - -### 图 1 · 产品闭环全景〔引·现〕 - -> 单源引用,不复制:产品闭环全景的 Mermaid 已在 **[产品/README.md §1](README.md#1-一句话与一张图绘境ai-是什么)** 内嵌,是该图的唯一来源。这里只链接、不抄一份进来——同一张图存两处、改一处漏一处,正是这份图集要消灭的漂移面。 - -这张图回答最顶层的问题:**绘境AI 到底是什么**。答案是一条闭环——把三件过去彼此割裂的事接成一条链:一个没有任何编程基础的普通人,从一句话出发做出一款能上线、能变现的轻量小游戏(**创作**);玩家在类似短视频的游戏信息流里刷到它、即点即玩(**分发与游玩**);平台通过广告、订阅会员、B 端定制三条线变现,收益与行为数据再回流反哺推荐(**变现与数据**)。 - -读这张图要抓住一句话:**这条闭环就是所有产品功能挂靠的主干**。竞品大多停在「生成工具」这一步——做完没人玩、也不赚钱;绘境的差异化不在生成本身,而在把「生成 + 流量 + 变现」串成一条链。图里那条从「收益 & 行为数据」回到信息流的虚线尤其关键:它是平台「越用越聪明」的来源,也是后面图 9 里数据护城河的产品侧投影。这张图没有现行/远期的灰度,它就是当前认定的产品骨架;它与本图说后面所有图的关系是「图 1 给主干,其余八张图把主干的每一段展开」。 - -### 图 2 · 19 个产品域 × 五大块〔SVG新·架·现〕 - -![图 2 · 19 个产品域 × 五大块](assets/02-19产品域五大块.svg) - -这张图把图 1 那条线性闭环展开成产品的**全貌**:155 条需求按「用户能感知的能力」聚成 19 个产品域,再归拢成五大块,让人一眼看清这个产品由哪些可感知的能力块组成、它们之间怎么流转。五大块从左到右就是闭环的五个阶段——**创作侧**把游戏做出来(素材/模板/自定义创作/授权 IP/发布,5 个域)、**玩家侧**让游戏被玩到(广场/信息流/社区,3 个域)、**变现侧**把钱赚回来(钱包/付费/广告,3 个域)、**成长侧**让创作者留下来(数据经营/成长/激励/通知,4 个域)、**平台侧**让生态转起来(IP 孵化/B 端/运营/账号合规,4 个域)。块顶之间的实线箭头画出产物流转的主方向:创作侧产出 → 玩家侧消费 → 变现侧回钱 → 成长侧留人 → 平台侧转生态。 - -读这张图要抓住三个细节。其一,图上用蓝色高亮块钉死了 **⭐ 三个 P0 最密集的域**:自定义创作、发布分发、游戏信息流——它们是 MVP 闭环的关键环节,「能生成、能发、能刷到」这条最小链全靠它们撑起;其余域多为 P1/P2 的后续加厚。其二,每个域卡片右上角标了该域的优先级密度(P0 / P1 / P0-P1 混合),让人快速判断哪些域是 MVP 必交、哪些是远期。其三,也是本图最该让人看清的**设计缝**:创作侧底部那个紫色虚线框——**第二条生成轨(tier2 富游戏轨)**。当前 19 个域承载的是超休闲档的廉价生成线;经营、合成、挂机这类多系统的 premium 品类不在这条线上,而由第二条生成轨承载,它面向价值更高、产量更低的场景,与超休闲线解耦并存。这一轨目前是**待 0 号 spike 验证的假设、未落代码、不在 MVP 的 P0 范围、也不单列正式 P-id**——所以图上必须用虚线把它和 19 个实心域块严格区分,绝不能让人误以为它已是 MVP 的一部分。它的完整设计在[架构域生成引擎子树](../架构/生成引擎/自治富游戏引擎.md),本图只挂一个边界注脚。 - -### 图 3 · 155 → 55 P0 收敛〔引·现〕 - -> 单源引用,不复制:155 → 55 收敛的 Mermaid 已在 **[产品/README.md §4](README.md#4-mvp-的边界155-选-55)** 内嵌,是该图的唯一来源。这里只链接、不抄。 - -这张图回答「MVP 到底做多少」。完整产品有 155 条需求分布在 19 个域,MVP 阶段不全做,而是聚焦其中 **55 条 P0**——它们刚好串起一条「种子用户可试用的全链路」:创建 → 生成 → 预览 → 发布 → 信息流 → 游玩 → 互动 → 广告 → 收益。这张图最该让人记住的,是它背后那条产品哲学:**MVP 不追求功能多,而追求这条闭环每一环都能真实跑通**;P1/P2 是闭环跑通之后的加厚,不是 MVP 的考核项。 - -这条「155 选 55」的收敛口径,是理解整个产品域优先级的总开关——图 2 里 ⭐ 标记的三个 P0 密集域为什么最硬,正是因为它们贡献了这 55 条里最关键的那些环。要注意的设计缝在于:55 这个数字是**产品验收口径**(哪些功能 MVP 必须可验收),不是**建设完成度**(这 55 条各自代码建到了哪一步)。两者是两回事——某条 P0 在产品口径上「必交」,不等于它今天已经真端到端;真实完成度以需求映射的现状快照与 MVP 进度总账为准(详见 §0 的纪律说明)。 - -### 图 4 · 需求编号体系〔Mer新·讲·现〕 - -这张图讲清「怎么读懂一条需求」。需求清单里每一行是一条产品功能,带四个属性;第一次接触的人只要会拆这四个属性,就能读懂整份清单。**编号** `P-{域}-{序号}` 把每条需求挂到它所属的产品域(域用三字母缩写,MAT 素材 / TPL 模板 / CRT 创作……);**端** 说明这条功能服务谁(创作者 / 玩家 / 经营 / 运营 / B 端 / 通用——其中「经营」特指创作者查看自己作品的数据,与「运营」即平台管理员不是一回事,这是最容易混的一对);**优先级** 区分 P0(MVP 必须验收)与 P1/P2(后续迭代);**来源** 标这条需求是从早期 demo 原型来的、还是延续既有能力(v2.0)、还是 demo 暴露出的缺口编号(G#)。 - -```mermaid -flowchart TB - ID["一条需求 = 一行
例:P-CRT-08 生成结果实时预览试玩"]:::id - ID --> A1["① 编号 P-{域}-{序号}
P-CRT-08 = 自定义创作域第 8 条
域用三字母缩写"]:::attr - ID --> A2["② 端:服务谁
创作者 / 玩家 / 经营 / 运营 / B端 / 通用
⚠️ 经营=创作者看自己数据 ≠ 运营=平台管理员"]:::attr - ID --> A3["③ 优先级
P0 = MVP 必须验收(155 里 55 条)
P1 / P2 = 后续迭代"]:::p0 - ID --> A4["④ 来源
demo = 早期原型出现过
v2.0 = 延续既有能力 · G# = demo 暴露的缺口"]:::attr - - classDef id fill:#eff6ff,stroke:#2563eb,stroke-width:2px; - classDef attr fill:#ffffff,stroke:#475569,stroke-width:1.4px; - classDef p0 fill:#fef9c3,stroke:#ca8a04,stroke-width:1.6px; -``` - -这张图没有现行/远期边界,它是一份「读法说明」,本身就是当前真相。它和图 2、图 3 是一组:图 4 教你拆单条需求的四个属性,图 2 把这些需求按域聚成全貌,图 3 用「优先级」这个属性把 155 收敛成 55。要点出的一处易错点已经画在图上——**端属性里「经营」和「运营」必须分清**:经营是创作者侧(看自己作品的留存/完玩/广告转化),运营是平台管理员侧(审核/精选/封禁/经营看板),两者在需求清单里分属不同的域(OPS vs OPN),混了就会把功能归错端、排错期。 - -### 图 5 · 需求 ↔ 模块 M:N 映射(RTM)〔SVG新·ER·现〕 - -![图 5 · 需求 ↔ 模块 M:N 映射(RTM)](assets/05-需求模块映射RTM.svg) - -这张图回答「一条产品需求由谁实现」——也是产品域与技术域之间唯一的桥。它画的是一张**关系矩阵**(不是面向对象的类图):左侧是产品侧的 155 条需求(按五大块分组的 P-id),右侧是技术侧的 13 个模块(204 条 T-id),中间是这张 RTM(Requirements Traceability Matrix,需求可追溯矩阵),用多对多的连线把两侧关联起来。之所以要单独有这么一张表,是因为产品需求和技术模块**各自高内聚、互不引用**——需求清单不认识 T-id,架构域不认识 P-id;两者之间的多对多对应关系是最容易变动的耦合点,把它单独隔离在这一张表里,**改产品或改模块,只动这一张表**,另一侧纹丝不动。 - -读这张图要抓住三层意思。其一,**三个读法标记**:每行一条 P-id,标了它的「首要 owner」(主要负责的那一个模块,问责与排期归属)、「★主」(主要实现这条需求的技术功能,可以是多个 T-id)、「辅」(支撑或兜底的技术功能,常跨模块)。图中央用一行放大的样例把这三层讲透——产品需求「生成结果实时预览试玩」(P-CRT-08)首要 owner 是 runtime 模块,★主靠 T-RT-01 编译 / T-RT-06 渲染容器 / T-RT-07 注入 / T-RT-04 沙箱四个技术功能,辅以 T-STU-07 的 SSE;产品侧完全不需要知道这些 T-id,技术侧也不需要知道 P-id,只有这张表两边都认识。其二,**多对多是双向的**:一个产品域会连多个技术模块,一个技术模块也会被多个产品域连——图上的交叉连线刻意画出这种「网状」而非「一一对应」的关系,这正是为什么必须用一张专门的矩阵、而不能把映射塞进任何一侧。其三,本图最该让人看清的**设计缝**画在中间表底部那个紫色虚线框里:**两块「待建 · post-spike」的映射**——第二条生成轨(tier2 富游戏轨)和控制面/管理面治理层。这两块设计已经成形、但尚未落地,先在 RTM 里挂上指针、免得它们在产品与技术之间悬空;但它们**还没有正式的 T-id 与映射行**,要等 0 号 spike 验证通过后才展开。所以图上用虚线把这两块和 19 个域的实心映射严格分开——它们是「已挂指针、待建设」,不是「已映射、已实现」。 - -### 图 6 · 五大块产品域关系〔Mer新·架·现〕 - -这张图把图 2 里五大块之间那几根块顶箭头单独放大,讲清「产物在五大块之间怎么流转」。如果说图 2 是「五大块各装了哪些域」的静态全貌,这张图就是「五大块之间动态怎么咬合」的流转图:创作侧产出可玩游戏,是整条链的源头;玩家侧消费这些游戏,把「有人玩」坐实;玩家的游玩与互动触发变现侧把钱赚回来;赚到的收益与沉淀的数据反哺成长侧,让创作者看到回报、愿意留下来继续创作;而平台侧作为底座,一边给生态提供 IP/B 端/运营/合规的支撑,一边把数据与收益的回流闭合回创作侧——形成「创作产出 → 玩家消费 → 变现回流 → 创作者留存 → 再创作」的飞轮。 - -```mermaid -flowchart LR - CRT["创作侧
把游戏做出来
素材/模板/创作/授权IP/发布"]:::crt - PLZ["玩家侧
让游戏被玩到
广场/信息流/社区"]:::play - PAY["变现侧
把钱赚回来
钱包/付费/广告"]:::pay - GRW["成长侧
让创作者留下来
数据经营/成长/激励/通知"]:::grow - OPN["平台侧(底座)
让生态转起来
IP孵化/B端/运营/账号合规"]:::plat - - CRT -->|"产出可玩游戏"| PLZ - PLZ -->|"游玩+互动触发"| PAY - PAY -->|"收益+数据反哺"| GRW - GRW -.->|"创作者继续产出(飞轮)"| CRT - OPN -.->|"IP/B端/审核/合规 支撑全链"| CRT - PAY -.->|"行为数据回流→推荐优化"| PLZ - - classDef crt fill:#eff6ff,stroke:#2563eb,stroke-width:2px; - classDef play fill:#f0fdf4,stroke:#16a34a,stroke-width:2px; - classDef pay fill:#fefce8,stroke:#ca8a04,stroke-width:2px; - classDef grow fill:#fdf4ff,stroke:#a21caf,stroke-width:2px; - classDef plat fill:#f1f5f9,stroke:#475569,stroke-width:2px; -``` - -这张图没有现行/远期边界,它是当前认定的产品域间关系。它最该让人看出的是两条**回流虚线**——从成长侧回到创作侧的「创作者继续产出」、从变现侧回到玩家侧的「行为数据回流推荐优化」:正是这两条回流把一条线性的价值链弯成了飞轮,也正是它们对应了商业定位里「数据/网络飞轮」这层护城河(见图 9)。要点出的设计缝是:平台侧被画成「底座」而非链上的一环——它不在主流转线上,而是横向给全链提供 IP、B 端、审核、合规的支撑;这与图 2 把平台侧画成第五大块并不矛盾,图 2 讲的是「它装了哪些域」,本图讲的是「它在流转中扮演底座角色」,两个视角互补。 - -### 图 7 · 创作者旅程〔Mer新·时·现〕 - -这张图从**创作者**的视角,把图 1 闭环的「创作侧 + 成长侧」展开成一条时间线上的旅程:一个创作者从进入平台到看到收益,依次经历哪些步骤、每步落在哪个产品域。它回答的是「一个创作者在这个产品里的一天是怎样的」——这是产品体验的主线之一,也是判断「创作侧的产品设计是否顺畅」的依据。 - -```mermaid -sequenceDiagram - autonumber - actor C as 创作者 - participant MAT as 素材中心(MAT) - participant CRT as 自定义创作(CRT) - participant RT as 实时预览 - participant PUB as 发布分发(PUB) - participant OPS as 数据经营(OPS) - - C->>MAT: 挑素材 / 选玩法模板(可选) - C->>CRT: 一句话描述要做什么游戏 - CRT-->>C: AI 生成可试玩游戏 + 进度实时展示(步骤+百分比) - C->>RT: 预览试玩 - alt 不满意 - C->>CRT: 带描述迭代 / 一键重新生成 - CRT-->>C: 重新生成 - end - C->>PUB: 发布前检查(锁风+性能+版权)→ 一键多渠道发布 - PUB-->>C: 自有必选 + 抖音/微信/快手/TapTap(外部渠道远期) - Note over C,OPS: 游戏上线、被玩家刷到、产生收益与行为数据 - OPS-->>C: 数据看板 / 留存趋势 / 收益明细 → 看到回报 -``` - -这张图把创作者旅程画成「挑素材 → 一句话生成 → 预览迭代 → 发布 → 看收益」五段。最该让人看出的是**两个体验关键点**:一是生成环节的「进度实时展示 + 预览试玩 + 带描述迭代」闭环——这对应 P0 最密集的自定义创作域,它决定创作者第一次用的时候「等得安不安心、改得动改不动」,是留存的第一道关;二是旅程末端的「看到收益」——OPS 数据经营让创作者看到留存、趋势与收益明细,对应成长侧「让创作者留下来」的产品意图,把一次性创作变成可持续经营。要点出的设计缝是:发布环节的**外部渠道(抖音/微信/快手/TapTap)当前是远期**——自有渠道是 MVP 必选且已真端到端,外部渠道在产品口径里是 P1、代码侧基本未建(详见需求映射现状快照);所以图上把外部渠道标为远期,避免让人以为一键多渠道里的每个渠道今天都通了。 - -### 图 8 · 玩家旅程〔Mer新·时·现〕 - -这张图从**玩家**的视角,把图 1 闭环的「玩家侧 + 变现侧」展开成一条旅程:一个玩家从刷到游戏到产生互动与变现,依次经历哪些步骤。它和图 7 是一对——图 7 是「做游戏的人」的主线,图 8 是「玩游戏的人」的主线;两条旅程在「游戏被发布出来、进入信息流」这一点交汇。 - -```mermaid -sequenceDiagram - autonumber - actor P as 玩家 - participant FED as 游戏信息流(FED) - participant RT as 即点即玩(沙箱运行时) - participant SOC as 社区互动(SOC) - participant ADV as 广告变现(ADV) - participant STU as 同款创作(→工作坊) - - P->>FED: 打开游戏信息流 → 竖屏即刷 - FED-->>P: 首屏封面(封面/标题/作者/玩法/开始) + 加载进度反馈 - P->>RT: 点击即玩(无需下载安装) - RT-->>P: 即点即玩,上下滑/按钮切换下一款 - P->>SOC: 点赞 / 收藏 / 分享到社交平台 / 举报 - Note over P,ADV: 游玩中触发变现 - ADV-->>P: 激励视频(看 30s 换复活/道具) / 插屏广告 - opt 玩家被激发想自己做 - P->>STU: 「发起同款」跳转工作坊 → 变成创作者 - end -``` - -这张图把玩家旅程画成「刷 feed → 即点即玩 → 点赞分享 → 发起同款」。最该让人看出的是**两处产品设计的巧思**:一是「即点即玩」——首屏封面给足信息、点击就玩、无需下载安装、上下滑就换下一款,这套竖屏短视频式的体验是绘境区别于「下载-安装-启动」传统小游戏的差异化主战场,对应 P0 最密集的游戏信息流域;二是旅程末端的「**发起同款**」——一个玩家如果被某款游戏激发、想自己做一个,可以一键跳转工作坊变成创作者(P-FED-12)。这一步是整个产品最重要的闭环回路:它把「玩家」转化成「创作者」,让图 7 的创作者旅程与图 8 的玩家旅程**首尾相接**,正是商业定位里「网络效应」护城河(创作者越多→内容越丰富→玩家越多→创作者越多)的产品载体。要点出的设计缝是:广告变现里**真实广告联盟 SDK 当前是待接的**——计费业务逻辑已真(mock 可端到端跑通),但接真实广告联盟受日历闸门(广告资质)约束(详见运营域合规闸门与需求映射现状快照),所以这条变现在产品口径上成立、在落地上还卡着外部资质。 - -### 图 9 · 商业定位与护城河一图〔SVG新·讲·现〕 - -![图 9 · 商业定位与护城河一图](assets/09-商业定位与护城河.svg) - -这张图回答「绘境AI 在市场上是谁、靠什么赢」——它把商业定位、竞争格局、资本效率与护城河路径压进一张图,是面向投资人尽调与对外沟通的总览。它也是本图说**最该守诚实纪律**的一张:护城河话术的核心铁律是「**不假装有护城河**——卖的是『不公平起跑 + 飞轮与生态护城河的路径』,任何一处言过其实都会击穿全局信任」;所以图上把「现在真有的」和「要去点燃的」用实心块与紫色虚线框严格分开,绝不让人误以为护城河已经建好。 - -图分四层来读。**最上一句话定位**:绘境 = 唯一把「做得出 → 有人玩 → 赚到钱」接成一条闭环的 AI 游戏生态平台,竞品停在生成工具,绘境做全链路——并明确点出「生成是入场券,不是壁垒」。**差异化闭环**那一行把三件事接成链,并在右侧钉死一个最容易被误解的点:护城河真正的来源是数据/网络效应/资产/合规四层,**不是生成引擎**——大模型会追平生成,但追不平这四层沉淀。**中段左侧竞争格局**用三家竞品(极逸 SOON 自研最强但无流量无变现、TapTap 制造有流量但封闭分成低、FunloomAI 有付费验证但品类窄无流量)说明绘境的避战策略:不在技术深度上硬碰,而抢「生态完整度」这条没人占的轴,结论是「技术深度认输,生态完整度抢第一」。**中段右侧资本效率**把「现在真有的不公平起跑」量化——约 1/5 成本、约 1/3 时间、钱撑 18-24 月,策略是「能用成熟开源/商用组合的绝不自研,自研只投在必须自掌的控制点 + 数据入口」。 - -整张图最该让人看清的是**底部那一栏的诚实切分**:护城河的四层(数据壁垒 / 网络效应 / 资产壁垒 / 合规壁垒)全部用紫色虚线框 + 「未来式」小标画出,因为它们是**要在窗口期(6-12 个月)里点燃的、而不是已经有的**——短期没有护城河,有的是上面那三样不公平起跑,融资就是点火的燃料钱。这层切分必须看清:如果把飞轮、网络效应、生态资产画成「已有的墙」,就违背了护城河话术的红线。要补充一处源档里的设计缝(图上未展开、读图时一并记住):对外话术与内部判断有一处**刻意的时间差**——护城河话术 B3 对外仍说「复杂引擎用 Cocos / 不自研引擎」,但内部已改判 tier2 富游戏自治轨走 AgentScope(Python 自治 ReAct)+ Phaser/Pixi 全无头引擎、Cocos 收窄到 3D / 复杂场景 / 渠道导出轴(仍是有效决策、非废弃);因为 tier2 还在 0 号 spike 待验,没验证的东西不对外背书,等 spike 过了再同步对外口径。这处「内外口径暂不一致」是有意为之、有据可查,不是漏洞。 - ---- - -## 3. 图清单与状态表 - -| # | 图名 | 家族 | 形式 | 状态 | 覆盖内容 | -|---|---|---|---|---|---| -| 1 | 产品闭环全景 | 流 | 引(链 README §1) | 现 | 做得出→有人玩→赚到钱 闭环主干 + 数据回流;所有产品功能挂靠的主干 | -| 2 | 19 个产品域 × 五大块 | 架 | SVG新 | 现 | 创作/玩家/变现/成长/平台 五大块归拢 19 域 + ⭐三 P0 密集域 + 块间流转 + tier2 第二轨边界(虚线·待建) | -| 3 | 155 → 55 P0 收敛 | 流 | 引(链 README §4) | 现 | 155 条完整需求 → 55 条 P0(MVP 验收口径)→ 覆盖全链路闭环;不追功能多、追闭环跑通 | -| 4 | 需求编号体系 | 讲 | Mer新(内联) | 现 | `P-{域}-{序号}` + 端/优先级/来源 四属性怎么读;经营≠运营 易错点 | -| 5 | 需求 ↔ 模块 M:N 映射(RTM) | ER | SVG新 | 现 | 产品需求(155 P-id) ↔ 技术模块(204 T-id) 多对多矩阵 + 首要owner/★主/辅 读法 + 两块待建映射(虚线·post-spike) | -| 6 | 五大块产品域关系 | 架 | Mer新(内联) | 现 | 创作产出→玩家消费→变现回流→创作者留存 的块间流转 + 两条回流虚线(飞轮)+ 平台侧作底座 | -| 7 | 创作者旅程 | 时 | Mer新(内联) | 现 | 挑素材→一句话生成→预览迭代→发布→看收益;生成迭代闭环 + 外部渠道远期标注 | -| 8 | 玩家旅程 | 时 | Mer新(内联) | 现 | 刷feed→即点即玩→点赞分享→发起同款;即点即玩差异化 + 同款回路接回创作者旅程 + 广告 SDK 待接 | -| 9 | 商业定位与护城河一图 | 讲 | SVG新 | 现 | 一句话定位 + 差异化闭环 + 竞争格局 + 资本效率 + 四层护城河(虚线·未来式,诚实不假装有墙)| - -> **状态分布**:九张图整体状态全为 **现**(产品定义层)。其中三处以**元素级虚线**标出 future-state、绝不画成现行 MVP 范围:图 2 / 图 5 的**第二条生成轨(tier2)与治理层**(待 0 号 spike·未落代码·尚无正式 P-id)、图 9 的**四层护城河**(要在窗口期点燃·未来式·非已有)。**防漂移门**:本图说 frontmatter 记五份源档的 commit hash(README @ 366a44b4、需求清单 @ 15b707fd、需求模块映射 @ 7ecd616e、商业定位 @ f4ee2310、护城河话术 @ 5cadf504),源档变更即比对,hash 对不上则本图说与对应 SVG 标「待复核」。 -> -> **产品定义 ≠ 建设进度**:本图说画的是「产品要提供什么」(55 P0 = 验收口径),不是「代码建到哪一步」。55 条 P0 各自的真实完成度(真端到端 / 半真 / 桩)以 [`需求模块映射.md`](需求模块映射.md) 现状快照与 [`docs/mvp/MVP进度总账`](../../mvp/MVP进度总账.md) §2 矩阵为唯一 SoT,本图说不重复承载。 -> -> **PNG 后续**:9 张图中 3 张为 SVG(图 2/5/9),SVG 入仓后在 mini-desktop 批量转 PNG(6c6g 禁 chrome);4 张 Mermaid(图 4/6/7/8)GitHub 直接渲染、无需转换;2 张(图 1/3)单源引用 README、不产新图。 diff --git a/docs/architecture/前端/前端图说.md b/docs/architecture/前端/前端图说.md deleted file mode 100644 index f9f830e6..00000000 --- a/docs/architecture/前端/前端图说.md +++ /dev/null @@ -1,201 +0,0 @@ ---- -date: 2026-06-22 -topic: 前端域图说——把「绘境AI 有哪两个前端、靠什么撑起一致体验、生成出的游戏怎么在前端真玩」画成一套图 + 讲解(架构图集第一期·前端域) -status: 现行为主(图 1–8)+ 待建一张(图 9 tier2 富游戏前端承载)· 防漂移门记源档 commit hash -映射源档: - - docs/architecture/前端/README.md @ 15b707fd - - docs/architecture/架构/产物执行沙箱.md @ 38357c3d - - docs/architecture/架构/生成引擎/引擎与运行时.md @ 7ecd616e ---- - -# 前端域图说 - -> **这是什么**:绘境AI 前端域的「看图入口」。前端域回答三件事——绘境AI 有**哪两个前端、各服务谁**、它们靠**一套什么样的设计体系撑起一致的体验**(颜色、间距、字体、组件、主题、多语言这些视觉与交互的共同语言怎么统一沉淀)、以及生成出的游戏**怎么在前端被真正地玩起来**。这份图说把这三件事,连同两件跨域的事(试玩宿主的安全隔离、tier2 富游戏的前端承载),用一套图加配套讲解画清楚。复杂、跨层的用手绘 SVG 大图,简单、线性的用 Mermaid,已有 README 内嵌的 Mermaid 一律单源引用、不复制。 -> -> **给谁看**:前端工程师、设计师、产品经理、新加入的同事,以及做技术尽调的人。判断「这套前端方案是不是想要的那个」看这份图就够建立全局轮廓,逐令牌的取值仍去代码里的 `game-studio/src/styles/tokens.css`,逐组件实现去 `.agents/skills/` 下的操作手册。 - ---- - -## 0. 阅读约定与同步纪律 - -- **映射的设计档**:本图说不另立设计,只把前端域的现行设计画出来。设计体系(两个前端、token 两层、组件库四层、三条数据流、三大契约、主题切换、视图地图)全部出自前端域的 canonical 设计主文档 [`前端/README.md`](README.md);试玩宿主那张图(图 8)的安全隔离细节交叉引用 [`架构/产物执行沙箱.md`](../架构/产物执行沙箱.md);tier2 富游戏承载那张图(图 9)的装载侧细节交叉引用 [`架构/生成引擎/引擎与运行时.md`](../架构/生成引擎/引擎与运行时.md)。frontmatter 里记了这三份的当前 commit hash,作为**防漂移门**:源档一旦变更、hash 对不上,这份图说与对应 SVG 即标「待复核」,由收口脚本比对。 -- **同步纪律**:**设计一变动,本图说与对应 SVG 必须同步更新**,每张 SVG 脚注注明映射源档与状态。已并入 wave 收口清单。 -- **本域状态分布**:前端域的设计体系已全量收口并合入主干 `dev/2.0.0`,所以**九张图里八张是「现」**(现行已建);唯一的例外是图 9——tier2 富游戏的前端承载,归属已定(归前端域)但**代码未建**,整图状态是「建」(待建),全图用虚线画、并在图顶加一条状态约定条,绝不画成已建。本域不像运维域那样三态交织,但「现」与「建」这条边界仍要看清:别因为设计体系本身已收口,就误以为第二条生成轨(tier2)的前端也建好了。 -- **产物形式**:SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 转(6c6g 禁 chrome、无转换器)。本图说只出 SVG。 - -## 1. 全图通用图例 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上,前端域主要关心其中三个): - -- **质量**(`#15803d`):换肤零改组件、向后兼容别名、迁移期新旧共存、三条数据流解耦、业务域分块导航、统一圆角矩形——设计体系的一致性与可维护性是前端域的主轴。 -- **数据**(`#475569`):token 作为「跨端单一事实源」、品牌字符串收敛到 `common.brand` 一个 key、逐令牌权威清单只在 `tokens.css` 一处——同一份事实只有一处权威定义。 -- **安全**(`#dc2626`):试玩宿主的 iframe 沙箱 + CSP + postMessage 双校验、路由守卫只认真实 token、匿名 `anonId` 衔接未登录态、`Icon` 禁用 emoji。 - -(前端域很少涉及**可观测** `#0ea5e9`、**可靠** `#16a34a`、**成本** `#16a34a`、**伸缩** `#7c3aed` 这几个维度;它们主要落在后端 / 运维域。tier2 承载那张图会点到一点伸缩——第二条生成轨让 feed 长成承载两类产物的能力。) - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现成直接用、同步消费、同业务域内的跳转。 -- **虚线**(`#64748b`,`stroke-dasharray="6 4"`):自建 / 解耦 / 远期铺路 / 待建的目标态、跨业务域的流转。 -- **双线 / 加粗框**:跨域共享的公共件或需要强调的边界。 - -> 前端域图说里,虚线还额外承担一层**状态语义**:图 9(tier2 富游戏前端承载)整图是「待建」,所以整张用虚线框画、配状态约定条;而图 2 设计 token 里「跨端使命」那一格虽然在「现」图里,但它本身是远期铺路(当前只服务 Web),所以也用虚线单独框出。一眼区分「现行已建的实心块」与「远期 / 待建的虚线框」,是本域要守的纪律点——设计体系本身收口了,但不能让人误以为跨端 App、tier2 富游戏承载也跟着建好了。 - ---- - -## 2. 图集 - -### 图 1 · 两个前端定位〔引·现〕 - -> 单源引用,不复制:「两个前端定位」的 Mermaid 已在 **[前端/README.md §1](README.md#1-绘境ai-有两个前端)** 内嵌,是该图的唯一来源。这里只链接、不抄一份进来——同一张图存两处、改一处漏一处,正是这份图集要消灭的漂移面。 - -这张图回答「绘境AI 到底有几个前端、各是给谁的」。答案是**两个独立的前端应用**,刻意拆开,因为服务的人和承载的能力截然不同。**game-studio** 是产品前端,同时服务**创作者**(一句话生成游戏、预览迭代、发布、看收益)和**玩家**(刷短视频式的竖屏游戏信息流、点赞分享评论),用 **Vue3 + Vant**——Vant 是移动优先的 Vue 组件库,天然适配游戏流的滑动交互。**game-admin** 是管理后台前端,服务**平台运营**和**管理员**(内容审核、用户管理、数据看板、合规处置),用 **Vue3 + Element Plus**——这是绘境AI fork 的开源 Java 后台框架 Huijing 官方主推的桌面端组件库,二次开发友好。 - -读这张图要抓住一条范围边界:设计体系的建设**当前聚焦 game-studio**,因为它直面大众市场、卖相与一致性要求最高、投入产出比也最高;game-admin 是内部后台工具,沿用 Element Plus 默认体系即可,只在品牌色这一层与 studio 对齐。所以下面图 2 到图 7 讲的「设计体系」,除非特别标注 admin,默认都是 studio 的。这张图没有现行 / 远期的灰度,它就是当前真相;它和这份图集里别的域的关系是——「前端域只讲两个前端长什么样、靠什么撑起一致体验」,产品对用户提供什么记在[产品域](../产品/README.md)、系统用什么技术搭起来记在[架构域](../架构/README.md),各司其职、互不重复。 - -### 图 2 · token 两层 + 组件库四层(前端域最核心)〔SVG升·架构·现〕 - -![图 2 token 两层 + 组件库四层](assets/02-token两层与组件库四层.svg) - -这是整个前端域**最核心的一张架构图**,它由两条主线组成,理解了它就理解了设计体系为什么这么搭。左边是**设计 token 的两层结构**:`primitive 原始层`只描述「物理事实」(`--cyan` 是某个青色、`--sp-3` 是 12px,它只是调色板和尺子,不带含义),`semantic 语义层`描述「用途」(`--bg` 是页面背景、`--accent` 是强调色、`--danger` 是危险色、`--surface-1..3` 是从底到顶的层叠表面)。右边是**组件库的四层封装**:`L0 token`把令牌落地成 CSS 变量、`L1 Vant 适配`把 Vant 的 `--van-*` 变量映射到我们的语义层、`L2 品牌基元`封装一批带绘境AI 气质的基础组件(AppButton/Field/Icon/Card/Chip/EmptyState/NavHeader/TabBar/Skeleton/Toast)、`L3 业务组合`把基元拼成 GameCard/InteractBar/FilterTabs 这些业务组件。两条主线在**语义层交汇**——图上用最深的蓝色把语义层高亮出来,因为它是整个体系的支点。 - -这张图最该让人看出的,是 token 为什么要分两层、组件库的纪律是什么。**分两层的关键好处是「换肤时零改组件」**:组件只准引用语义令牌、绝不直接碰原始层;主题切换只需在语义层重新指向不同的原始色(暗色主题下 `--bg` 指向深色、浅色下指向浅色),组件因为只认 `--bg` 这个语义名,会自动跟着变,一行组件代码都不用动。图上把这条写进了高亮的语义层框里。**L1 那层的价值是「白嫖 Vant」**:换的只是皮肤变量,Vant 社区打磨多年的交互、无障碍、键盘支持原样吃下,不必自研——这是「复用现成」(实线)而非「自建」的典型。这张图里有一处刻意用虚线画的设计缝:左下角「跨端使命」那一格是**远期铺路、当前只服务 Web**——同一份 token 现在只喂 Web 的 CSS 变量,未来桌面 App 复用 Web 技术栈直接沿用、移动 App 把同一份 token 导出 JSON 喂 React Native / Flutter,但这些都还没做。所以它虽然画在「现」图里,本身却是远期,用虚线框 + 「远期」小标单独标出,免得有人以为跨端 App 也建好了。 - -### 图 3 · 三条数据流:token / theme / i18n〔SVG新·流·现〕 - -![图 3 三条数据流 token/theme/i18n](assets/03-三条数据流.svg) - -这张图回答「设计体系在运行期是怎么动起来的」。如果说图 2 是设计体系的**静态结构**,那图 3 就是它的**三条单向数据流**,把那张静态图里的层「跑活」。**① token 流**:`L0 token → L1 vant-theme → L2 品牌组件 → L3 业务面`,自底向上逐层消费——这是图 2 组件库四层在运行期的数据走向,组件只认下层语义名,所以换肤、跨端只动语义层一处。**② theme 流(换肤)**:`localStorage(studio.theme) → → 语义层覆盖 → 全组件自动反应`,源头是用户在设置里的选择(持久化在 `localStorage`,无值时一律回落暗色),`[data-theme]` 只覆盖语义层、不碰原始层。**③ i18n 流(国际化)**:`main.ts 注册 vue-i18n → 组件用 useI18n($t) 取文案 → localStorage(studio.lang) 与 同步`,硬约束是禁组件内出现裸中文、缺失 key 回退 `zh-CN`、数字日期货币走原生 `Intl` 本地化。 - -这张图最该让人看出的,是三条流**互不耦合、各有清晰的源头与终点**,正是这种解耦让「换肤」和「换语言」都做到零改组件。图上还点出两处共享:theme 流和 i18n 流**共用同一套 `localStorage` + `` 属性机制**——首屏内联脚本一并把 `data-theme` 和 `lang` 设好(这一步既是 theme 流防 FOUC 的关键,也是 i18n 流的语言初始化,图 6 会把这个时序单独放大);而 token 流的「为何单向」框里写明了一条贯穿全域的纪律——每层只认下层语义名,所以无论换肤、跨端还是长期演进,都只动语义层一处。i18n 流终点那个「品牌字符串收敛一处」(`common.brand` 一个 key,值 = 「绘境AI」,改名只改一处)和 token 的「跨端单一事实源」是同一种思路:同一份事实只有一处权威定义,这是前端域「数据」徽章的由来。 - -### 图 4 · demo 取舍〔引·讲·现〕 - -> 单源引用,不复制:「demo 取舍」的 Mermaid 已在 **[前端/README.md §3](README.md#3-一个核心判断学-demo-的设计语言不学它的布局范式)** 内嵌,是该图的唯一来源。这里只链接、不抄一份进来。 - -这张图回答「设计体系从哪里起步、为什么是现在这个形态」。绘境AI 手上有一份早期的投资人路演原型 `docs-design/huijing-ai-demo.html`(简称 demo),对它做了一个**决定整个前端形态的取舍判断**:**保留** demo 的「设计语言」(token 体系、`cyan→green` 的品牌渐变、暗色玻璃质感,这套视觉语言有辨识度、值得沿用),**否决** demo 的「布局范式」(它是一个桌面产品外壳——固定 236px 宽的左侧栏 + 1140px 宽的工作台,把短视频流硬塞进一个 390×660 的模拟手机框里展示,这是投资人路演用的「控制台」叙事,不是真正给大众用户的消费级界面)。 - -读这张图要抓住一个产品判断:现行定位因此是**响应式优先的消费级 Web**——移动端做沉浸式全屏体验、桌面端做居中放大,一套组件适配两端;原生桌面 App、原生移动 App 都是更远期的层级,现在不做,但设计 token 提前为它们铺好了路(正是图 2 那个虚线的「跨端使命」)。这张图的设计缝在于:它是一份**记录型结论**,目的是防止后人把 demo 当成目标、反而削弱了 studio 已有的真实能力——README §7 专门列了一份「勿误砍 / 勿误判清单」,比如 studio 比 demo 多出来的「真实试玩宿主」(图 8 那套)是护城河命门、绝不能因为「demo 里没有」就砍掉,而「首页门户 / 素材中心 / 玩法模板市场屏」这些是有意的 MVP descope、不是缺陷、别去返工。看这张图时把这条边界记住:demo 给的是设计语言,不是产品蓝图。 - -### 图 5 · 三大契约:token / 组件 / i18n〔SVG新·类·现〕 - -![图 5 三大契约 token/组件/i18n](assets/05-三大契约.svg) - -这张图回答「设计体系对外暴露哪些不能私自破坏的约定」。它把三套契约并排画出来,每套是一个 `contract`(跨人协作时大家都遵守的约定)。**契约① token**:命名空间是 CSS 自定义属性,旧令牌(`--cyan`/`--green`/`--radius`/`--shadow`)全保留、其中 `--cyan` 被设为新语义令牌 `--accent` 的**向后兼容别名**,新增 primitive 原始层 / semantic 语义层 / 尺度令牌三组。**契约② 组件**:`AppButton` 的 props(`type/size/block/loading/disabled`)保持不变、向后兼容,primary 主按钮恢复了品牌签名渐变并补齐全部交互态(focus 焦点环 / disabled 0.5 透明 / active 按压),`Icon` 组件禁用 emoji。**契约③ i18n**:用 `vue-i18n@10`,文案按业务域划分命名空间,品牌字符串收敛到 `common.brand` 一个 key。 - -这张图最该让人看出的,是三套契约**共守一条 additive(增量式)原则**——图顶用一条绿色总纲条钉死:只增不毁,旧引用零破坏,各页面在迁移期新旧共存、逐面切换,绝不一刀切。这条原则是设计体系能平滑落地、不把现有页面改崩的根本。图上还标了两条与别的图呼应的纪律:token 契约里「三档主题 `[data-theme=light|dim]` 只覆盖语义层、不碰原始层」呼应图 2 / 图 3 的换肤机制;组件契约里那条用虚线框出的「圆角铁律」(禁药丸 / 椭圆 / 正圆混用,半径按尺寸分级 `xs/sm/md/lg/xl = 4/6/8/12/20`,默认 `md=8px`,唯一例外是 loading 旋转环保留圆形)单独立了一份规则文档 `.agents/rules/ui-uniform-rounded-rectangles.md` 约束,是创始人拍板的硬约束。这张图的设计缝提示在于:逐令牌的权威清单不在图里、以代码 `tokens.css` 为准——图只画契约的骨架与命名空间,别拿它当令牌字典用。 - -### 图 6 · 主题切换 [data-theme] + 防 FOUC〔Mer新·时序·现〕 - -这张图把图 3 里 theme 流那条线**单独放大成一个时序**,讲清「用户切了主题,从点击到全屏变色,中间经过哪几步、为什么首屏不会闪」。它要解决的核心问题是 **FOUC**(Flash of Unstyled Content,即页面首帧闪现未应用样式的难看瞬间):如果等到 Vue 应用挂载后才读用户的主题偏好,那首屏会先用默认色画一帧、再「啪」地跳成用户选的色,很难看。绘境AI 的解法是在 `index.html` 的首屏渲染**之前**内联一段脚本,先从 `localStorage` 把 `data-theme` 和 `lang` 设到 `` 上,这样浏览器画第一帧时主题就已经是对的。 - -```mermaid -flowchart TB - CLICK["用户在设置内切主题
(暗 / 浅 / 柔 三档)"] --> WRITE["写 localStorage
studio.theme = light/dim/(暗=不写)"] - WRITE --> ATTR1["设 <html data-theme>
运行期立即生效"] - ATTR1 --> COVER["语义层 [data-theme] 覆盖
--bg/--surface/--accent… 重指向"] - COVER --> REACT["全组件自动反应
(组件只认语义名 · 零改代码)"] - - RELOAD["下次刷新 / 首次进入"] -.->|首屏渲染前| INLINE["index.html 内联脚本
读 localStorage 先设 data-theme + lang"]:::boot - INLINE -.->|第一帧就是对的色| ATTR1 - DEFAULT["无持久化值"] -.->|一律回落| DARK["暗色 (:root · 不写 data-theme)"]:::boot - RELOAD -.-> DEFAULT - - classDef boot fill:#f5f3ff,stroke:#7c3aed,stroke-width:1.5px,stroke-dasharray:5 4; -``` - -这张图最该让人看出的,是**防 FOUC 靠的是「首屏内联脚本」这个时机点**——图上用紫色虚线把这条「下次刷新 / 首次进入」的引导路径单独画出,它不是主交互流(实线那条是「用户当场切主题」),而是页面加载时的初始化路径,两条路殊途同归地把 `data-theme` 设对。还有一处产品默认值要记住:**首次进入默认暗色**(`:root`,不写 `data-theme`),之后才由用户在设置内显式切换;无持久化值时一律回落暗色,图上用虚线标出这条回落。这张图的设计缝只有一个:跟随系统的 `prefers-color-scheme` 信号是远期增强、当前尚未接入——代码只读 `localStorage`、不读系统信号,所以现在「暗 / 浅 / 柔」三档纯由用户手动选,不会跟着系统深浅色自动变。这条边界别误判成缺陷,它是有意的现行口径。 - -### 图 7 · studio 视图地图〔SVG新·架构·现〕 - -![图 7 studio 视图地图](assets/07-studio视图地图.svg) - -这张图回答「studio 到底有哪些页面、怎么按业务组织、怎么导航」。它把 studio 的全部路由视图按**五个业务域**布局:**玩家消费**(Feed 游戏信息流 / ZoneFeed 专区流 / Play 试玩宿主 / Share 分享落地页)、**创作**(Create 入口 / Task 生成进度 / Preview 预览 / Project 我的项目 / Detail 详情 / Publish 发布门禁)、**平台与成长**(Login / Profile / Message / Dashboard / CreatorHome)、**studio 专区**(Material 素材中心 / Template 玩法模板中心)、**B 端定制**(BizLeads / BizCreate / BizLeadDetail)。底部 4 个 tab 标签栏(游戏流 / 创作 / 消息 / 我的)是主导航,其余视图经卡片点击、详情跳转、深链进入。 - -这张图最该让人看出的,是**视图按业务域聚类后的导航脉络与准入边界**。玩家侧有一条主消费回路:Feed → 点卡片 → Play → 点分享 → Share;消费域到创作域之间有一条跨域流转(玩家「发起同款」从消费走向创作),图上用虚线标出。准入边界是这张图的安全主轴:路由守卫只认真实 token(`isRealLogin`),匿名 `anonId` 衔接未登录态——Feed / ZoneFeed / Play / Share 匿名可达(即刷即玩不挡门、玩家无需登录就能消费,图上用绿框标),而 Create / Project / Publish / Biz / Dashboard 必须登录(未登录访问跳 `/login` 并带 redirect 回跳,图上用红框标)。这张图有两处刻意钉住的口径,看图时别误判:其一,studio 专区里的「玩法模板中心」是**品类框架入口**,与 demo 那块已被 W-CLEAN 废除的「填参式模板市场」是两回事,图上专门注明、勿混;其二,**视图计数口径**——图列了 20 个有独立路由的视图,而 README §8 写「19 视图」,差的那 1 个是 U9 新增的 CreatorHome 公开聚合主页,本图以 `router/index.ts` 实际路由为准、并把 `StatusBadge.vue`(Project 域内的子组件、非独立路由)明确排除在外。这个「20 vs 19」的小漂移是真实的、据实标出,不臆造对齐。 - -### 图 8 · 真实试玩宿主〔Mer新·时序·现〕 - -这张图回答「玩家点了一款 AI 生成的游戏,它怎么在浏览器里真正跑起来——而不是像 demo 那样只弹个 toast」。这是 studio 比 demo 多出来的、**护城河命门**级别的能力:真实试玩宿主。它是一条时序——`GamePlayer.vue` 先按 `versionId` 取运行包清单、对 manifest 算 sha256 与后端写包时的 checksum 严格比对(不过就退 demo 兜底),再用 `inject.ts` 把游戏代码 + SDK + 引擎 bundle 拼成 srcdoc 注入一个带 `sandbox` 属性的 iframe,挂上 `HostBridge` 桥,iframe 内 `boot()` 启动后经 postMessage 把 `game_loaded` / `game_start` 回流给宿主、宿主再 emit 给上层 Feed/Play。 - -```mermaid -sequenceDiagram - participant FE as Feed / Play (上层) - participant GP as GamePlayer.vue (宿主) - participant IJ as inject.ts (srcdoc 注入) - participant IF as iframe (sandbox · origin=null) - participant BR as HostBridge (桥) - - FE->>GP: 点游戏卡片 → 进 /play/:gameId/:versionId - GP->>GP: 取包清单 → 对 manifest 算 sha256 严格比对 checksum - Note over GP: 校验不过 / 取包出错 → 退 demo 兜底(不向上 emit error) - GP->>IJ: buildIframeSrcdoc(包 + SDK + engineBundle) - Note over IJ: 函数序列化注入 + 硬编码 CSP 写进 srcdoc meta
connect-src none 禁网络出站 - IJ-->>GP: 完整 HTML 文本 - GP->>IF: <iframe sandbox srcdoc> 挂载 - GP->>BR: attachBridge(origin 白名单 + schema 双校验) - IF->>IF: boot() 启动引擎 / 兜底 - IF->>BR: postMessage(game_loaded / game_start · 经双校验) - BR->>FE: emit('ready') / emit('start') - IF->>BR: postMessage(game_end · 宿主轮询 latch 代发) - BR->>FE: emit('end') -``` - -这张图最该让人看出的,是**前端域在这里只画「宿主容器 + 桥」这一侧,安全隔离的纵深防御交叉引用沙箱图说**。前端域要关心的是:游戏与宿主之间**只有 postMessage 一条通道**,而桥把 iframe 来的每条消息当不可信边界——先过 origin 白名单(空白名单默认拒绝)、再过 schema 逐字段校验(`channel` 必须等于 `wanxiang-game-sdk`、`type` 必须在 8 个枚举内),任一不过就丢弃、绝不分发。这套**双校验**是前端这一侧的安全支点。图上还点出一个有意的过渡口径:游戏没有自己的 emit 通道发 `game_end`,所以**宿主轮询 `host.state().phase`、到 `gameover` 就 latch 一个 `game_end` 代发**(图上标「宿主轮询 latch 代发」)——这是 ref 游戏纯引擎工厂、内部没 SDK 的现实下的过渡桩,W-G1 生成主线落地后改由各品类标准信号产出。 - -这张图的设计缝必须看清,且**别误判成已合规**:iframe 的 CSP、sandbox 属性、sha256 校验这套机制都已建成、真跑通过,但**整套现在跑在「同源过渡态」**(同源 srcdoc + `allow-same-origin`),还有三处真实债——CSP 文档口径与代码对不上(文档写 `script-src 'self'`、代码实际是 `'unsafe-inline' 'unsafe-eval'`)、`sandbox` 属性三处不一致、后端下发的 `sandboxAttr` 前端根本没接线。这些债的 file:line 与收敛方向(游戏包迁独立 usercontent 子域、CSP 改 HTTP 响应头下发、sandboxAttr 前端真接线)逐条记在沙箱图说与 **[`架构/产物执行沙箱.md`](../架构/产物执行沙箱.md) §8**,是这张图的权威细节来源。看这张图时记住:机制建成 ≠ 合规收口,安全隔离的完整故事在沙箱域,本图只画前端宿主侧的装载与桥。 - -### 图 9 · tier2 富游戏前端承载〔Mer新·架构·建(归属已定·待建)〕 - -这张图回答「第二条生成轨(tier2 富游戏)落地后,前端要多做哪两件事」——也是本域**唯一一张状态非「现」的图**。它整体是**建**(待建):绘境AI 除了现行的 Tier0/1 超休闲廉价线(SAA + LittleJS,产物是单包),另起了第二条生成轨 tier2(AgentScope Python 自治 agent 造多系统富游戏,产物是真 Phaser 多文件源工程)。tier2 给前端域带来两件**现在还没做、但归属已定(归前端域)**的事,所以整张图用虚线画、图顶加一条状态约定条说明整图状态。 - -```mermaid -flowchart TB - subgraph BAR["状态约定:整图 = 建(待建)· 虚线框 = 归属已定、代码未建 · 等 tier2 临近过 0 号 spike 再展开设计"] - end - - FEED["game feed 信息流
(现行:承载 LittleJS 单包)"]:::now - subgraph LOAD["① feed 双轨装载分发(待建·归前端域)"] - direction LR - T1["现有轨:LittleJS engineBundle 单包
随 manifest 内嵌 · 已建"]:::now - T2["第二轨:tier2 Phaser 多文件工程
经独立源项目契约构建出 bundle"]:::todo - end - subgraph ENTRY["② studio 创作端第二轨入口(待建·归前端域)"] - direction LR - E1["超休闲廉价线入口
(现行 Create)"]:::now - E2["富游戏 premium 线入口
(待建)"]:::todo - end - - FEED --> LOAD - T1 -.->|两轨共用同一套 iframe+CSP+桥
都暴露 __GameBundle / 都调 bootGameHost| SANDBOX["执行沙箱(公共件·不随轨变)
详见图 8 + 沙箱图说"]:::shared - T2 -.-> SANDBOX - ENTRY -.->|创作端体现两条生成线分轨| LOAD - - classDef now fill:#dcfce7,stroke:#16a34a,stroke-width:1.6px; - classDef todo fill:#faf5ff,stroke:#7c3aed,stroke-width:1.5px,stroke-dasharray:5 4; - classDef shared fill:#e2e8f0,stroke:#0f172a,stroke-width:2px; -``` - -这张图最该让人看出的,是**前端域在 tier2 这件事上「钉归属、不预先设计」的克制**。两件待建的事是:其一,**feed 双轨装载分发**——feed 要同时承载两种产物(现有 LittleJS 单包 + tier2 Phaser 多文件工程),按产物类型分发到不同装载路;其二,**studio 创作端的第二轨入口**——创作端要体现「超休闲廉价线 / 富游戏 premium 线」两条生成线,给富游戏一个独立入口。图上用绿色实心块画现行已建的部分(现有 feed、LittleJS 单包轨、现行 Create 入口),用紫色虚线画待建的部分(第二轨、premium 入口),一眼区分。 - -这张图有一条最关键的边界要看清,它决定了前端域的工作量没有想象中大:**两条装载轨共用同一套执行沙箱**——不管哪条轨出来的 bundle,都暴露 `window.__GameBundle` 全局名、都调 `bootGameHost`、都跑在同一套 iframe + CSP + 桥里(图上用深色实心块把「执行沙箱」画成公共件,两轨都虚线指向它)。tier2 换的是「游戏内容怎么造出来」,换不掉「造出来之后怎么被关进笼子跑」。所以前端域要补的只是**运行时容器的双轨分发 + 创作入口**,装载与渲染的 how 在架构域、见 **[`架构/生成引擎/引擎与运行时.md`](../架构/生成引擎/引擎与运行时.md)**(其 §7.3 把「tier2 第二装载分支」列为待开项)与 `架构/生成引擎/tier2实现详设.md`。这张图的设计缝就是它的状态本身:**整套是待建**,详细前端设计要等 tier2 临近过 0 号 spike 再展开,现在只钉归属、不预先设计——绝不能因为图画出来了,就误以为第二条生成轨的前端已经建好。 - ---- - -## 3. 图清单与状态表 - -| # | 图名 | 家族 | 形式 | 状态 | 覆盖内容 | -|---|---|---|---|---|---| -| 1 | 两个前端定位 | 架 | 引(链 README §1) | 现 | game-studio(创作者+玩家·Vue3+Vant)/ game-admin(运营+管理员·Vue3+Element Plus)+ 设计体系聚焦 studio 的范围边界 | -| 2 | token 两层 + 组件库四层 | 架 | SVG升 | 现 | primitive→semantic→component 两层 + L0/L1/L2/L3 四层,语义层交汇 + 换肤零改组件 + 跨端使命(远期虚线) | -| 3 | 三条数据流 token/theme/i18n | 流 | SVG新 | 现 | token 自底向上 / theme localStorage→data-theme 换肤 / i18n vue-i18n 文案,三流解耦 + 共用一套 localStorage 机制 | -| 4 | demo 取舍 | 讲 | 引(链 README §3) | 现 | 留设计语言(token+渐变+暗色玻璃)/ 弃布局范式(桌面侧栏控制台)→ 响应式优先消费级 Web + 勿误砍清单 | -| 5 | 三大契约 token/组件/i18n | 类 | SVG新 | 现 | additive 共守原则 + 向后兼容别名(--cyan→--accent)+ AppButton props 不变 + Icon 禁 emoji + 圆角铁律 + common.brand 单一源 | -| 6 | 主题切换 [data-theme]+防 FOUC | 时 | Mer新(内联) | 现 | localStorage→首屏内联脚本→data-theme→语义层覆盖→组件自动反应;首次默认暗色 + 无值回落暗色;prefers-color-scheme 远期未接 | -| 7 | studio 视图地图 | 架 | SVG新 | 现 | 20 路由视图按五业务域布局(玩家消费/创作/平台成长/studio 专区/B 端)+ 4 tab 导航 + 匿名可达/必须登录准入边界 + 20vs19 计数口径 | -| 8 | 真实试玩宿主 | 时 | Mer新(内联) | 现 | 取包→sha256 校验→srcdoc 注入→iframe sandbox→桥双校验→postMessage 回流;game_end 宿主轮询 latch 代发;同源过渡态 + 三债交叉引沙箱 §8 | -| 9 | tier2 富游戏前端承载 | 架 | Mer新(内联) | **建** | feed 双轨装载分发 + studio 第二轨创作入口(归前端域·待建);两轨共用同一套沙箱(公共件);钉归属不预先设计,等 0 号 spike | - -> **状态分布**:现 ×8(图 1–8,设计体系已全量收口合入 `dev/2.0.0`)、建 ×1(图 9,tier2 富游戏前端承载归属已定但代码未建·整图虚线)。**防漂移门**:本图说 frontmatter 记三份源档的 commit hash(前端/README @ 15b707fd、产物执行沙箱 @ 38357c3d、引擎与运行时 @ 7ecd616e),源档变更即比对,hash 对不上则本图说与对应 SVG 标「待复核」。 -> -> **PNG 后续**:9 张图中 4 张为 SVG(图 2/3/5/7),SVG 入仓后在 mini-desktop 批量转 PNG(6c6g 禁 chrome);3 张 Mermaid(图 6/8/9)GitHub 直接渲染、无需转换;图 1/4 单源引用 README、不产新图。 diff --git a/docs/architecture/后端/后端图说.md b/docs/architecture/后端/后端图说.md deleted file mode 100644 index 4033cdd3..00000000 --- a/docs/architecture/后端/后端图说.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -date: 2026-06-22 -topic: 后端域图说——把「这 13 个业务模块在工程上怎么落地、数据怎么跨域连、谁能做什么」画成一套图 + 讲解(架构图集第一期·后端域) -status: 全域现行(现 ×8)· 防漂移门记源档 commit hash -映射源档: - - docs/architecture/后端/README.md @ 38357c3d - - docs/architecture/后端/数据模型.md @ 38357c3d - - docs/architecture/后端/鉴权与权限.md @ 38357c3d - - docs/architecture/架构/13模块.md @ 7ecd616e ---- - -# 后端域图说 - -> **这是什么**:绘境AI 后端域的「看图入口」。后端域回答的是服务实现视角的几个问题——这 13 个业务模块在工程上**怎么摆**(包结构、两段式、端前缀、错误码分段)、全平台的数据**长什么样、怎么跨域连**、以及「谁能做什么」这条鉴权约束在代码里**如何强制**。这份图说把这几件事,用一套图加配套讲解画清楚。复杂、跨模块的用手绘 SVG 大图,简单、线性的用 Mermaid,已有的图一律单源引用、不复制。 -> -> **给谁看**:要动后端代码的工程师、做后端或数据侧 code review 的人、排查跨模块依赖与鉴权边界的人。判断「这套后端结构是不是想要的那个」看这份图就够建立全局轮廓;逐个 T-id、逐列字段、逐条依赖仍去文末三个权威源(13模块.md / game-cloud `.agent` / 各源档)。 - ---- - -## 0. 阅读约定与同步纪律 - -- **映射的设计档**:本图说不另立设计,只把后端域四份 canonical 设计档画出来——工程落地(分层、两段式、端前缀、错误码、单体形态)出自 [`后端/README.md`](README.md),全平台数据模型出自 [`数据模型.md`](数据模型.md),鉴权与权限出自 [`鉴权与权限.md`](鉴权与权限.md),模块边界与业务链路另引架构域的 [`13模块.md`](../架构/13模块.md)。frontmatter 里记了这四份的当前 commit hash,作为**防漂移门**:源档一旦变更、hash 对不上,这份图说与对应 SVG 即标「待复核」,由收口脚本比对。 -- **同步纪律**:**设计一变动,本图说与对应 SVG 必须同步更新**,每张 SVG 脚注注明映射源档与状态。已并入 wave 收口清单。 -- **本域状态分布**:后端域八张图**全部是「现」(现行已建)**——它们画的都是已落代码、已 e2e 验证过的工程结构与数据形态,不存在「设计已出待接线」或「远期目标态」的图。这一点和运维域不同(运维域有观测待接、k3s 缓做两类)。但「现行已建的结构」里仍有几处**设计缝与桩**必须照实标出:contracts 的 DB 镜像已漂移、compliance 检测原子恒返回 pass、feed 游标分页是桩、网关双重校验是未来态而非现状——这些图上都用文字或虚线如实点出,绝不因为整体是「现」就把桩画成真。 -- **产物形式**:SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 转(6c6g 禁 chrome、无转换器)。本图说只出 SVG。 - -## 1. 全图通用图例 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上,后端域主要关心其中三个): - -- **数据**(`#475569`):跨表软关联而非物理外键、两个 quality_score 口径不互灌、play_count 三处口径只有一处权威、金额一律 BIGINT 以分计——这是图 5 / 图 6 的主轴。 -- **安全**(`#dc2626`):权限只在后端可信边界强制、B 端 RBAC 与 C 端归属隔离两套并存、创作白名单必须在后端、mock 后门生产关死——这是图 7 / 图 8 的主轴。 -- **可靠**(`#16a34a`):幂等键四范式、资金守恒不变式、统一发布编排的跨表原子事务、契约与实现解耦让模块独立演进。 - -(另四个维度 **可观测** `#0ea5e9`、**成本** `#16a34a`、**伸缩** `#7c3aed`、**质量** `#15803d` 在后端域不是主轴,本域图基本不点。) - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现成直接用、同步主调、链内逐步推进、主干强关系。 -- **虚线**(`#64748b`,`stroke-dasharray="6 4"`):自建 / 解耦 / 软关联 / 异步回灌 / 未来态。 -- **双线 / 加粗框**:跨模块共享的公共件,或需要强调的边界(如越界红线、可信边界单点)。 - -> 后端域图说里,**虚线主要承担「软关联」与「异步回灌」两层语义**:图 5 的跨表连线全是逻辑软关联(代码里一根物理外键都没有),用虚线画;图 6 的飞轮回路是异步回灌,用绿虚线画,与同步主调的实线区分开。红色虚线框另外标「越界红线」——依赖图里本不该存在、画出来是为了强调禁止的边(如图 2 里 studio 不可依赖别人的 -server)。 - ---- - -## 2. 图集 - -### 图 1 · 后端分层与依赖(实现视角)〔引·现〕 - -> 单源引用,不复制:后端分层与依赖的 Mermaid 已在 **[后端/README.md §1](README.md#1-边界声明本域--13-个后端业务模块的服务实现视角)** 内嵌,是该图的唯一来源。这里只链接、不抄一份进来——同一张图存两处、改一处漏一处,正是这份图集要消灭的漂移面。 - -这张图回答「后端是什么形状」。它把后端的实现视角浓缩成三层:最下面是 **Huijing fork 提供的基座**(system 用户/权限/OAuth2、infra 文件/任务/日志、bpm 工作流,以及 pay——pay 是 Huijing 原生模块,MVP 阶段还没接入这套单体),中间是 **13 个 game 业务模块**(本域主体),每个模块再拆成 **-api / -server 两段式**工程结构。 - -读这张图要抓住两个后端实现上的特殊归属,README 在图注里专门点了出来:其一,**pay 单独画在基座里**,因为它是 Huijing 原生模块、当前未接入单体;其二,**ip 不独立建模块,而是作为一条 seam(接缝/横切能力)寄宿在 compliance 里**。这两点不是疏漏,是有意的工程取舍,后面图 5 的数据视角会再次印证——库里确实没有独立的 `game_pay_*` / `game_ip_*` 迁移落地。 - -这张图的边界声明很清晰:后端域只回答「模块在工程上怎么实现(HOW)」,**不回答「每个模块各管什么领域、彼此怎么依赖(WHAT)」——那是架构域的职责**,在 [13模块.md](../架构/13模块.md) 里。两者各守一摊、互不重复,这样无论改架构还是改代码,都只有一处需要更新。所以这张图刻意薄;要看 13 模块各自的职责边界与依赖方向,去架构域,本图说不重画一份会过期的副本。 - -### 图 2 · 两段式模块:-api 契约面 / -server 实现面〔SVG新·类·现〕 - -![图 2 两段式模块 -api/-server](assets/02-两段式模块.svg) - -这张图把图 1 里「每个模块的两段式工程结构」放大讲清。它回答的是后端低耦合到底靠什么落地——答案是一道很硬的边界:**每个 game 模块都拆成 `-api` 和 `-server` 两个 Maven 子模块,跨模块只准依赖对方的 `-api`,绝不许直接碰 `-server`**。 - -图左半边是单个模块的剖面(以 project 为范例,所有模块同构)。`-api` 是契约面,只声明、不实现:里面放枚举(状态机取值、业务常量)、错误码常量(本模块独占段,见图 4)、跨模块 DTO(模块间传输对象)、以及 Feign 接口或 `@Primary` 本地 bean(远程调用声明——单体内同进程直调,拆微服务才换真 Feign)。`-server` 是实现面,放真正干活的代码:controller(app/admin 双端)、service(业务逻辑、`@Transactional` 只加这层、归属校验在此强制)、dal(DO + Mapper)、convert(DO↔VO↔DTO 转换)。 - -图右半边讲清这道边界为什么值钱。当 studio 这样的上游要调 project / aigc / runtime 时,**它的 pom 只声明依赖各模块的 -api,编译期就根本拿不到对方的实现类**——越界在构建期就被挡死,而不是靠人自觉。好处是任一模块都能独立演进、独立测试,后续从单体拆成独立仓时不被实现耦合绊住:契约稳定、实现可换。图里特意用红色虚线框标出「这些模块各自的 -server 对 studio 不可见」,强调依赖图里**根本不存在**指向 -server 的边——这条红线画出来是为了让人一眼记住禁止什么。 - -要补充三个工程上的硬约束,图右侧也钉住了:DTO/VO/DO 三层语义不可混用(DTO 在 -api 跨模块共享、VO 在 -server controller 面向前端、DO 在 -server dal 映射表,错位会出问题);契约不放后端(API/DB/SDK/event 等 8 类跨端契约的单一事实源是仓根的 `contracts/`,后端只引用、不另立,DB schema 的 Flyway 执行副本要和 contracts 源 diff 一致);双包同名 controller 必须显式 bean 名(`controller.admin` 与 `controller.app` 下同简单类名的控制器,裸 `@RestController` 会 bean 名相撞、整个 context 启动失败,这是踩过两次的坑)。物理形态上,13 模块是逻辑划分,**全部以 jar 聚合进 huijing-server 单体进程运行**,Nacos/RocketMQ 是框架自带的远期形态、MVP 未部署。 - -### 图 3 · 包名 → 端前缀自动生效〔Mer新·流·现〕 - -这张图讲一个很容易被当成「魔法」的机制:app 控制器和 admin 控制器的代码里**都不写 `/app-api`、`/admin-api` 前缀**,但请求进来却能各自落到对的端上。它的原理不是魔法,而是 Huijing 框架按**包路径通配**:控制器放在 `controller.app.*` 包下,框架自动给它套 `/app-api` 前缀;放在 `controller.admin.*` 包下,套 `/admin-api`。这条规则的前提,正是图 2 里那条统一包名约定——所有业务代码都在 `com.wanxiang.huijing.game.module.{模块}.{层}` 之下,框架才能据此通配。 - -```mermaid -flowchart LR - DEV["控制器源码
@RequestMapping('/project')
(不写端前缀)"]:::src - subgraph SCAN["Huijing 框架按包路径通配"] - direction TB - APP{"包在
controller.app.*
之下?"} - ADM{"包在
controller.admin.*
之下?"} - end - DEV --> APP - DEV --> ADM - APP -->|是| PAPP["自动套 /app-api
→ /app-api/project"]:::app - ADM -->|是| PADM["自动套 /admin-api
→ /admin-api/project"]:::adm - PAPP --> UT1["URL 前缀反推 userType
/app-api → MEMBER (C 端)"]:::note - PADM --> UT2["URL 前缀反推 userType
/admin-api → ADMIN (B 端)"]:::note - - classDef src fill:#eff6ff,stroke:#2563eb,stroke-width:2px; - classDef app fill:#f5f3ff,stroke:#7c3aed,stroke-width:1.5px; - classDef adm fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px; - classDef note fill:#f1f5f9,stroke:#475569,stroke-width:1.2px; -``` - -这张图最该让人看出的,是它和鉴权的衔接——**端前缀不只是路由,还反推用户类型**。框架据 URL 前缀实时推导 userType:`/app-api` 推出 MEMBER(C 端创作者+玩家),`/admin-api` 推出 ADMIN(B 端运营+管理员)。userType 不在登录时记死、而是按前缀实时算,这正是图 7 鉴权双模型的入口:一个 C 端 token 去打 `/admin-api`,会因为「token 携带的 userType 与 URL 推导出的不一致」在 TokenAuthenticationFilter 那一步就被拒。所以这张看似只讲路由的图,其实是鉴权链路的第一段。它没有现行/远期的灰度,就是当前真相。 - -### 图 4 · 错误码分段:各模块独占一段〔Mer新·讲·现〕 - -这张图回答「全平台的错误码怎么不打架」。规则很简单:错误码格式是 `1-{模块段}-{业务}-{细分}`,**每个模块独占一段、互不重叠**,新增模块在自己 `-api` 的错误码常量类里登记自己那一段。这样任意两个模块各自演进、各自加错误码,都不会撞号。 - -```mermaid -flowchart TB - FMT["错误码格式:1-{模块段}-{业务}-{细分}
各模块独占一段,禁止冲突"]:::fmt - subgraph BASE["Huijing 基座段(源档明确给出)"] - direction LR - B1["1-001 system"] - B2["1-002 infra"] - end - subgraph GAME["13 game 业务模块段"] - direction LR - G1["1-100 project"] - G2["1-101 aigc"] - G3["1-102 runtime"] - G4["1-103 feed"] - G5["1-104 telemetry"] - end - subgraph INFER["顺延分配(源档未逐一给出·按『每模块一段顺延』推断)"] - direction LR - I1["1-105 pay"] - I2["1-106 trade"] - I3["1-107 community"] - I4["1-108 ip"] - I5["1-109 compliance"] - I6["1-110 biz"] - I7["1-111 ad"] - end - FMT --> BASE - FMT --> GAME - FMT --> INFER - - classDef fmt fill:#fefce8,stroke:#ca8a04,stroke-width:2px; -``` - -读这张图要注意一处诚实标注:**源档只明确给出了 system / infra / project / aigc / runtime / feed 六段**(图上前两组),其余模块(pay/trade/community/ip/compliance/biz/ad)的段号是**按「每模块一段顺延」规则推断分配的**(图上第三组单独框出、标「顺延·推断」),不是源档逐一写死的。这处推断照实标,是为了不把推论当成已核实的事实——真要给某模块定错误码段,以该模块 `-api` 的错误码常量类登记的为准。举两个图 7 会再次出现的真实锚点:project 的归属守卫抛 `PROJECT_NOT_OWNER(1_100_000_001)`,落在 project 的 100 段;创作白名单拒绝抛 `PLAYER_CREATE_NOT_IN_WHITELIST(1_002_090_003)`,落在 passport 寄宿的 system/infra 段——这也印证了 passport 身份层物理上寄宿在基座、不在 13 模块清单里这件事。 - -### 图 5 · 全平台数据模型跨域 ER(canonical)〔SVG新·ER·现〕 - -![图 5 全平台数据模型跨域 ER canonical](assets/05-数据模型跨域ER.svg) - -这张图回答「全平台的数据长什么样、怎么跨域连」——也是**全仓唯一的 ER 权威**,其它图说(如总图说、运维图说)要画数据都引用它、不另起一份。它据 Flyway V1–V25 真实落库的约 40 张表画出来,按五条业务链分块:① 创作链路(studio+aigc+project+source)、② 编译发布与合规闸门(runtime+compliance)、③ 分发与数据回路(feed+telemetry)、④ 钱财闭环(ad+trade)、⑤ 身份关联(community+biz),最上面一条是所有创作的归属起点 passport 身份层。 - -这张图最该先记住的是图顶那条**全图铁律**:所有连线都是逻辑「软关联」,**代码里一根物理外键都没有**。这是单体内有意的解耦——模块之间只经各自的 `-api` 包用 DTO 软关联,归属隔离靠 Service 的可信边界(Mapper 强制带 `eq(userId)` 谓词),而不是靠 DB 约束。代价是数据一致性要 Service 自己保,收益是模块能各自演进、后续拆独立仓时不被外键绊住。所有 `game_*` 表里的 `creator_user_id` / `user_id`,数值上都等于 `game_player.id`(OAuth2 的 userId),但代码层一根外键都没有。图上因此用实线画主干强关系、虚线画软关联与回灌,二者一眼区分。 - -图右下角用三个框钉住了三处**最容易误读的真相**,读图时务必一并记住。第一处是**必须知道的数据语义坑**(踩错会出真 bug):两个 quality_score 不能互灌(aigc 的是 0–1 衡量生成质量、telemetry/feed 的是 0–100 衡量运营质量);play_count 有三处口径但只有一处权威(试玩会话不计、telemetry 聚合才是权威源、project 的是回灌的物化字段);金额一律 BIGINT 以「分」计、禁浮点;业务归属列 `creator_user_id` 与审计列 `creator` 是两回事。第二处是**contracts 镜像已漂移**——执行权威是 huijing-server 里的 25 个迁移 V1–V25,但 contracts/db-schemas/ 只含 18 个(单模块 additive 的 V14–V20 不进 contracts),**只读 contracts 会漏掉** aigc 的 level/trace/source/modify 列、source_project 的并发幂等唯一键、feed 的 exposure_limit。这是个真实的漂移面,图上诚实画出、不掩盖。第三处是**两套身份数值与代码路径都分开**:C 端玩家创作者用 `game_player`(物理寄宿 huijing-module-system、不在 13 模块清单),B 端管理员用 `system_users`;而 pay/ip 当前没有独立迁移落地,赋余额走 trade 的 grant、素材登记走 studio 的 `game_material`——这和图 1 的「pay 未接单体、ip 寄宿 compliance」从数据侧对上了。 - -还有一个贯穿全图、最该让评审看出的设计意图是**源与产物两个存储面解耦**:`game_source_project` 存源(source_json)、`game_version` 存产物(package_url 指向的包),「改源不改包」指的是改了源要重新构建才生成新产物、不是直接动包;源构建失败时会留下孤儿源(status=2)而不污染产物面。这正是生成主线「游戏是长生命周期项目、改源重建」范式在数据层的落点。迁移只增不改——新增表或列要回 [数据模型.md](数据模型.md) 补节点,逐列细节以迁移头注为准。 - -### 图 6 · 五条业务链路(跨模块调用)〔SVG新·时·现〕 - -![图 6 五条业务链路跨模块调用](assets/06-五条业务链路.svg) - -如果说图 5 是数据的静态结构,这张图就是它的动态——把「一句话到能赚钱」拆成**五条链**,看每条链怎么跨模块串起调用。五条链分别是:链 1 创作(一句话→可试玩草稿)、链 2 发布(草稿→已发布入流)、链 3 试玩(刷流→取包→沙箱跑→互动)、链 4 广告收益(曝光/激励→计费→分账→入钱包)、链 5 数据回路(遥测→聚合→质量分→feed 重排)。每条链左侧标该步的主调模块,实线是同步主调、绿虚线是异步回灌。 - -读这张图要抓住几条最该看出的设计意图。**链 2 发布是全仓唯一一处真正的跨表原子发布**:project 的统一发布编排把 compliance 裁决、runtime 出包、feed 入流串成一个事务,任一步失败就整体回滚、不留半截状态——这是脊柱级的可靠性能力,图上用事务边界框单独强调。**链 5 数据回路里 telemetry ↔ feed 是 13 模块里唯一一处双向依赖**:feed 消费 telemetry 算出的质量分来排序,telemetry 回收 feed 上的互动信号来计算,这条唯一的双向边正是平台「越用越聪明」飞轮的机制所在,图上用一条绕回的绿虚线画出来。**链 4 广告收益的两段靠 source_ref 对账锚点串起**:ad 的 `revenue.id` 就是 trade 的 `income.source_ref`,trace_id 从 ad 一路透传到 trade 串起对账链,金额全程 BIGINT 以分计——这条边界不容含糊,是钱的事。 - -这张图也照实标出了几处**设计缝与桩**(用 📌 标在对应步骤上),绝不把桩画成真:链 3 的游标分页目前是桩(永远返回第一页)、`packageUrl` 字段恒为 null(真实取包要走 `GET /runtime/package/{vid}`);链 4 的真广告联盟 SDK 是桩、被 mock 挡在审核闸门前;compliance 的检测原子目前也是桩。这些缺口的共同特点是**逻辑已建、卡在不可压缩的日历闸门**(ICP 备案、支付进件、广告审核)或 W-G1 质量门,而不是代码没写——策略是先把逻辑建好、闸门到位即接。最后一个衔接点:这五条链正是运维冒烟门验的五条端到端闭环(见运维图说图 4),冒烟门按链分组点检每条链的关键 API 是否都在岗。 - -### 图 7 · 鉴权 OAuth2 / RBAC 双模型〔SVG新·类·现〕 - -![图 7 鉴权 OAuth2/RBAC 双模型](assets/07-鉴权双模型.svg) - -这张图回答「谁能做什么」。它要先记住图顶那条**铁律**:权限只在后端可信边界强制,前端拦截一律不算数;当前 MVP 是单体,可信边界就是服务侧那一个点(不是网关)。权限基座是 Huijing fork 自带的 Spring Security + OAuth2 + RBAC,全仓没有引入 sa-token。 - -这张图的核心是**两类用户、两套权限模型并存**,这是它最该让人看清的一件事。系统只有两类用户:MEMBER(C 端,创作者+玩家同型)和 ADMIN(B 端,运营+管理员),userType 按 URL 前缀实时推导(接图 3)。一个请求穿过服务侧 TokenAuthenticationFilter(取 token 换登录态、校验 userType 与端前缀一致)和 URL 准入后,按端类型分流到两套截然不同的强制机理: - -- **B 端走声明式 RBAC**(图左):后台所有 admin 控制器统一用 `@PreAuthorize("@ss.hasPermission('模块:资源:动作')")` 声明所需权限位,真正的裁决在 `PermissionServiceImpl` 按「角色→角色拥有的菜单」求交集,超管短路、严格模式(权限点找不到 Menu 即判无权限),落在 RoleDO/MenuDO/RoleMenuDO/UserRoleDO 四张表上。强制点是方法级的注解。 -- **C 端走 Service 层归属隔离**(图右,黄金范式):app 控制器**完全不用 `@PreAuthorize`**——这是此前文档最大的认知缺口。它的范式是控制器取 `getLoginUserId()` 透传给 Service,Service/Mapper 用 `eq(creatorUserId/userId)` 把当前用户钉进 WHERE 谓词,从数据层强制只能碰本人数据。落地靠一个黄金归属守卫 `validateProjectOwner`(不是本人就抛 `PROJECT_NOT_OWNER`「无权操作他人的游戏项目」),后续 C 端写域直接克隆它。强制点是数据层的谓词,不是角色权限。 - -图底还叠了一层**创作白名单**(A2 内测准入):C 端虽创作者与玩家同型,但「能不能创作」由 `validateCreator` 卡住,`creator_flag≠1` 就拒。这道白名单最该记住的是——**前端守卫挡不住直连 API,所以它必须在后端强制**。图右下角的「现状 vs 未来态」是这张图的纠偏点:控制器注释里多处写的「网关+服务端双重校验」是**微服务拆分后才成立的未来态**,当前单体下网关根本不在请求路径上、权限强制 100% 在服务侧单点;图上用实心绿块画现状、虚线框画未来态,把这处与现状不符的旧说法如实标清。mock 后门是 staging 现状、生产必须关死的红线,它和 `validateCreator` 查无行放行是配套的内测豁免。这张图配合图 8 一起看:图 7 讲两套强制机理,图 8 讲哪些端点落哪一档。 - -### 图 8 · 匿名 / 登录态的准入矩阵〔Mer新·讲·现〕 - -这张图把图 7 的两套机理落到「具体端点该走哪一档」的速查上。平台的端点按准入要求分四档:匿名公开读、登录态、B 端 RBAC 权限位、C 端归属隔离。匿名读端点用 `@PermitAll` 由框架扫描注解自动转成免登 URL 列表,其余端点落到 `anyRequest().authenticated()` 的兜底(未登录即 401)。 - -```mermaid -flowchart TB - REQ["请求到达服务侧"] --> Q1{"端点带
@PermitAll?"} - Q1 -->|是| ANON["① 匿名公开读
任何人无需 token
(app 端共 34 处匿名读)"]:::anon - Q1 -->|否| Q2{"已登录?
(userType 须匹配端前缀)"} - Q2 -->|否| E401["401 未登录"]:::deny - Q2 -->|是| Q3{"端类型?"} - Q3 -->|"/admin-api · B 端"| RBAC["③ RBAC 权限位
@PreAuthorize + PermissionServiceImpl
持对应角色-菜单权限的 ADMIN"]:::rbac - Q3 -->|"/app-api · C 端"| OWN["④ 归属隔离
Service eq(userId) + validateProjectOwner
MEMBER 且是数据归属人"]:::own - RBAC -->|无权| E403["403 无权限"]:::deny - OWN -->|非本人| E403 - - ANON -.->|"匿名读三纪律"| RULE["只暴露公开字段·裁剪 PII
公开读靠手动 eq 谓词隔离(不假定 DataPermission 自动生效)
跨租户读显式收敛"]:::rule - - classDef anon fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px; - classDef rbac fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px; - classDef own fill:#ede9fe,stroke:#7c3aed,stroke-width:1.5px; - classDef deny fill:#fee2e2,stroke:#dc2626,stroke-width:1.5px; - classDef rule fill:#fff7ed,stroke:#d97706,stroke-width:1.3px,stroke-dasharray:5 4; -``` - -这张图最该让人看出的,是匿名读这一档**不是「无所谓」、而是有三条必须守的纪律**(图上用橙色虚线框单独标出,出自安全规则、P-OPN-08 已实证):只暴露公开字段、把 PII 裁剪掉;公开读靠手动 `eq` 谓词隔离,**不能假定框架的 DataPermission 会自动生效**(它只注册在 AdminUserDO 等业务表上);跨租户读要显式收敛。这三条是匿名读端点最容易踩的坑——以为放了 `@PermitAll` 就完事,结果把私有数据或 PII 漏出去。一个要记住的细节是**登录端点本身免登**:C 端的 send-sms-code / sms-login / invite-register 三个带 `@PermitAll`(不然没法登录),但 `me` 走登录态;sms-login 是「已注册即登录、未注册自动注册」一体,成功后发真 OAuth2 token。这张图没有现行/远期边界,就是当前每个端点准入的真相速查表。 - ---- - -## 3. 图清单与状态表 - -| # | 图名 | 家族 | 形式 | 状态 | 覆盖内容 | -|---|---|---|---|---|---| -| 1 | 后端分层与依赖(实现视角) | 架 | 引(链 README §1) | 现 | 基座 + 13 模块 + 两段式三层;pay 未接单体 / ip 寄宿 compliance 两处特殊归属;WHAT 归架构域、本域只讲 HOW | -| 2 | 两段式模块 -api/-server | 类 | SVG新 | 现 | 单模块剖面(-api 枚举/错误码/DTO/Feign · -server controller/service/dal/convert)+ 跨模块只依赖 -api 的越界红线 + DTO/VO/DO 三层 + 单体形态 | -| 3 | 包名 → 端前缀自动生效 | 流 | Mer新 | 现 | controller.app/admin 包路径如何自动套 /app-api、/admin-api + 前缀反推 userType 接鉴权 | -| 4 | 错误码分段 | 讲 | Mer新 | 现 | `1-{模块段}-***` 各模块独占段;六段源档给出 + 七段顺延推断(诚实标推断)| -| 5 | 全平台数据模型跨域 ER(canonical) | ER | SVG新 | 现 | 五链路约 40 表跨域 ER + 全图无物理外键铁律 + 三处真相框(数据语义坑 / contracts 镜像漂移 / 两套身份)+ 源↔产物解耦 | -| 6 | 五条业务链路 | 时 | SVG新 | 现 | 创作/发布/试玩/广告收益/数据回路 跨模块调用;唯一真原子发布 + 唯一双向边 + source_ref 对账锚点;游标/联盟SDK/检测原子桩照实标 | -| 7 | 鉴权 OAuth2/RBAC 双模型 | 类 | SVG新 | 现 | 两类用户两套权限并存:B 端 @PreAuthorize RBAC + C 端 Service 归属隔离 + 创作白名单 + 网关双重校验=未来态纠偏 + mock 后门红线 | -| 8 | 匿名/登录准入矩阵 | 讲 | Mer新 | 现 | 四档准入(匿名/登录/RBAC/归属)速查 + 匿名读三纪律 + 登录端点本身免登 | - -> **状态分布**:现 ×8(后端域全部现行已建,无接/缓/建/future)。但「现行已建的结构」里照实标出了若干**设计缝与桩**——contracts DB 镜像漂移(图 5)、compliance 检测原子恒 pass / feed 游标分页桩 / 真联盟 SDK 桩(图 6)、网关双重校验是未来态而非现状(图 7)——绝不因整体为「现」就把桩画成真。**防漂移门**:本图说 frontmatter 记四份源档的 commit hash(README/数据模型/鉴权与权限 @ 38357c3d、13模块 @ 7ecd616e),源档变更即比对,hash 对不上则本图说与对应 SVG 标「待复核」。 -> -> **PNG 后续**:8 张图中 4 张为 SVG(图 2/5/6/7),SVG 入仓后在 mini-desktop 批量转 PNG(6c6g 禁 chrome);3 张 Mermaid(图 3/4/8)GitHub 直接渲染、无需转换;图 1 单源引用 README、不产新图。 diff --git a/docs/architecture/架构/产物执行沙箱图说.md b/docs/architecture/架构/产物执行沙箱图说.md deleted file mode 100644 index b4f6dc67..00000000 --- a/docs/architecture/架构/产物执行沙箱图说.md +++ /dev/null @@ -1,198 +0,0 @@ ---- -date: 2026-06-22 -topic: 产物执行沙箱图说——把「一款 AI 生成的不可信游戏,怎么在玩家浏览器里被隔离着安全跑起来」画成一套图 + 讲解(架构图集·架构域) -status: 现行已建(机制全建成·真跑通过)· 同源过渡态债见 §8 诚实标注 · 防漂移门记源档 commit hash -映射源档: - - docs/architecture/架构/产物执行沙箱.md @ 38357c3d ---- - -# 产物执行沙箱图说 - -> **这是什么**:绘境AI「产物执行沙箱」的「看图入口」。生成主线已经从「禁代码、填参数」迁到「让 agent 写码」,于是注入面就是模型现写的 JavaScript 本体——平台必须把每一款生成游戏当成有恶意、有 bug 的不可信输入。这份图说把「这段不可信代码怎么在玩家浏览器里被隔离着安全跑」画清楚:取包、sha256 完整性校验、srcdoc 注入、iframe + CSP 隔离、postMessage 双向桥双校验、SDK 受控能力面、加载/失败/销毁的生命周期,一条端到端链路。复杂、跨层的用手绘 SVG 大图,简单、线性的用 Mermaid。 -> -> **给谁看**:要改宿主 / 桥 / CSP / 校验逻辑的前端工程师,做安全审计的人,以及想搞清「生成出来的游戏能调什么、不能碰什么」的产品与架构。判断「这套隔离方案是不是想要的那个」看这份图就够建立全局轮廓,逐行的 file:line 仍去源档 [`产物执行沙箱.md`](产物执行沙箱.md) 与对应前端源码。 - ---- - -## 0. 阅读约定与同步纪律 - -- **映射的设计档**:本图说不另立设计,只把架构域里 [`产物执行沙箱.md`](产物执行沙箱.md) 这一份 canonical 设计档画出来。frontmatter 记了它的当前 commit hash(`38357c3d`),作为**防漂移门**:源档一旦变更、hash 对不上,这份图说与对应 SVG 即标「待复核」,由收口脚本比对。 -- **同步纪律**:**设计一变动,本图说与对应 SVG 必须同步更新**,每张 SVG 脚注注明映射源档与状态。已并入 wave 收口清单。 -- **本域状态分布**:这套机制(iframe + 桥 + 双校验 + sha256)**已全部建成、真跑通过**,所以七张图的整图状态都是**现**。但「机制建成不等于合规收口」——源档 §8 逐条带 file:line 记了四处真实债(CSP 文档口径与代码对不上、sandbox 属性三处不一致、后端下发的 sandboxAttr 前端没接线、HJ-AUDIT-001 同源红线当前是过渡态)。这些**现状债**不是「待建」,而是「现行机制里的已知缺口」,图上一律用**橙色虚线框 + 文字小标**与「现行已建的实心块」一眼区分开(图 1 右下、图 2 注记里能看到),绝不能因为机制都建了就误判它已经安全收口。 -- **产物形式**:SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 转(6c6g 禁 chrome、无转换器)。本图说只出 SVG;Mermaid 图 GitHub 直接渲染、无需转换。 - -## 1. 全图通用图例 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上,沙箱域主要关心其中两个): - -- **安全**(`#dc2626`):这是本域的主轴。三层封堵(build 段静态门 / iframe CSP / sandbox 属性)、桥的 origin + schema 双校验、storage 四道闸、sha256 取包完整性、bundle 内联前的 `` 消毒——全是为了把不可信代码关在笼子里。 -- **质量**(`#15803d`):受控面那条「异常绝不向游戏抛、超时就降级、写类 fire-and-forget」的降级铁律,根源是玩家验收门五条 AND 里的「无错」——游戏运行期一旦抛未捕获错误就判不过,所以受控面任何意外都得自己吞掉。隔离做得再硬,也不能把能正常试玩的游戏因为一次可恢复失败而误报「加载失败」。 - -(其余维度 **可观测** / **可靠** / **成本** / **数据** / **伸缩** 在本域不是主轴,故图上基本不点。) - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现成直接用、同步动作、调用或发消息。 -- **虚线**:承担两层语义——一是**状态语义**,凡是「现状债 / 过渡态」的元素一律用橙色虚线框 + 文字小标画出(与现行已建的实心块区分);二是**边界语义**,图 5 里两条信任边界都用虚线框表达「壳」(红虚线=沙箱壳、紫虚线=受控面壳)。 -- **双线 / 加粗框**:跨域共享的公共件或需要强调的边界。**绿色实线箭头**在本域特指「唯一放行通道」(postMessage),**红色虚线**特指「被封死的逃逸面」。 - -> 本域图说最该守的纪律点是:**别把「机制已建」当成「已经安全」**。iframe、桥、双校验、sha256 这套都真跑通过了,但它现在跑在**同源过渡态**(同源 srcdoc + `allow-same-origin`),HJ-AUDIT-001 那条「独立源就绪前禁用 `allow-same-origin`」的红线在代码里并未强制。读图时凡见橙色虚线框,就是「这里还有债、别当已合规」。 - ---- - -## 2. 图集 - -### 图 1 · 不可信代码隔离全景〔SVG新·架·现〕 - -![图 1 不可信代码隔离全景](assets/sbx-01-不可信代码隔离全景.svg) - -这张全景图回答整份文档的总问题:**一段模型现写的不可信 JavaScript,凭什么敢放进玩家的浏览器里跑**。答案是三层封堵,它们在不同时机、不同位置生效,合起来才是完整的封锁,单看任何一层都不够。图从左到右按「出厂前 → iframe 这层壳 → iframe 外的宿主平台」铺开,用绿色标出唯一被放行的通道、用红色虚线标出每一个被封死的逃逸面。 - -最左边一层最容易被忽略,因为它**不在 runtime、而在 build 段**。`build-from-source.mjs` 的 `scanLogic` 在把 gameDefinition 装配成可玩工厂、交给 esbuild 打包**之前**,对模型产出的 `behavior.code` 和 `rule.condition` 做静态扫描,命中危险模式就转成 `validationError` 回灌给 repair,而不是指望 prompt 自觉。为什么这一层必须放在 build 边界做?因为同步 JS 在进程内没法被硬中断——一个 `while(true)` 死循环或一次沙箱逃逸只要执行了就晚了,所以唯一的机会是在执行前的校验边界把危险模式拦掉。图上把 `LOGIC_BANS`(逃逸 / DOM 网络 / 死循环 / 确定性四类)和 `CONDITION_BANS`(条件必须是无副作用的纯布尔表达式)的实际拦截清单都列了出来,让人看清这层拦的不是抽象的「危险代码」,而是一份具体的黑名单。 - -中间是 iframe 这层壳,它叠了两道:**CSP** 硬编码进 srcdoc 的 ``,其中 `connect-src 'none'` 是最关键的一条——它禁掉一切网络出站,所以即便游戏代码想把玩家数据 `fetch` 出去也发不出去;引擎 bundle 不在 iframe 内 fetch,而是由宿主层 fetch 后内联进来,所以这条不会卡死引擎装载。**sandbox 属性**则封死宿主 DOM 逃逸、顶层导航与弹窗,并把游戏与宿主之间的通道收窄到只剩 postMessage 一条。这条收窄直接决定了产品形态:广告、支付、存储这些要碰平台资源的事,游戏在 iframe 内根本办不了(没网络、碰不到宿主 DOM),只能经 postMessage 抛给宿主侧去办、办完再回包——这就是为什么 SDK 的 ad/pay/storage 都是「游戏发请求 → 宿主执行 → 回包」的形状,而不是游戏直接调。 - -这张图必须让人看出的「设计缝」画在右下角那个橙色虚线框里:**机制建成 ≠ 合规收口**。当前整套跑在同源过渡态(同源 srcdoc 配 `allow-same-origin`),HJ-AUDIT-001 的同源红线在代码里没强制;CSP 的文档口径写的是 `script-src 'self'`、代码实为 `'unsafe-inline' 'unsafe-eval'`;sandbox 属性在 DB 默认值、前端硬编码、文档示例三处不一致;后端下发的 `sandboxAttr` 前端压根没消费。这四条债的收敛要一起做(迁独立 usercontent 子域、CSP 改 HTTP 响应头下发、sandboxAttr 真接线)才闭合,详见图 2 注记与源档 §8。把这块诚实画出来,正是为了不让人看到「三层都建了」就以为已经安全了。 - -### 图 2 · 取包 → sha256 校验 → iframe + CSP → 注入 时序〔SVG新·时·现〕 - -![图 2 取包校验注入时序](assets/sbx-02-取包校验注入时序.svg) - -这张时序图把一款不可信游戏「从后端取包到在玩家面前可玩」的全过程展开成六条泳道之间的一串动作。它的叙事主线是:**每一环都假设上一环的产物可能有问题**,所以取包之后必有完整性校验、注入之时必带 CSP、建桥之后所有消息必过双校验。 - -取包这一步并不是一条直线,而是 `resolvePackage` 分四态走,图上方用橙色虚线框列了出来:创作者预览时 `props.manifest` 直接传一个内存里的包进来、不走网络;玩家试玩时按 `versionId` 取运行包清单、清单里带 `manifestUrl` 就去拉真包做 sha256 校验;清单没有 `manifestUrl`(mock 阶段后端还没回填)就退 demo 兜底;取包或校验中途出任何错,也退 demo 兜底。这里有一个关键且有意的设计:可恢复的失败**只 `console.warn`、不向上 emit error**。原因是父级 `Play` 把 error 当致命态处理、会用 `v-else-if` 把宿主整个隐藏掉显示「加载失败」,而 demo 兜底本可以正常试玩,两者冲突,所以宁可降级也不上抛。 - -中段是这张图的安全支点——**sha256 双通道校验**(图中绿色块)。manifest 从后端拉下来后要确认传输途中没被篡改、和后端写包时算的 checksum 一致。校验走双通道:安全上下文(https / localhost)优先用原生 `SubtleCrypto`,性能最好;非安全上下文回退一份纯 JS 的 SHA-256 完整实现。为什么需要这份纯 JS 回退?因为创始人用局域网 IP 明文 http(例如 `http://100.64.0.7:4173`)跨内网机访问时,浏览器判定为「非安全上下文」、`crypto.subtle` 直接是 `undefined`,旧实现这时会抛错、导致校验失败退 demo 兜底,最终页面误报「游戏加载失败 / 网络波动」——真因是上下文不安全,根本不是网络。回退纯 JS 后,完整性校验在明文 IP http 和安全上下文下行为一致。后端这一端也对齐了字节一致性:取 manifest 的端点返回的是原始 JSON 文本(返回类型 `String`,`GlobalResponseBodyHandler` 只拦 `CommonResult` 不拦它),保证前后端对同一份字节算摘要。 - -后段是注入与回流。`buildIframeSrcdoc` 用一个不常见的手法叫「函数序列化注入」:`createWanxiangSDK` 和 `startRuntime` 都是不依赖模块外部符号的纯工厂函数,用 `Function.prototype.toString()` 取源码文本拼进 iframe 内联 `` 变体防止它越出脚本块。最后一条回流值得记住:**游戏没有自己的 emit 通道发 game_end**,所以宿主 `watchGameEnd` 每 500ms 轮询 `host.state().phase`、到 `gameover` 就 latch 一个 `game_end` 代发——这是过渡口径,ref 游戏是纯引擎工厂、内部没 SDK,自发不出终态,等 W-G1 生成主线落地后改由各品类标准信号产出、这个轮询桩可下线。图右侧旁注还点出加载超时按装载路分流(引擎包 12s、旧 demo 路 5s),因为引擎冷启动是旧路的 4.8~5.3 倍、内联 bundle 解析会逼近甚至超过 5s,放宽到 12s 是为了防误杀,而首屏 P75<3s 由 `perf_first_screen` 单独监测、与这个防挂死上界正交。 - -### 图 3 · postMessage 双向桥与双校验〔Mer新·时·现〕 - -下面这张流程图把桥(`bridge.ts` 的 `HostBridge` 类)处理一条 iframe 来消息的判定过程画清楚。桥的设计立场是:**把 iframe 来的每一条消息都当不可信边界处理**——先过 origin 闸、再过 schema 闸,任一不过就丢弃、绝不分发;过了两道闸还要分辨它是不是宿主先前请求的回包,是回包就消费掉、不是才往上分发。 - -```mermaid -flowchart LR - M["iframe postMessage 来一条消息"] --> O{"① origin 在白名单?
isOriginAllowed"} - O -->|"否(空白名单默认拒绝)"| R1["onReject('origin_not_allowed')
丢弃"]:::deny - O -->|是| S{"② schema 逐字段校验?
validateEnvelope"} - S -->|否| R2["onReject(reason)
丢弃"]:::deny - S -->|是| P{"是宿主请求的回包?
requestId 配对命中 pending"} - P -->|是| RP["消费 pending 回调
clearTimeout + resolve"]:::ok - P -->|否| D["onMessage 分发给上层 GamePlayer"]:::ok - - classDef deny fill:#fee2e2,stroke:#dc2626,stroke-width:2px; - classDef ok fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px; -``` - -第一道是 **origin 白名单**:`isOriginAllowed` 对空白名单**默认拒绝**——白名单为空视为不允许任何来源,这是安全默认而非疏忽。因为 srcdoc 文档的 origin 是 `null`,建桥时 `buildOriginAllowlist` 在 `includeNull` 时会把字符串 `'null'` 连同宿主自身 origin 一起加进白名单(这也正是 §8 同源债的一个落点:过渡态下白名单含 `'null'`)。第二道是 **schema 逐字段校验** `validateEnvelope`:`channel` 必须严格等于固定标识 `wanxiang-game-sdk`(否则是噪声或伪造),`type` 必须在 8 个枚举的 `VALID_TYPES` 内(即图 4 那张 type 表的全集),`direction` 必须合法,`traceId` 必须是字符串(贯穿链路必备),`payload` 必须存在,`requestId` 若带则必须是字符串。这里有个有意的细节:`validateEnvelope` **不抛异常、只返回判定结果**——源码注释写得明白,「异常本身也是一种攻击面」,所以校验器自己绝不制造可抛路径。 - -过了两道闸之后的回包识别,是为了支撑「请求-响应」语义:宿主往游戏发消息用 `post`(`targetOrigin` 用 `'*'`,因为 srcdoc 的 origin 是 `null` 没法精确指定),而 `request` 是 Promise 化的请求-响应、带 5s 超时(`REQUEST_TIMEOUT_MS`),**超时就以 `undefined` 兜底 resolve、绝不 reject**——向上抛 reject 会打断游戏,所以宁可降级。卸载时 `dispose` 移监听、清所有挂起请求的定时器防泄漏。这张图没有现行/远期边界,它就是现行桥的真实判定流;要注意的「设计缝」只有一处,且不在桥本身而在它的输入侧——白名单含 `'null'` 是同源过渡态的产物,独立源落地后这一项会随 §8 收敛一起收紧。 - -### 图 4 · 受控面(SDK bridge)能力面〔SVG新·类·现〕 - -![图 4 受控面能力面](assets/sbx-04-受控面能力面.svg) - -这张类图回答「平台到底向不可信游戏注入了什么能力、又是怎么注入的」。结论是:iframe 内的游戏只能经一个全局对象 `window.WanxiangGameSDK`(加上引擎装载契约)与外界打交道,受控面之外一律被 CSP 和 sandbox 封死——没有这个 SDK,平台就只是个静态文件托管。图把受控面拆成 Core 层和 Plugin 层,并把另一条并列的入口(引擎装载契约)单列在右栏。 - -**Core 层内联进游戏入口、压缩后小于 8KB、首屏即在**,暴露 `init` / `on` / `track` / `reportError` / `ad` / `pay`。`init` 注入 `traceId` 贯穿「生成 → 运行 → 上报」全链路;`track` 是 10 条 / 5s 的批量缓冲、写类 fire-and-forget;`ad` / `pay` 经桥的 `request` 走 5s 超时降级。**Plugin 层按需懒加载、首屏不付代价**,其中 storage 这个面尤其要小心,因为它直接写宿主的真 localStorage——图上把它的四道闸列全了:闸①只放行恰好等于 `idle:gameId:versionId` 的 key(tycoon 这类无离线态的模板根本不发 storage、白名单也不含它的前缀),闸②挡 4KB 以上的 value(idle 存档实际不到 200B),闸③在写入前用 `JSON.parse` 校验、回读时只接受对象形态(防有人手工篡改 localStorage 注入脏数据),闸④落盘时加 `wxgame:` 前缀做命名空间隔离。这里有个设计点值得点出:回包给游戏的是宿主侧**已经解析好的对象**、不是原始字符串,解析在宿主这道受信边界做、runtime 侧零 `JSON.parse`,守住「游戏代码不引可抛路径」的红线。 - -整张图最该让人记住的是那条贯穿所有受控面的**降级铁律**:异常绝不向游戏抛、超时就降级、写类操作 fire-and-forget。它的根源不是工程洁癖,而是玩家验收门那五条 AND 里的「无错」——游戏运行期一旦抛出未捕获错误就判不过,所以受控面任何意外都得自己吞掉。右栏的引擎装载契约(`window.__GameBundle.bootGameHost` / `render(mainContext)` / `ctx.getEngine()`)是**与 SDK 并列的第二条受控入口**,但它属于另一条正交边界(详见图 5),图上特意提醒:软著 `ruanzhu-5` 和专利 `01-受控插件引擎` 写的是这条受控插件面、不是 iframe 产物沙箱,引用时别张冠李戴。图下方的 8 种 postMessage type 表是桥 schema 闸的枚举全集,把每个 type 的方向和用途列清,让人对照图 3 看清「双校验放行的到底是哪 8 类消息」。 - -### 图 5 · 两条正交边界(别混)〔SVG新·架·现〕 - -![图 5 两条正交边界](assets/sbx-05-两条正交边界.svg) - -这张图专治一个最容易混的概念:系统里有**两条信任边界**,它们方向正交、位置嵌套,必须分清。边界① 是**沙箱边界**——「整个 iframe 这层壳」与「外面的宿主平台」之间的隔离,它活在 iframe 壳本身,是本档(产物执行沙箱.md)的主轴;边界② 是**受控面**——iframe 内部、「游戏代码」与「引擎能力」之间的约束(`api.d.ts` 的 `PluginContext`),它管「游戏代码调引擎时只能走公开 API」,是 [`引擎与运行时.md`](生成引擎/引擎与运行时.md) 的主轴。一句话记牢:**受控面在沙箱里面,沙箱在受控面外面。** - -图用嵌套的方框把这个关系画成可视:红色虚线大框是沙箱壳、紫色虚线框嵌在里面是受控面、最内层才是不可信游戏代码——它被两层边界一起关着。右上角的「同心嵌套示意」是这张关系的速记版。之所以强调它们「正交」,是因为两条边界各管一段、不互为替代、缺一不可,而且任何一条被破时另一条仍在兜:如果沙箱边界破了(比如同源过渡态 `allow-same-origin` 下游戏触到了宿主 DOM),受控面拦不住它——这正是 §8 同源债的要害;反过来如果受控面破了(游戏绕开公开 API 去抓引擎内部),外层沙箱壳仍兜得住,不致让数据外泄或出网。把这层「破了一条还有另一条」的纵深关系画出来,就能解释为什么两条边界都不能省。 - -这张图右下角把「最容易混的三处」钉死,都是实际踩过或容易踩的坑:一是引用知识产权时别张冠李戴(软著/专利写的是受控插件面=边界②,不是 iframe 产物沙箱=边界①);二是关联档分工别串(`引擎与运行时.md` 讲装载 + 引擎能力=边界②,本档讲执行隔离 + 安全边界=边界①,两者正交不重叠);三是收敛各管各的(沙箱债 §8 的同源/CSP/sandboxAttr 属边界①,受控面的血统债 random/time/input 属边界②,别合并谈)。这张图没有现行/远期边界,它讲的是一个**当前就成立的概念结构**;它和图 1 的关系是图 1 给「三层封堵的全景」、图 5 给「两条边界的嵌套关系」,一个讲手段、一个讲概念分层。 - -### 图 6 · CSP / 同源红线 / 产物消毒〔Mer新·讲·现〕 - -下面这张讲解图把沙箱里「与外界资源打交道」最硬的三条红线集中起来:CSP 的 `connect-src 'none'`、HJ-AUDIT-001 的 `allow-same-origin` 同源红线、以及 bundle 内联前的产物消毒。它们分别管「不准出网」「同源下不准用 same-origin」「注入的脚本不准越界」,是沙箱安全性的三块基石,也是 §8 现状债集中的地方。 - -```mermaid -flowchart TB - subgraph CSP["① CSP(硬编码进 srcdoc <meta>)"] - direction TB - C1["default-src 'none' —— 兜底封掉一切未显式放开的资源类型"] - C2["connect-src 'none' —— 禁一切网络出站(最关键)
引擎 bundle 由宿主层 fetch 后内联,不在 iframe 内 fetch,故不被卡"] - C3["script-src 'unsafe-inline' 'unsafe-eval'
gamedef 运行时用 new Function 编译逻辑串,必需 'unsafe-eval'"]:::debt - end - subgraph SO["② 同源红线 HJ-AUDIT-001(当前=过渡态)"] - direction TB - O1["红线:allow-same-origin 只有当游戏包托管在独立源
(usercontent 子域·与宿主不同源)时才可用"] - O2["现状债:现行同源 srcdoc + allow-same-origin,红线代码里未强制
origin 白名单还含字符串 'null'"]:::debt - end - subgraph SAN["③ 产物消毒"] - direction TB - N1["escapeBundleForInlineScript:断 </script>(含大小写/空白变体)和 <!--
让 bundle 文本无法越出 <script> 边界"] - N2["纵深防御:bundle 来自后端 engineBundle、整包 sha256 已覆盖完整性
消毒是额外一层、不是唯一依赖"] - end - DEF["纵深承接:'unsafe-eval' 的风险由三重边界一起兜——
逻辑串先经 build 段 scanLogic 静态拦危险面 + connect-src 'none' 无出网 + iframe sandbox 隔离"]:::ok - C3 --> DEF - O2 --> FIX["收敛方向(三件一起做才闭合):
① 迁独立 usercontent 子域(让 allow-same-origin 真安全)
② CSP 从 srcdoc meta 改 HTTP 响应头下发
③ sandboxAttr 这条 DB→VO→前端链真接线"]:::fix - - classDef debt fill:#fff7ed,stroke:#d97706,stroke-width:1.5px,stroke-dasharray:5 4; - classDef ok fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px; - classDef fix fill:#eff6ff,stroke:#2563eb,stroke-width:1.5px; -``` - -读这张图要抓住一个反复出现的张力:**`'unsafe-eval'` 看着危险,但它是 gamedef 运行时必需的、且风险被纵深防御承接住了**。声明式 gameDefinition 的 behaviors / rules 各带逻辑 JS 串,`gd-runtime.js` 用 `new Function` 编译执行,缺 `'unsafe-eval'` 会抛 `EvalError` 被静默吞进 errors[]、tickBehaviors 空转、游戏只渲首帧静态、世界永不演进(这是 2026-06-21 实证过的 bug:加上之后 errCnt 从 7 降到 0、score 从 0 开始演进)。它的风险承接是一条纵深链:逻辑串先在 build 边界经 `scanLogic` 静态拦掉危险面,再加 `connect-src 'none'` 断网络出站,再加 iframe sandbox 隔离——三重边界使它不扩大真实攻击面。所以图上把 `'unsafe-eval'` 标成债色、但用一条绿线指向「纵深承接」,表达「它是有意的、不是漏洞」。这里也要诚实记一笔文档漂移:`.agents/rules/security-and-reliability.md` §1.1、skills 手册、契约注释都写 `script-src 'self'`,但代码实为 `'unsafe-inline' 'unsafe-eval'`、且硬编码在 srcdoc meta 里,**以代码为准、文档口径已过期**。 - -第二块同源红线是本域最该警惕的过渡态。HJ-AUDIT-001 说 `allow-same-origin` 只有当游戏包托管在独立源时才能用,因为同源下 iframe 能触宿主 DOM / 存储、甚至自己移除 sandbox,沙箱铁律就失效了;但代码现状是同源 srcdoc 配 `allow-same-origin`、origin 白名单还含 `'null'`,红线在代码里没强制。这是已知的、独立源落地前的过渡态,不是已合规。图把它和收敛方向连在一起,强调三件事(迁独立 usercontent 子域、CSP 改 HTTP 响应头下发、sandboxAttr 真接线)必须一起做才闭合——只做一件不解决问题。第三块产物消毒相对干净:`escapeBundleForInlineScript` 断 `` 和 ` Preload : 进入可视区前预取 - Preload : 后一个 · 预加载 - Preload : 取包 + sha256 校验 + buildIframeSrcdoc
iframe 挂载但未掌帧 - Current : 当前 · 播放中 - Current : boot 掌帧 · game_loaded→phase=ready
game_start · watchGameEnd 轮询终态 - Disposing : 前一个 · 销毁中 - Disposing : dispose 移监听 + 清挂起请求定时器
iframe 卸载 · 防泄漏 - - Preload --> Current : 滑到它(成为当前) - Current --> Disposing : 滑走(被下一个取代) - Disposing --> [*] : 释放完成 - - Current --> Fallback : 加载失败 / sha256 不过 / 超时 - Fallback : 降级 demo 兜底
只 console.warn · 不向上 emit error - Fallback --> Disposing : 滑走时照常销毁 - - note right of Current - 加载超时按装载路分流: - 引擎包 12s · 旧 demo 路 5s - (引擎冷启动 4.8~5.3×) - end note -``` - -读这张图要抓住两个设计点。其一,**三容器轮转是为了让「即点即玩」成立**:如果每次滑到一款游戏才开始取包、校验、注入、冷启动引擎,玩家会看到明显的等待;提前在「进入可视区之前」就把后一个容器预加载好(取包 + sha256 + 挂载 iframe、但还不掌帧),滑到时才 `boot` 掌帧,等待就被藏进了滑动的过程里。与此对称,前一个滑走的容器进入销毁态,`dispose` 必须移监听、清掉所有挂起请求的定时器——这一步不能省,否则切几十款游戏后会积累一堆未清理的监听和定时器、内存泄漏。其二,**加载失败是一条降级支路、不是终止**:sha256 校验不过、取包出错、或加载超时,都不让宿主整个隐藏显示「加载失败」,而是降级到 demo 兜底、只 `console.warn`、不向上 emit error(原因同图 2——父级 `Play` 把 error 当致命态会隐藏宿主,而 demo 本可正常试玩)。这条降级支路汇回正常的销毁流:滑走时它照常被 `dispose`。 - -这张图没有现行/远期边界,它是现行 feed 容器生命周期的真实形态;唯一的过渡口径在「终态怎么来」——`watchGameEnd` 轮询 `host.state().phase` 到 `gameover` 才 latch 一个 `game_end` 代发,是因为 ref 游戏自己没有 emit 通道,等 W-G1 落地后改由各品类标准信号产出、轮询桩可下线(与图 2 同源)。 - ---- - -## 3. 图清单与状态表 - -| # | 图名 | 家族 | 形式 | 状态 | 覆盖内容 | -|---|---|---|---|---|---| -| 1 | 不可信代码隔离全景 | 架 | SVG新 | 现 | 三层封堵(build 段 scanLogic / iframe CSP / sandbox 属性)+ 唯一放行通道 postMessage + 封死逃逸面 + 同源过渡态四债(§8)诚实标注 | -| 2 | 取包 → sha256 校验 → iframe + CSP → 注入 时序 | 时 | SVG新 | 现 | resolvePackage 四态 + sha256 双通道(Web Crypto / 纯 JS 回退)+ 字节一致性 + 函数序列化注入 + __GameBundle + 加载超时分流 + game_end latch 代发 | -| 3 | postMessage 双向桥与双校验 | 时 | Mer新(内联) | 现 | origin 闸(空白名单默认拒绝·含 'null')+ schema 逐字段闸 + 回包 requestId 配对 + request 5s 超时降级不 reject + 校验不抛异常 | -| 4 | 受控面(SDK bridge)能力面 | 类 | SVG新 | 现 | Core 层 <8KB(init/on/track/reportError/ad/pay)+ Plugin 层懒加载(storage 四闸)+ 引擎装载契约(第二条入口)+ 降级铁律 + 8 type 表 | -| 5 | 两条正交边界 | 架 | SVG新 | 现 | 沙箱边界(iframe 壳 vs 宿主,边界①)vs 受控面(游戏↔引擎,边界②)嵌套关系 + 破一条另一条仍兜 + IP/关联档/收敛三处别混 | -| 6 | CSP / 同源红线 / 产物消毒 | 讲 | Mer新(内联) | 现 | connect-src 'none' + 'unsafe-eval' 纵深承接(漂移①以代码为准)+ allow-same-origin 同源红线过渡态(HJ-AUDIT-001)+ escapeBundle 消毒 + 收敛三件一起做 | -| 7 | 三容器加载 / 失败 / 销毁态机 | 状 | Mer新(内联) | 现 | 当前播放 / 前一销毁 / 后一预加载 三态轮转 + dispose 防泄漏 + 加载失败降级 demo(不上抛)+ 加载超时分流 + game_end latch 过渡口径 | - -> **状态分布**:现 ×7(机制全建成、真跑通过)。本域无「待接 / 待建 / 缓做 / future」整图——但**现行机制内有四处已知债**(CSP 文档口径漂移、sandbox 属性三处不一致、sandboxAttr 半孤儿、HJ-AUDIT-001 同源红线过渡态),在图 1 / 图 6 中用橙色虚线框 + 文字小标与「现行已建实心块」区分,绝不画成「已合规收口」。**防漂移门**:本图说 frontmatter 记源档 `产物执行沙箱.md @ 38357c3d`,源档变更即比对,hash 对不上则本图说与对应 SVG 标「待复核」。 -> -> **PNG 后续**:7 张图中 4 张为 SVG(图 1/2/4/5),SVG 入仓后在 mini-desktop 批量转 PNG(6c6g 禁 chrome);3 张 Mermaid(图 3/6/7)GitHub 直接渲染、无需转换。 -> -> **单源说明**:本域所有图均为新作(SVG新 / Mer新),无「引」类。源设计档 `产物执行沙箱.md` 自身在 §1/§2/§3 内嵌了三段 Mermaid(端到端时序、三重边界、桥双校验),本图说**不复制它们**——图 2 是把 §1 时序升保真为 SVG(更细、含四态与双通道)、图 3 的桥流程按「桥判定流」单独框选重画(横向、突出双闸 + 回包配对,与源档 §3 的纵向块图视角不同)、图 1/4/5 则是源档没有的新视角(全景 / 类图 / 边界嵌套)。源档那三段 Mermaid 仍是它自己叙述的一部分、各居其位,本图说在散文里指向源档对应章节,不在两处各存一份同图。 diff --git a/docs/architecture/架构/架构图说.md b/docs/architecture/架构/架构图说.md deleted file mode 100644 index d5df598a..00000000 --- a/docs/architecture/架构/架构图说.md +++ /dev/null @@ -1,195 +0,0 @@ ---- -date: 2026-06-22 -topic: 架构域图说——把「这套系统用什么技术、怎么搭起来、关键选型为什么这么选、13 个模块怎么分工与依赖、一条生成请求怎么跑通、契约族长什么样」画成一套图 + 讲解(架构图集·架构域) -status: 现行为主(含决策史对照与契约现实缝)· 防漂移门记源档 commit hash -映射源档: - - docs/architecture/架构/README.md @ 38357c3d - - docs/architecture/架构/13模块.md @ 7ecd616e - - docs/architecture/架构/契约总览.md @ 38357c3d ---- - -# 架构域图说 - -> **这是什么**:绘境AI 架构域的「看图入口」。架构域回答的是 HOW——**这套系统用什么技术、怎么搭起来**:分层怎么分、关键选型为什么选 A 不选 B、13 个后端模块各管什么又怎么依赖、一条「一句话生成游戏」的请求怎么跑通、游戏怎么安全地跑在玩家面前、契约族长什么样又在哪些地方已经漂了。这份图说把这些用一套图加配套讲解画清楚:复杂、跨模块的用手绘 SVG 大图,已经在各 README 里画好的 Mermaid 一律单源引用、绝不复制。 -> -> **给谁看**:CTO、技术合伙人、首席架构师、新加入的工程师、做模块边界与契约评审的人、外部技术尽调。判断「这套架构是不是想要的那个、哪里有设计缝」看这份图就够建立全局轮廓;某个子系统的更深细节(生成引擎 SAA 拓扑、沙箱、运维部署)去对应子文档与子域图说。 - ---- - -## 0. 阅读约定与同步纪律 - -- **映射的设计档**:本图说不另立设计,只把架构域三份 canonical 设计档画出来——整体分层、关键决策的「为什么」、模块依赖、生成时序、安全运行时、推荐打分出自 [`架构/README.md`](README.md);13 模块的逐个职责 / 边界 / 依赖 / 建设状态出自 [`架构/13模块.md`](13模块.md);契约族导航与口径收口出自 [`架构/契约总览.md`](契约总览.md)。frontmatter 里记了这三份的当前 commit hash,作为**防漂移门**:源档一旦变更、hash 对不上,这份图说与对应 SVG 即标「待复核」,由收口脚本比对。 -- **同步纪律**:**设计一变动,本图说与对应 SVG 必须同步更新**,每张 SVG 脚注注明映射源档与状态。已并入 wave 收口清单。 -- **单源原则**:本图集要消灭的就是「同一张图存两处、改一处漏一处」的漂移面。所以**凡 README 已内嵌的 Mermaid,一律不复制进来**——图说在叙述里链接到「见 README 第 X 节」,只新作 README 没有的图(SVG新 / Mer新)。本域 12 张图里有 7 张是这种单源引用(图 1/3/4/5/6/7/8),它们的图形本体在 README,这里只讲「这张图让人看出什么、有没有设计缝」。 -- **本域状态分布**:架构域绝大多数是「现行已建/已实测」,但有两类内容必须看清状态、绝不能画成纯现行——**决策史**(图 2/3 里被推翻的 Dify / OpenGame / 自研壳,是演进对照、不是现行架构)和**契约现实缝**(图 11 里 DB 镜像已漂移、CI 防漂移门缺位等,是声明与代码不符的待决策位)。每张图内部还会用实心块 / 虚线框 + 「废·决策史」「现实缝」小标把状态再标一层。 -- **产物形式**:SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 转(6c6g 禁 chrome、无转换器)。本图说只出 SVG;引用类不产新图,Mermaid 类 GitHub 直接渲染。 - -## 1. 全图通用图例 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上,架构域按图点其中几个): - -- **成本**(`#16a34a`):选 Huijing 买现成后台、便宜模型直出、单 key 入计费台账、不硬凑组件——都是「用 5 人小团队 + ¥4,300/月 跑起来」的取舍。 -- **质量**(`#15803d`):harness 九门兜底、三层约束框架、error_rate 硬降权保可玩底线——质量是设计进去的,不是 LLM 自评。 -- **数据**(`#475569`):契约即跨端数据边界、推荐用信号驱动排序、生成成本可对账——数据口径对不对,是这套系统能不能闭环的根。 -- **可靠**(`#16a34a`):幂等矩阵、最终一致 + 补偿、SLO + Error Budget、多通道兜底——涉及外部模型 / 异步 / 支付,可靠性必须前置。 -- **可观测**(`#0ea5e9`):四路信号(Metrics/Traces/Logs/Errors)+ SAA 节点 observation——但落地态是「接」,详见运维域。 -- **安全**(`#dc2626`):iframe 沙箱 + CSP 红线、契约防漂移门缺位是真实风险、脱敏——前端单点挡不住,必须在可信边界强制。 - -(另两个维度 **伸缩** `#7c3aed`、**可观测** `#0ea5e9` 在生成/治理图上点到。) - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现成直接用、同步动作、单向依赖或数据流。 -- **虚线**(`#64748b`,`stroke-dasharray="6 4"`):自建 / 解耦 / 远期 / 待接线 / 决策史的目标态或对照态。 -- **双线 / 加粗框**:跨域共享的公共件或需要强调的边界。 - -> 架构域图说里,虚线还额外承担一层**状态语义**:凡是「废·决策史」(图 2/3 被推翻的旧方案)、「待接线」(图 10 可观测落地)、「现实缝」(图 11 已漂移的契约)的元素,一律用虚线框 + 文字小标画出,与「现行已建」的实心块在视觉上一眼区分。这是本域最该守的纪律点——决策史和现实缝都极易被误读成「现在就是这样、而且没问题」,画得再好看也不能让人误判。 - ---- - -## 2. 图集 - -### 图 1 · 六层技术分层〔引·现〕 - -> 单源引用,不复制:整体六层分层的 Mermaid 已在 **[架构/README.md §1](README.md#1-一张图看懂整体分层)** 内嵌,是该图的唯一来源。这里只链接、不抄一份进来。 - -这张图回答「整套系统怎么搭起来」。绘境AI 的后端基于 Huijing Cloud(一套基于 Spring Cloud Alibaba 的开源 Java 企业级后台框架,我们 fork 它做二次开发)搭建,在它提供的基座能力之上叠加 13 个游戏业务模块。整套系统自上而下分六层:用户从浏览器进来,经**接入层**(CDN / Nginx)和**网关层**(Spring Cloud Gateway,统一路由 / 限流 / 鉴权 / 灰度),落到**业务服务层**(13 个游戏业务模块);业务服务依赖 Huijing 原生的**基础设施层**(system 用户权限 / infra 文件任务 / bpm 工作流),并向上调用一个独立的 **AI 生成层**来产出游戏(SAA 裸图编排 → new-api 网关 → 便宜 LLM,配 LittleJS + Runner v2 引擎);所有这些都坐在**中间件层**(MySQL / Redis / MinIO)之上,由旁路的**可观测性层**监控。 - -读这张图最该抓住的是它那条贯穿始终的设计取向:**先简后扩、能复用就不自研**。后端复用 Huijing 现成的 60% 后台能力,生成不自研大模型而接通用模型,引擎不自研而用成熟的 LittleJS——这条取向直接决定了系统能用很小的团队和很低的成本跑起来。要注意图里有一处必须按状态读的设计缝:中间件层画了 `Nacos · RocketMQ`,但旁边明写「框架自带 · MVP 未部署」——它们是 Huijing 框架自带的依赖、yaml 也在仓里,但 MVP 运行时并没有启动 broker / registry,凡涉及「异步 MQ」「服务注册发现」的设计在 MVP 阶段都以「进程内调用 / 本地配置」落地。这是 future-state,别当现行已部署。 - -### 图 2 · 关键选型决策树〔SVG新·讲解·现+史〕 - -![图 2 关键选型决策树](assets/arch-02-关键选型决策树.svg) - -这张图回答「架构里那几项最关键的选型,为什么选 A 不选 B」——架构里真正值钱的不是组件清单,而是这些决策背后的理由。图把对整体形态影响最大的四项决策横排,每项给出**现行选定**(绿色实心)、**同期落选候选**(灰框)、以及它击败对方的理由:① 后端框架选 Huijing Cloud(60%+ 后台开箱即用),击败缺企业级基础设施的 NestJS 和缺 RBAC/BPM 现成方案的 Go;② 生成主线分两条线、按品类各选其形:现行的 Tier0/1 廉价线选 new-api 网关 + 便宜 LLM + SAA 裸图(Java)编排(由三次裁定 C2 / HJ-GEN-001 / HJ-AGI-002 逐步锚定),远期的 tier2 富游戏线选 AgentScope(Python 自治 ReAct,独立 service)——一条用确定性图把便宜模型框住、一条放开 agent 自治去啃富交互,两种范式各管各的品类(详见图 3);③ 运行时引擎也随这两条线分轴:Tier0/1 廉价线用 LittleJS 增强发行版(Tier1 唯一交付层),tier2 富游戏自治轨用 Phaser/Pixi 全无头引擎(远期),而 Cocos 收窄到「3D / 复杂场景 / 渠道导出」这一轴(编辑器 + 人在环离线作者),三者击败启动重的 Unity、导出不含快手的 LayaAir——**注意 Cocos 不是被废弃,而是被收窄到它真正适配的那一档,仍是有效决策**;④ AI 素材工具链选 mmx-cli(免 GPU、免训练),ComfyUI 退备选。 - -这张图最该让人看清的是底部那条橙色虚线的**决策史带**——也是本图最该守状态纪律的地方。Dify(可视化 LLM 工作流平台)、OpenGame(现成游戏生成 agent)、自研轻量 Canvas Runtime <15KB(自研壳)这三项,在早期蓝图里都是「省自研时间、开箱即用」的首选,但随后的技术验证把它们逐一推翻:Dify+OpenGame 被「直连便宜模型 + 固定运行时更轻就达标」的 spike 证伪、降为远期增强从未部署(对应契约 #6 dify-workflow-io 同步作废);自研壳被 LittleJS 以 85/82 的 spike 比分击败、连 15KB 红线也一并废除(改为三层约束框架)。把它们画出来不是说现在还在用,而是为了理解「为什么现在是这样」——这是据图做架构 review 时最容易踩的坑:把演进中被推翻的旧方案误当现行。所以它们一律虚线框 + 「废·决策史」标,与绿色现行块一眼区分;现行 vs 决策史在生成主线上的更细对照见图 3。 - -### 图 3 · 生成主线 现行 vs 决策史〔引·流·现+废〕 - -> 单源引用,不复制:生成主线「现行 ✅ vs 蓝图原案 ❌」的两个 Mermaid(现行 SAA 链路 + 决策史 Dify/OpenGame)已在 **[架构/README.md §2.2](README.md#22-生成主线--new-api-网关--便宜-llm--saa-裸图编排现行)** 内嵌,是该图的唯一来源。这里只链接、不抄进来;选型层面的「击败了谁」总览见本域图 2。 - -这张图把生成主线这一处「演进最剧烈、也最关键」的决策单独放大,讲清现行那条链路到底长什么样、又是怎么从蓝图原案走过来的。**现行主线**(已后端实测)是一条:创作者 Prompt → SAA 裸 `StateGraph` 编排(16 节点 / 7 条件边)→ new-api 网关(OpenAI 兼容、多模型)→ 便宜 LLM(DeepSeek-v4 / MiniMax-M2.7·M3)→ harness 九门兜底(校验 / 构建 / 真玩)→ GamePackage 出包。几个术语一句话记住:**new-api 网关**让「换模型/多通道兜底」变成网关层的配置事、业务代码无感,且单 key 自动入计费平面(接入坑:baseUrl 要剥掉末尾 `/v1`);**harness 九门**意味着 done 由这些确定性门判定、不让 LLM 给自己打分,门不过就不出包;**SAA 图的唯一布线源**是 `SaaStudioGraph.assemble()`,生产派发与回归测试共用同一份 assemble、避免布线漂移。还有一处范式校正必须看清:这条线的**终态产物定为一份可维护的 `src/` 源项目**(配置驱动 / 模块化 / 资产分离的长生命周期项目,改源不改包、改完重新构建;改数值 / 换美术走确定性编辑免 LLM,改逻辑才让 LLM 重生成对应模块——创始人 2026-06-20 定调)。当前 generate 节点产出的 **gameDefinition 是结构化中间表示、不是合格终态产物**——声明式数据壳把逻辑核塞进 JSON 字符串、运行时用 `new Function` 解释,这只是「通往 `src/` 工程的中间脚手架」、属已知偏离终态的债;产线正把它迁成「直接生成 `src/` 源项目」(plan `2026-06-18-001` U1–U4,仍在落地,以生成引擎子树最新裁定为准)。 - -这张图要让人看出的设计缝在于「现行」与「决策史」的硬分界:蓝图原案 Dify + OpenGame 那一组(图里 ❌ 标「从未部署·降远期」)和现行 SAA 线之间没有渐变——不是「先用 Dify 后来换 SAA」,而是 Dify/OpenGame 从未部署、直接被验证更轻的路径取代。除此之外还有一条**两条生成线**的边界要一并记住(深度在生成引擎子树展开):现行这条 SAA + LittleJS 廉价线,是为超休闲轻游戏(Tier0/1)量身打造的可靠产线;经营 / 合成 / 多系统富交互那一档富游戏它结构性地够不着,由远期的 **tier2 富游戏自治轨**承载——那条轨换的是整套生成范式(AgentScope Python 自治 ReAct + 独立 service + Phaser/Pixi 引擎 + 三层校验地板 + 人工终审),与廉价线解耦并存、只经公共件交汇。两条线**远期有可能收敛为一条**——把 SAA + LittleJS 也并入 AgentScope 自治范式(登记为未来方向,当前不做、不预先设计)。读这张图时若想看「为什么是 SAA 而不是 Dify」的选型理由,回到图 2 的决策史带;若想看 SAA 16 节点拓扑、加节点方法、救场阶梯、dispatcher 契约、tier2 详设这些更深的实现细节,去生成引擎子文档与 `.agents/skills/saa-graph-orchestration.md`——本图只承载「现行主线轮廓 + 与决策史的边界」,深度留生成引擎子树。 - -### 图 4 · 13 模块聚类〔引·架·现〕 - -> 单源引用,不复制:13 模块按业务闭环位置归成四簇的 Mermaid 已在 **[架构/README.md §3](README.md#3-13-个后端业务模块总览)** 内嵌(与 [13模块.md §1](13模块.md) 同源),是该图的唯一来源。这里只链接、不抄进来。 - -这张图回答「13 个后端模块怎么分工」。绘境AI 把全部业务能力按领域边界(一类职责归一处)切成 13 个模块,追求高内聚、低耦合;它们物理上以 jar 聚合进 Huijing 单体一起运行(MVP 单体启动、Spring Profile 控制加载),逻辑上各自独立、可被未来拆分。按在业务闭环里的位置,13 个模块归成四簇:**创作链路**(studio 创作编排 / aigc 无状态生成原子 / runtime 编译沙箱发布 / project 项目生命周期)把游戏做出来;**分发与数据**(feed 游戏流推荐 / telemetry 事件聚合质量评分)让游戏被玩到、被看清;**变现链路**(pay 收单 / trade 分账结算 / ad 广告)把钱赚回来;**平台与合规**(community 社交通知 / ip 素材授权 / compliance 内容安全锁风门 / biz B端定制)让生态转得起来、守得住。 - -读这张图要记住一条配套口径:图里画的是「应该怎么切」(结构权威),不是「实际建成多少」。每个模块有一个三字母前缀,技术功能用 `T-{模块}-{nn}` 编号(全平台共 204 项,是结构注册表,只增不改号)。**真/桩/未建的实时状态以 `docs/mvp/MVP进度总账.md` 为准,冲突时口径是「状态 > 实现 > 结构」**——例如 ip 模块在结构上独立成簇,实际却是「seam 寄宿在 compliance 里」(有意不独立建),pay 是 Huijing 原生模块但当前未接入单体启动器。这张图给的是空间感和职责边界,看「这块功能落在哪个模块」用它,看「这块建到哪了」要去 13模块.md §4 的状态总表或进度总账。 - -### 图 5 · 13 模块单向依赖〔引·架·现〕 - -> 单源引用,不复制:13 模块单向依赖方向图(箭头 A→B 表示 A 依赖 B)已在 **[架构/README.md §4](README.md#4-模块依赖为什么是这个方向)** 内嵌(与 [13模块.md §2](13模块.md) 同源),是该图的唯一来源。这里只链接、不抄进来。 - -这张图回答「模块之间为什么是这个依赖方向」。模块间是单向、低耦合的依赖——A 依赖 B、B 绝不反向依赖 A,且只通过每个模块的 `-api` 包(只声明 DTO + Feign 接口、不含实现)交互。读这张图有四条主线:**创作侧 studio 站在最上游**,把活分派给 aigc(生成)、runtime(编译)、ip(素材)、project(落库)、compliance(安全裁决),它自己几乎不被别人依赖——这是「工作台」该有的位置;**生成原子 aigc 保持无状态**,只向下依赖 compliance(Prompt 安全)和 project(写结果),无状态意味着它天然可重放、可横向扩容;**数据回路 telemetry ↔ feed 互相依赖**——这是图里唯一一处双向边(feed 消费质量分来排序、telemetry 回收互动信号来计算),正是平台「越用越聪明」的机制所在;**资金侧 trade 向下收口**,依赖 pay(收单)和 ad(广告收入)拿原始数据,自己只管分账结算。 - -这张图最该让人看出的是「为什么单向切分值得」:这种切法让任何一个模块都能独立演进、独立测试,也是未来从单体拆成微服务时的天然切割线——依赖方向就是拆分边界。要注意图里还藏着两处「风格-版权」与「专区」的跨模块协作环路(compliance ↔ ip ↔ aigc),它们不是普通的模块依赖,而是横切关注点,单独由图 12 讲清 owner 与聚合关系,这里只需先记住「telemetry↔feed 那条双向边是有意的、不是设计漏洞」。 - -### 图 6 · 一条生成请求时序〔引·时·现〕 - -> 单源引用,不复制:「一句话生成游戏」的完整时序图(创作者 → studio → Gateway → aigc → SAA → new-api → LLM → 九门 → project)已在 **[架构/README.md §5](README.md#5-一条生成请求是怎么跑通的)** 内嵌,是该图的唯一来源。这里只链接、不抄进来;生成任务状态机见本域图 7。 - -这张图回答「把分层、生成主线、模块依赖串起来,一次生成到底怎么跑通」——这是创作链路的核心路径。时序从创作者输入 Prompt + 选风格开始:game-studio 发 `POST /app-api/aigc/generate` 经 Gateway 鉴权转发到 aigc 模块,aigc 创建生成任务(由 `GenerationDispatcher` 派发)后**立即返 202 Accepted + taskId**、前端转去轮询/SSE 监听;真正的生成在后台展开——aigc `dispatch(job)` 进入 SAA 裸图编排,按 render→classify→design→generate 等 16 节点跑,各节点经 new-api 的 OpenAI 兼容接口调模型(单 key 自动入 newapi_cost 计费),模型产出 GameConfig / 游戏代码后过 harness 九门(done 由确定性门定、不过则走修复回环),通过才出 GamePackage、回调写入 project 草稿版本、通知前端任务完成。 - -读这张图要抓住两个关键设计:其一,**生成是异步的**——202 + taskId + 轮询这套,是因为外部模型调用耗时不可控(生成 P50<60s、P95<180s),不能让创作者的请求线程一直挂着;其二,**派发走 `GenerationDispatcher`、生成态藏在 job/callback 契约后**——http worker 与进程内 SAA 图二选一、单写,这层抽象让「现在用进程内 SAA、将来换 http worker」变成可替换的实现细节,调用方无感。这正是图里把 SAA 编排画在 dispatcher 之后的原因:它不是直接被 controller 调,而是被 dispatcher 按契约派发。 - -### 图 7 · 生成任务状态机〔引·状·现〕 - -> 单源引用,不复制:生成任务状态机(QUEUED→RUNNING→SUCCEEDED/FAILED/TIMED_OUT + 重试/取消迁移)已在 **[架构/README.md §5](README.md#5-一条生成请求是怎么跑通的)** 内嵌(紧接时序图后),是该图的唯一来源。这里只链接、不抄进来。 - -这张图回答「生成任务自己的生命周期怎么管」。生成任务是一个有明确状态机的对象,它给「超时/失败/重试/取消」都定义了清楚的边:任务创建即 QUEUED,被消费/派发进 RUNNING;RUNNING 有三个出口——生成完成且质量通过 → SUCCEEDED,生成失败或质量不达标 → FAILED,超时(120s)→ TIMED_OUT;FAILED 可重试 ≤2 次回 QUEUED,TIMED_OUT 可重试 ≤1 次回 QUEUED,超过重试次数才终态 FAILED;用户取消则从 QUEUED 或 RUNNING 进 CANCELED。 - -这张图最该让人看出的,是它把「任何涉及外部模型调用的链路都必须考虑的可靠性设计」具象化了——状态机不是为了好看,而是为了把「模型可能慢、可能失败、可能要让用户取消」这些现实约束变成代码里可执行、可观测的边。配套的硬指标钉在状态机外:生成成功率 ≥80%(MVP 验收线,远期蓝图 ≥85%)、队列最大积压 500 任务、超过返回 429。读这张图时要把它和图 6 配着看:图 6 给「一次成功生成怎么跑」的正路,图 7 给「失败/超时/取消怎么收口」的全路径——正路只是状态机里 QUEUED→RUNNING→SUCCEEDED 那一条主干。 - -### 图 8 · 游戏安全运行时序〔引·时·现〕 - -> 单源引用,不复制:游戏在 iframe 沙箱里安全加载运行的时序图(请求 manifest → 校验 → 并行取 entry+assets → 创建 iframe+CSP → 注入 GameConfig+SDK → 执行 → postMessage 回报)已在 **[架构/README.md §6](README.md#6-游戏怎么安全地跑在玩家面前)** 内嵌,是该图的唯一来源。这里只链接、不抄进来;沙箱与受控面两条正交边界的更深拆解见 [产物执行沙箱.md](产物执行沙箱.md) 及其图说。 - -这张图回答「生成出来的游戏怎么安全地跑在玩家面前」。生成出来的游戏运行在 iframe 沙箱(浏览器内嵌的隔离框架)里,与平台彻底隔离;平台能力靠 WanxiangGameSDK 注入进去——这是平台向游戏注入能力的**唯一通道**,没有它平台就只是个静态文件托管。时序从 game-studio 请求 manifest.json(hash 缓存)开始:拿到 `{entry, assets[], checksum}` 后先校验 manifest 完整性,再并行请求 entry.js + 关键 assets,创建带 CSP + sandbox 的 iframe,注入 GameConfig + Game SDK bridge,沙箱内执行 entry.js 初始化游戏、回 postMessage `game_loaded`,游玩中 SDK bridge 持续上报生命周期事件、结束回报 `game_complete + score`。加载用三容器策略(参考抖音预加载):当前播放的容器之外,前一个在销毁、后一个已预加载完成,上滑切换无缝。 - -这张图最该让人看清的是几条不能破的安全红线——它们是安全与合规可控的根,画在时序之外但比时序更重要:**iframe 沙箱**用 `sandbox="allow-scripts allow-same-origin"`,但有一条 2026-06-10 审计补的红线——`allow-same-origin` 仅当游戏包部署在独立源(与宿主不同源的 usercontent 子域)时才可用,同源下 iframe 能触宿主 DOM/存储甚至自己移除 sandbox,沙箱铁律就失效,所以独立源就绪前禁用 `allow-same-origin`;**CSP** 是 `script-src 'self'; connect-src 'none'`,即游戏内无任何网络请求,且必须经游戏包托管侧的 HTTP 响应头下发、不能只靠 meta 标签;**LLM 产物消毒**——GameConfig 文案字段入库前做白名单字符集 + 长度校验,渲染侧一律转义后绘制,禁 innerHTML/eval。底线原则是:创作者通过配置(不是写代码)驱动游戏行为,平台对运行时代码拥有完全控制权。 - -### 图 9 · 推荐打分公式〔SVG新·讲解·现〕 - -![图 9 推荐打分公式](assets/arch-09-推荐打分公式.svg) - -这张图回答「好游戏怎么被刷到」。MVP 阶段的推荐是规则 + 信号、不上机器学习(那是增长期的事)。每款游戏的曝光排序由一个打分公式决定,图把这个公式拆成三类信号画清楚:**正向信号加分**(quality_score 综合质量分 / freshness 新鲜度 / interaction_rate 互动率,让好游戏浮上来)、**负向信号减分**(skip_rate 跳过率 / error_rate 错误率 / report_rate 举报率,让差游戏沉下去)、**调节项**(bonus_new_creator 新人保底曝光 + bonus_featured 运营精选加分,给生态公平起点和运营调控手)。候选集存在 Redis Sorted Set(TTL 60s,cursor 分页,Sorted Set 天生按分排序适配无限流),由 feed 读取下发成竖屏游戏流。 - -这张图最该让人看出的有两点。其一,**error_rate 是唯一的「硬」降权**(图上用红框单标)——加载失败/试玩次数高的游戏直接沉底,因为可玩性是底线,技术上跑不动的游戏一票否决排序,这条把「好不好玩」之前先卡住「能不能玩」。其二,**这套规则为什么够用**:右下角那条飞轮回路画出了「玩家在 feed 玩/互动 → telemetry 回收信号算 quality_score → 回喂 Score 重排候选集」的闭环——质量好 + 爱玩的游戏自然浮上来,数据回流持续校准推荐,这正是平台护城河的来源,不需要 ML 模型在 MVP 阶段就能把排序做对。要按状态读的设计缝在「MVP 现实形态」那个橙虚线框里:候选集**当前可直查 MySQL、Redis 缓存是增长期形态,且游标分页目前是桩(永远返回第一页)**——公式和飞轮是设计真相,但 Redis 候选集与真游标分页的落地仍是增长期的事,别当已全建。 - -### 图 10 · 工程治理(幂等 / SLO / 可观测)〔SVG新·讲解·现〕 - -![图 10 工程治理 幂等/SLO/可观测](assets/arch-10-工程治理.svg) - -这张图回答「可靠性和质量怎么被钉死」。这套系统涉及外部模型、异步任务、支付,所以可靠性不是事后补的、而是设计进去的。图把三条贯穿的硬约束并排画出:**① 幂等矩阵**——重复点「生成」靠 idempotency_key(Redis 5 分钟去重)、支付回调重复靠订单状态机 + 乐观锁、发布重复提交靠 project_version 唯一约束,配套原则是尽量避免分布式事务、改用最终一致 + 补偿、并用定时任务扫「中间态超 5 分钟」的记录兜底;**② SLO + Error Budget**——游戏流 API 99.5%(预算 3.6 小时/月)、AI 生成 99%(预算 7.2 小时/月,外部模型不可控故给得宽)、支付 99.9%(预算 43 分钟/月,钱相关最严),整体可用性 ≥99.5%(MVP);**③ 四路可观测**——业务服务把 Metrics/Traces/Logs/Errors 分送 Prometheus/Jaeger/Loki/Sentry,汇到 Grafana。 - -读这张图要抓住一个状态分界:**幂等机制与 SLO 目标是现行(写进设计、代码已落),但右侧四路可观测的「落地手段」整体是「接」(待接线)**——图上把四路信号都画成橙虚线 + 「接」小标,只有「SAA 编排节点 observation + new-api 调用埋点」是已就位的实心块,落地状态以运维域观测体系图为准。这是本图最该守的纪律:可观测的「设计意图」是真的,但「后端真接上 Prometheus/Grafana」大半还没做,不能因为 README §9 写了四路就以为监控已建好。图底那条红色风险带也值得记住——它列的不是假想风险而是设计里钉死的应对:**LLM 网关单点通道批量失效**(2026-06 已真实发生:二厂系通道全废,应对是多通道健康巡检 + key 监控 + 降级抽检 + 批跑冻结阀,new-api 网关正是为此设计)、**LLM 成本失控**(应对是限频限额 + 日预算熔断 + 显式 max_tokens)、以及**监管与上游平台风险**(非纯技术,由合规专项承载、登记在此防失踪)。 - -### 图 11 · 8+2 类契约族总览(声明 vs 现实)〔SVG新·架·现〕 - -![图 11 8+2 类契约族总览](assets/arch-11-契约族总览.svg) - -这张图回答「契约族到底有哪几类、各落哪、各算第几」——也是本域**最该据图做评审**的一张,因为它不只画理想的「应该怎样」,更如实标出已经漂掉的地方。图按三个物理落点组织:**落点① contracts/ 顶层**(跨三仓五工位 SSOT,含跨仓 8 类 + agent-loop/ + templates/)、**落点② game-runtime/src/ 内部接线面**(第 9 类 game-host.d.ts 装载契约 + 第 10 类 runtime-api-2d.d.ts 运行时访问约定,不落 contracts 是因为落了会让跨仓 SSOT 反向 import、拆仓即断)、**落点③ Java 镜像 -api 包**(contracts 的代码镜像,每模块一份,同步序单向:后端先写 -api、前端据此定 TS)。主编号轴只有一条:README 的 1–8 类 + 后来 additive 的第 9/第 10。 - -这张图的价值全在底部那条红色的**五处现实缝**带——这是《契约总览》自陈、也是据图 review 最该揪的:**缝① DB 镜像已漂移**(README §41 声称 contracts/db-schemas 是授权源、要求 diff 一致,实情是执行副本 25 个 V*.sql、contracts 只有 18 个、缺 V14–V20,真授权源其实是执行副本那 25 个全集);**缝② CI 防漂移门缺位**(文档把 flyway validate / contracts↔migration diff / prompt 四道闸写成 CI 自动拦截,核到代码:全仓唯一 CI 是 yudao fork 继承件、on push to master 在本仓 dev/2.0.0 永不触发、还跳测、无任何 diff 步——它们是文档承诺不是机器门);**缝③ Dify #6 已废**(dify-workflow-io 对应被推翻的蓝图原案,是 8 类里唯一一个「废」,留 DEPRECATED 墓碑,读到别当现行);**缝④「第 9 契约」五套编号相撞**(跨仓主轴第 9 类装载契约才是真正的第 9,其余「第 9 契约组 9a–9g」「9c」「生成主线 8 契约①–⑧」都是局部上下文,撞数字不撞语义,引用契约要认上下文不认数字);**缝⑤ agent-loop / templates 在 README 目录图漏登**(那张图只画了跨仓 8 类,新人 ls 到这两目录无从对应,本图与《契约总览》补登它们)。把这五处诚实画出来,正是为了让评审者据图就能发现「声明与代码不符」的待决策位——要么补一道真门(wave-close 或 pre-commit 加 contracts↔migration 的 cmp,漂移即拦截),要么裁定执行副本为唯一源、把 contracts/db-schemas 降只读快照,无论哪条都得先让 README §41 的口径和代码实情对上。 - -### 图 12 · 专区 Zone + 锁风门 Gate〔Mer新·讲解·现〕 - -这张图回答「两个跨模块概念的 owner 与聚合关系」。有些能力不属于单一模块,而是横跨多个模块协作完成;为避免「人人都管、结果没人管」的灰色地带,每个横切关注点都指定唯一的 owner(责任主)。MVP 阶段有两个这样的概念,图把它们的 owner、原料供给方、消费方画成一张概念图:**锁风门 Gate**(T-CMP-12,一道「风格-版权一致性」门)owner 是 compliance,它本身不检测风格,而是聚合 aigc 的风格检测原子(T-AGC-19)和 ip 的 IP 风格校验原子(T-IP-04),综合裁决出 pass / review / block,挂在 project 的发布前检查(T-PRJ-05)上;**专区 Zone**(T-PRJ-07,运营双轨分区:授权 IP 区 / UGC 用户原创区)owner 是 project,feed 据它分区推荐(T-FED-15)、ip 据它做素材双轨归类(T-IP-10)、studio 决定创作去向。 - -```mermaid -flowchart TB - subgraph GATE["锁风门 Gate(owner = compliance · T-CMP-12)"] - direction TB - AGC19["aigc 风格检测原子
T-AGC-19"]:::raw - IP04["ip · IP 风格校验原子
T-IP-04"]:::raw - CMP12["compliance 聚合裁决
pass / review / block
(标准 / 严格 / 人工复核)"]:::owner - PRJ05["project 发布前检查
T-PRJ-05(消费裁决)"]:::consume - AGC19 -->|供原料| CMP12 - IP04 -->|供原料| CMP12 - CMP12 -->|挂载| PRJ05 - end - subgraph ZONE["专区 Zone(owner = project · T-PRJ-07)"] - direction TB - PRJ07["project · Zone 实体 / 归属 / 运营位
授权 IP 区 ‖ UGC 区"]:::owner - STU["studio
决定创作去向"]:::consume - FED15["feed 据 Zone 分区推荐
T-FED-15(双区独立候选)"]:::consume - IP10["ip 据 Zone 双轨归类
T-IP-10"]:::consume - STU -.->|写入归属| PRJ07 - PRJ07 -->|分区依据| FED15 - PRJ07 -->|归类依据| IP10 - end - - classDef owner fill:#dcfce7,stroke:#16a34a,stroke-width:2px; - classDef raw fill:#eff6ff,stroke:#2563eb,stroke-width:1.5px; - classDef consume fill:#f1f5f9,stroke:#475569,stroke-width:1.5px; -``` - -这张图最该让人看出的是「owner 模式怎么消灭灰色地带」:锁风门由 compliance 当 owner(因为它是全平台的内容安全与裁决中枢),但它**不自产**风格检测原子——原料来自 aigc 和 ip,裁决挂到 project,三方各司其职、没有谁越界。这正是图 5 模块依赖里那条 compliance ↔ ip ↔ aigc 环路的真实含义。要按状态读的设计缝有一处:**专区 Zone 在 MVP 阶段还没有独立的数据库实体,是用字段承载的**(有意简化),所以图里 Zone 实体那一格画的是「逻辑归属」、不是一张独立表——这是 project 模块已知的、有意为之的缺口,不是设计漏洞。锁风门则有一处真实的合规债(虽不在本概念图的范围内、但读图时该知道):compliance 的核心检测原子目前全是桩、恒返回 pass,意味着锁风门框架虽真、自动拦截能力还没硬化,这是放量/接广告/上渠道前必须补的。 - ---- - -## 3. 图清单与状态表 - -| # | 图名 | 家族 | 形式 | 状态 | 覆盖内容 | -|---|---|---|---|---|---| -| 1 | 六层技术分层 | 架 | 引(链 README §1) | 现 | 接入→网关→业务→基座→AI→中间件 + 旁路可观测;先简后扩取向;Nacos/RocketMQ = future-state 未部署 | -| 2 | 关键选型决策树 | 讲 | SVG新 | **现+史** | 框架(Huijing)/生成两线(廉价 new-api+SAA ‖ tier2 AgentScope)/引擎分轴(LittleJS ‖ Phaser/Pixi ‖ Cocos 收窄 3D+渠道导出)/素材(mmx-cli) 四选定击败谁;废弃候选 Dify/OpenGame/自研壳作决策史;Cocos=收窄非废弃 | -| 3 | 生成主线 现行 vs 决策史 | 流 | 引(链 README §2.2) | **现+废** | 现行 SAA+LittleJS 廉价线 16 节点链路 vs 蓝图原案 Dify/OpenGame;终态产物=src 源项目(gameDefinition JSON 为中间脚手架·迁移中);两条生成线边界 + tier2 远期可能收敛入 AgentScope | -| 4 | 13 模块聚类 | 架 | 引(链 README §3) | 现 | 四簇 13 模块 + T-id 注册表;结构权威 vs「状态>实现>结构」口径 | -| 5 | 13 模块单向依赖 | 架 | 引(链 README §4) | 现 | 单向 -api 依赖四主线;telemetry↔feed 唯一双向边;依赖方向=未来微服务切割线 | -| 6 | 一条生成请求时序 | 时 | 引(链 README §5) | 现 | 202+taskId 异步派发 + SAA 编排 + 九门 + 回调写库;dispatcher 契约可替换 | -| 7 | 生成任务状态机 | 状 | 引(链 README §5) | 现 | QUEUED→RUNNING→SUCCEEDED/FAILED/TIMED_OUT + 重试/取消;成功率≥80%/积压500/429 | -| 8 | 游戏安全运行时序 | 时 | 引(链 README §6) | 现 | manifest 校验→iframe+CSP→注入 SDK→三容器;allow-same-origin/CSP/产物消毒三红线 | -| 9 | 推荐打分公式 | 讲 | SVG新 | 现 | Score 正负信号加权 + 调节项 + Redis 候选集;error_rate 硬降权;飞轮回路;候选集/游标桩现实形态 | -| 10 | 工程治理(幂等/SLO/可观测) | 讲 | SVG新 | 现 | 幂等矩阵 + 三服务 SLO/Error Budget + 四路可观测(落地态=接)+ 三关键技术风险及应对 | -| 11 | 8+2 类契约族总览 | 架 | SVG新 | 现 | 三落点 + 主编号轴;诚实标出 5 缝:DB 漂移/CI 门缺位/Dify#6 废/第9契约五套相撞/agent-loop+templates 漏登 | -| 12 | 专区 Zone + 锁风门 Gate | 讲 | Mer新(内联) | 现 | 两横切概念 owner 与聚合:Gate=compliance 聚合 AGC19+IP04 挂 PRJ05;Zone=project owner;Zone 无独立实体(字段承载) | - -> **状态分布**:现 ×10、现+史 ×1(图 2,决策史对照)、现+废 ×1(图 3,含废弃蓝图)。**注**:图 2/3 的「史/废」指其中**并列的决策史/废弃候选**部分(Dify/OpenGame/自研壳),现行选定部分仍是「现」;图 9/10/11 虽整体「现」,但各含一处必须按状态读的缝(候选集桩 / 可观测落地=接 / 5 处契约现实缝),已在散文与图内标清。**防漂移门**:本图说 frontmatter 记三份源档的 commit hash(README/契约总览 @ 38357c3d、13模块 @ 7ecd616e),源档变更即比对,hash 对不上则本图说与对应 SVG 标「待复核」。 -> -> **PNG 后续**:12 张图中 4 张为 SVG(图 2/9/10/11),SVG 入仓后在 mini-desktop 批量转 PNG(6c6g 禁 chrome);1 张 Mermaid(图 12)GitHub 直接渲染、无需转换;7 张(图 1/3/4/5/6/7/8)单源引用 README 已有 Mermaid、不产新图。 diff --git a/docs/architecture/系统全图说.md b/docs/architecture/系统全图说.md index b337f746..24c9d97a 100644 --- a/docs/architecture/系统全图说.md +++ b/docs/architecture/系统全图说.md @@ -2,22 +2,22 @@ date: 2026-06-22 topic: 系统全图说——绘境AI 架构图集的单一全量主档(总图 + 目录 + 全部图说 inline 在一篇,所有图经顶层相对路径引用故全部真渲染),取代原导览式 系统全景图说.md 与分散各域图说 status: 单一全量主档(系统级 7 图 + 七领域全部图说 inline)· 跨多档,frontmatter 记全部源档的 commit hash · 防漂移门 -映射源档(8 份图说): - - docs/architecture/系统全景图说.md @ f9d6baa5 - - docs/architecture/产品/产品图说.md @ 7fff6518 - - docs/architecture/架构/架构图说.md @ 7fff6518 - - docs/architecture/架构/产物执行沙箱图说.md @ 7fff6518 - - docs/architecture/后端/后端图说.md @ 7fff6518 - - docs/architecture/前端/前端图说.md @ 7fff6518 - - docs/architecture/运营/运营图说.md @ 7fff6518 - - docs/architecture/运维/运维图说.md @ 891a5777 -映射源档(6 份 README · 引图 Mermaid 来源): +映射源档(本文据各域设计档可视化而来;每图的具体映射见正文该图脚注 / 内联小注): - docs/architecture/产品/README.md @ 366a44b4 - - docs/architecture/架构/README.md @ 38357c3d + - docs/architecture/架构/README.md @ ae7d78dd - docs/architecture/后端/README.md @ 38357c3d - docs/architecture/前端/README.md @ 15b707fd - docs/architecture/运营/README.md @ 3e71715a - docs/architecture/运维/README.md @ 15b707fd + - docs/architecture/架构/13模块.md @ 7ecd616e + - docs/architecture/架构/契约总览.md @ 38357c3d + - docs/architecture/架构/产物执行沙箱.md @ ae7d78dd + - docs/architecture/后端/数据模型.md @ 38357c3d + - docs/architecture/后端/鉴权与权限.md @ 38357c3d + - docs/architecture/运维/观测体系.md @ 3e71715a + - docs/architecture/运维/k8s迁移.md @ 15b707fd + - docs/architecture/架构/生成引擎/README.md @ 37f5eddb + # 产品/运营 的细分设计档(需求清单·需求模块映射·商业定位·护城河话术 / 变现与单位经济·变现端到端·渠道发行·合规闸门·审核台运营)经各域 README 导航,具体映射见各图脚注 --- # 系统全图说 @@ -1718,4 +1718,4 @@ flowchart LR --- -> **全文防漂移门汇总**:本文 frontmatter 记了全部 8 份图说源档(系统全景图说 @ f9d6baa5、产品/架构/产物执行沙箱/后端/前端/运营 6 份图说 @ 7fff6518、运维图说 @ 891a5777)+ 6 份 README(引图 Mermaid 来源:产品 @ 366a44b4、架构 @ 38357c3d、后端 @ 38357c3d、前端 @ 15b707fd、运营 @ 3e71715a、运维 @ 15b707fd)的 commit hash;任一源档变更、hash 对不上,本文与对应 SVG 即标「待复核」,由收口脚本比对。**SVG 不动**:本文不动任何 SVG 文件,只经顶层相对路径引用它们;PNG 后续在 mini-desktop 批量转(6c6g 禁 chrome),Mermaid 图 GitHub 直接渲染、无需转换。 +> **全文防漂移门汇总**:本文 frontmatter 记了各域设计档(6 域 README + 架构 13模块/契约总览/产物执行沙箱、后端 数据模型/鉴权与权限、运维 观测体系/k8s迁移、生成引擎 README 等)的 commit hash;每图的具体映射源档另见正文该图脚注 / 内联小注。任一源档变更、hash 对不上,本文与对应图即标「待复核」,由收口脚本比对。**SVG 不动**:本文不动任何 SVG 文件,只经顶层相对路径引用它们;PNG 后续在 mini-desktop 批量转(6c6g 禁 chrome),Mermaid 图 GitHub 直接渲染、无需转换。 diff --git a/docs/architecture/系统全景图说.md b/docs/architecture/系统全景图说.md deleted file mode 100644 index e8a87644..00000000 --- a/docs/architecture/系统全景图说.md +++ /dev/null @@ -1,247 +0,0 @@ ---- -date: 2026-06-22 -topic: 系统全景图说——绘境AI 架构图集的顶层总图叙(导览式 apex):读它一遍即建立全局认知,并据它下钻到任一领域、据图 review 全局设计有没有缝 -status: 现行为主(系统级 7 图 + 七领域导航)· 跨多档,frontmatter 记主要几份源档的 commit hash · 防漂移门 -映射源档: - - docs/architecture/README.md @ ec7fda2c - - docs/architecture/架构/README.md @ 38357c3d - - docs/architecture/产品/README.md @ 366a44b4 - - docs/architecture/运营/README.md @ 3e71715a - - docs/architecture/运维/README.md @ 15b707fd - - docs/architecture/后端/数据模型.md @ 38357c3d - - docs/architecture/后端/鉴权与权限.md @ 38357c3d - - docs/architecture/架构/契约总览.md @ 38357c3d - - docs/architecture/架构/生成引擎/README.md @ 37f5eddb ---- - -# 系统全景图说 - -> **这是什么**:绘境AI 架构图集的**顶层总图叙**,整套图集的 apex。它的目标只有一个——让一个人**读它一遍**(看图加讲解)就建立起对整个系统的全局认知:这是个什么系统、分几层、有几条生成轨、数据怎么流、钱怎么转;读完之后,既能据它**下钻**到任意一个领域的图说去看细节,也能据它**做全局 review**,发现现行与远期的边界、跨域的裂缝、失败路径有没有兜住。 -> -> **它不是什么**:它不是把全图集一百多张图都塞进来的"大杂烩"。它只内联**系统级、跨域、没有任何单个领域拥有**的那几张新图,然后对七个领域各写一段导览散文,把人领到对应的领域图说门口。领域已经画过的图(产品闭环、13 模块依赖、生成请求时序、全平台 ER、契约现状、护城河、部署拓扑、生成引擎深度)在这里**一律只链接、不重画**——同一张图绝不在两处各存一份。 -> -> **给谁看**:第一次接触这个项目、想先建立全局轮廓的任何人;要据图做全局架构评审、找设计缝的技术负责人;以及想知道"系统每一块在哪、各自的命门是什么"的产品、运营与投资尽调读者。 - ---- - -## 0. 阅读约定:这是什么、怎么读 - -**这份总图叙的形态,是"先定向、再下钻"。** 它分两段功能:第 2 章是**系统定向图集**,用七张系统级大图把整个系统的轮廓自顶向下讲清楚;第 3 章是**领域导航**,对七个领域各写一段散文,告诉你那个域的图在讲什么故事、最该据图 review 的关键设计缝在哪、链接到哪份图说。建议的读法是:**先把第 2 章七张图连讲解读完**(建立全局轮廓),**再按你的角色或当前任务,选第 3 章某一两个领域下钻**(第 4 章给了按角色推荐的路径)。读完第 2 章,你应该能回答"这是个什么系统、分几层、两条生成轨现行 vs 远期怎么切、数据怎么回流、钱怎么转";读完第 3 章对应段,你应该知道"这块的图在哪、它的命门是哪条缝"。 - -**单源纪律(本图集的硬约束)。** 这份图集要消灭的就是"同一张图存两处、改一处漏一处"的漂移面。所以本总图叙只**内联**第 2 章那七张系统级新图——它们都是跨域才画得出来、没有任何单个领域拥有的图。凡是某个领域已经画过的图,本文一律**只在散文里链接、不复制**:产品闭环全景在产品图说、13 模块依赖与一条生成请求时序在架构图说、全平台数据模型 ER 在后端图说(图 5,全仓唯一 ER 权威)、契约现状在架构图说(图 11)、四层护城河与三线变现在运营图说、部署拓扑与观测在运维图说、生成引擎的深度(SAA 16 节点拓扑、九门、tier2 详设)在生成引擎子树。本文的系统级图与领域图**互补、不重复**:领域图回答"这一块内部长什么样",系统级图回答"这些块如何跨域串成一个整体"。 - -**同步纪律 + 防漂移门。** 设计档一变动,本图说与对应 SVG 必须同步更新。系统级图跨多份源档,frontmatter 记了主要几份的当前 commit hash 作为防漂移门:源档一旦变更、hash 对不上,本图说与对应 SVG 即标"待复核",由收口脚本比对。每张 SVG 脚注都注明了它的映射源档与状态。 - -**现行 vs 远期 / 已废,严格标注。** 系统级图里凡涉及未来式或被推翻的方案,一律用虚线框 + 文字小标画出,与"现行已建"的实心块一眼区分。三类边界必须看清:**远期·待 spike**(tier2 富游戏自治轨 = AgentScope + Phaser,见图 3——只有设计、未落代码,绝不画成现行已建)、**future-state**(Nacos / RocketMQ 框架自带但 MVP 未部署、k3s 缓做、观测体系待接线、两条生成线远期可能收敛为一条)、**已废**(Dify / OpenGame 从未部署降远期、玩法填参式模板与 15KB 红线已废除)、**终态迁移中**(廉价线产物的终态定为 `src/` 源项目,现行的声明式 gameDefinition JSON 只是通往它的中间脚手架、正迁移成直接生成 `src/`)。把这些诚实画出,正是为了让据图 review 的人不会把演进中或已废的方案误当现行。 - -**产物形式。** 系统级 SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 批量转(6c6g 禁 chrome、无转换器),本文只产 SVG 与本 md;角色×端那张是 Mermaid,GitHub 直接渲染、无需转换。 - ---- - -## 1. 全图通用图例 - -整套图集(含本总图叙)共用一套视觉约定,读任何一张图都按这套理解。 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上)。七个维度对应七种"这套设计在为什么负责": - -- **安全**(`#dc2626`):iframe 沙箱 + CSP 红线、权限只在后端可信边界强制、打款 fail-fast 绝不发假钱、上游 API key 不裸暴露。 -- **可观测**(`#0ea5e9`):四路信号(Metrics / Traces / Logs / Errors)+ SAA 节点 observation——但落地态是"接"(待接线),详见运维域。 -- **可靠**(`#16a34a`):幂等矩阵、最终一致 + 补偿、SLO + Error Budget、多通道兜底——涉及外部模型 / 异步 / 支付,可靠性必须前置。 -- **成本**(`#16a34a`):复用 Huijing 买现成后台、便宜模型直出、单 key 入计费台账、内网物理机不上云——都是"5 人小团队 + ¥4,300/月"跑起来的取舍。 -- **数据**(`#475569`):契约即跨端数据边界、推荐用信号驱动排序、行为数据回流"越用越聪明"——数据口径对不对,是系统能不能闭环的根。 -- **伸缩**(`#7c3aed`):单体平滑长成微服务、Redis 候选集适配无限流、声明式编排平滑长多节点。 -- **质量**(`#15803d`):harness 九门兜底、三层约束框架、error_rate 硬降权保可玩底线——质量是设计进去的,不是 LLM 自评。 - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现成直接用、同步动作、单向依赖或数据流的正向推进。 -- **虚线**(`#64748b`,`stroke-dasharray="6 4"`):自建 / 解耦 / 远期 / 待接线 / 决策史的目标态或对照态;远期·待建项用紫色 `#7c3aed`,失败 / 资金红线分支用红色 `#dc2626`。 -- **双线 / 加粗框**:跨域共享的公共件,或需要强调的硬边界(如两条生成轨唯一交汇的公共件、资金硬边界)。 - -**状态码**(贯穿全图集):**现** = 现行已建 / 已实测 · **接** = 现行待接线(设计已出、代码未接) · **建** = 待建 · **缓** = 缓做(排在 W-G1 之后) · **F / future** = future-state(框架自带未部署 / 远期轨) · **废** = 决策史对照(被推翻的旧方案)。系统级图里,凡远期 / 待接 / 缓做 / 已废的元素一律虚线框 + 小标,绝不画成现行。 - ---- - -## 2. 系统定向图集 - -下面七张图是这份总图叙的核心。它们都是**跨域才画得出、没有任何单个领域拥有**的系统级图。按"先看全局形状、再看两条生成轨、再看数据与钱与可靠性与边界"的顺序读下来,就能自顶向下建立对整个系统的认知。 - -### 图 1 · 六域生命周期全景〔SVG·架·现〕 - -![图 1 六域生命周期全景](assets/01-六域生命周期全景.svg) - -这张图回答最顶层的问题:**绘境AI 是个什么系统,它的六个领域如何串成一条主线**。设计文档按产品生命周期的六个领域组织——产品(用户要给谁、给什么)、架构(用什么技术、怎么搭)、后端与前端(并列的实现层,同受架构约束)、运营(合规上线、变现、发行)、运维(部署、健康、可用性)。这六域不是六个互不相干的孤岛,而是一条**接力链**:产品定义出"做什么",架构据此定"怎么搭",前后端把它实现出来,运营让它合规变现,运维让它持续跑得住。 - -读这张图要抓住三件事。其一,**它是一条闭环、不是六个孤岛**——判断"系统有没有缝",就是看相邻两域的交接处对不对得上;一个具体的例子是后端定义的五条业务链(创作 / 发布 / 试玩 / 广告收益 / 数据回路)恰好就是运维冒烟门按链分组点检的那五条,两域在这个接口上必须严丝合缝。其二,**它不是瀑布,是带回流的循环**——图上方那条贯穿全宽的大虚线,是玩家行为加收益数据回流、反哺产品优先级与生成质量的飞轮;正是这条回流把一条线性的价值链弯成了一个循环,也正是数据与网络效应护城河的来源(它的机制细节在图 4 展开)。其三,**单源纪律**——本图只画"六域如何串成一条生命周期",六域各自内部的图都在各自的图说里,本系统图绝不重画任何一张领域已有的图。看完这张图,你就知道了整个系统的骨架和"每一块在哪";接下来六张图把这条骨架的关键段一段段放大。 - -### 图 2 · 六层技术分层总图〔SVG·架·现 + F〕 - -![图 2 六层技术分层总图](assets/02-六层技术分层总图.svg) - -这张图回答"这套系统在技术上**怎么搭起来、分几层**"——是系统级的权威分层视图。一个用户从浏览器进来,自上而下穿过六层:**接入层**(CDN / Nginx)→ **前端应用**(studio + admin)→ **网关层**(Spring Cloud Gateway,MVP 下软转发)→ **业务服务层**(13 个游戏业务模块,jar 聚合进 Huijing 单体)→ **基础设施层**(Huijing 原生的 system / infra / bpm,开箱即用)与并列的 **AI 生成层**(SAA 编排 → new-api → 便宜 LLM → 九门,配 LittleJS 引擎)→ 最底的**中间件层**(MySQL / Redis / MinIO);整套之外,一条**旁路可观测层**横向监控。 - -这张图最该让人抓住的,是它那条贯穿始终的设计取向:**先简后扩、能复用就不自研**。后端复用 Huijing 现成的 60% 后台能力、生成不自研大模型而接通用模型、引擎不自研而用成熟的 LittleJS——正是这条取向让系统能用很小的团队和很低的成本跑起来。同时有两处状态边界必须按标注读、绝不能误当现行已建:**其一,中间件层的 Nacos / RocketMQ 是框架自带、MVP 未部署**(图上虚线 + 「future」),所以凡涉及"异步 MQ""服务注册发现"的设计,在 MVP 阶段都按"进程内调用 / 本地配置"折算;**其二,整条旁路可观测层整体是"接"(待接线)**——唯一已就位的实心块是 SAA 节点 observation,其余 OTel 采集、Prometheus、Grafana、夜莺、admin 观测入口全部待接,落地全貌见运维图说图 6。这张图给的是空间感:看"某块技术落在哪一层、它现行还是 future"用它;看某一层内部的细节(13 模块怎么聚类、生成层怎么编排)去对应领域图说。 - -### 图 3 · 现行 SAA 廉价线 ‖ 远期 tier2 自治轨 边界〔SVG·架·现 + F〕 - -![图 3 现行 SAA 廉价线 ‖ 远期 tier2 自治轨 边界](assets/03-两条生成轨边界.svg) - -这张图是看清"生成引擎现行 vs 远期"的**总闸**,也是本总图叙最该守状态纪律的一张。绘境AI 有两条生成轨:**左轨是现行的 SAA 廉价线**——已实测合入主干,承载超休闲档(打砖块 / 合成 / 挂机 / 答题 / 网格点选),一条流水线从"创作者一句话"经 SAA 裸图编排(Java·16 节点)、new-api 网关、便宜 LLM、harness 九门兜底,到 emit 出包,用 LittleJS 当引擎、产物是一份可维护的 `src/` 源项目;**右轨是远期的 tier2 富游戏自治轨**——待 0 号 spike、未落代码,承载 premium 富游戏(经营 / 合成 / 多系统富交互),走的是与左轨正交的另一套范式:AgentScope(Python)的自治 ReAct agent、作为一个独立 service 跑、用 Phaser / Pixi 全无头引擎、靠三层校验当确定性地板加人工终审兜底,产物是 Phaser 的 `src/` 源工程。两轨**只经公共件交汇**:计费平面、送审 + feed、验收基线 + 执行沙箱、控制 / 管理面治理层。两轨**远期有可能收敛为一条**——把左轨的 SAA + LittleJS 也并入 AgentScope 自治范式(登记为未来方向,当前不做)。 - -读这张图,**最要紧的是把左轨的实心和右轨的紫色虚线分清**:左轨是"现行已建",右轨是"远期·待 spike",图上用虚线框 + 「远期·待 spike」标死,绝不能因为画出来了就误以为 tier2 已经在跑。还有三层意思必须读到。其一,**左轨的现行真实状态要诚实说清**:骨架立住、控制流确定、九门很硬已合入主干,但生成质量尚未稳定到 80% 门(便宜模型天花板,实测约 60%)、一部分策略被焊进机制代码;范式侧的终态产物定为可维护的 `src/` 源项目(改源不改包),现行 generate 产出的 gameDefinition JSON 只是通往它的中间脚手架、产线正把它迁成直接生成 `src/`(仍在落地,以生成引擎子树最新裁定为准);默认产线切换交 W-G1 把质量做到 80%(dispatcher flag 已存在,一行 flip)。其二,**为什么两轨不合并**:左轨声明式有向图与右轨命令式循环是两种正交范式,硬塞就成项目明令反对的"缝合设计",而且对卡 80% 门,图的确定性本就比口头督促更稳。其三,**它们的唯一交汇面是公共件**——tier2 换的是"游戏内容怎么造出来",换不掉"造出来之后怎么被关进笼子跑、怎么计费、怎么送审发行"。生成引擎的深度(SAA 16 节点拓扑、加节点法、救场阶梯、引擎运行时、tier2 详设)在生成引擎子树,本图只画两轨硬边界。 - -### 图 4 · 跨域数据流(越用越聪明)〔SVG·流·现〕 - -![图 4 跨域数据流(越用越聪明)](assets/04-跨域数据飞轮.svg) - -这张图把图 1 那条"回流大虚线"放大,回答"平台**靠什么越用越聪明**"。它是一条数据飞轮回路:玩家在 feed 玩、互动(①)→ 游戏内 SDK 埋点上报(②)→ telemetry 入库聚合(③,逐事件 uk_event_id 幂等防重)→ 算出 0–100 的质量分(④)→ 进推荐打分公式与 Redis 候选集(⑤)→ feed 重排下发(⑥)→ 回到①,玩家刷到更好的游戏。这条回路转一圈,好游戏自然浮上来、差游戏沉下去,数据回流持续校准推荐。 - -这张图最该让人看清的有三点。其一,**error_rate 是唯一的"硬"降权**——技术上跑不动的游戏直接沉底,这条把"好不好玩"之前先卡住"能不能玩",是可玩性底线。其二,**这条回路就是护城河第一层**——它是数据壁垒加网络效应的产品载体:真实流量积累出来的质量信号无法购买,飞轮转起后追赶成本指数级增长;竞品做生成工具止步于出包,没有这条回流就不"越用越聪明"。其三,**有两处现状要诚实标**:候选集当前可直查 MySQL、Redis 是增长期形态,游标分页目前是桩(永远返回第一页)——公式和飞轮是设计真相,落地仍是增长期的事;而飞轮的第二半(收益 / 留存数据回流成训练语料、反哺生成质量)是远期·待建,现行只覆盖了生成侧自增强这一半,图上用虚线把它和现行回路分开。这张图与图 1 是一对:图 1 点出"有这么一条回流",图 4 把回流的每一站和它的现状画清。 - -### 图 5 · 失败 / 降级 / 补偿路径全景〔SVG·流·现〕 - -![图 5 失败 / 降级 / 补偿路径全景](assets/05-失败降级补偿全景.svg) - -这张图是**据图做评审最容易发现"缝"的一张**。它横切三类不可控调用——外部模型(链路 A)、异步任务 / 跨表(链路 B)、支付 / 打款(链路 C)——把"超时 → 失败 → 重试 → 幂等 → 补偿 → 兜底"这条可靠性主线集中画出来。链路 A 给生成调便宜 LLM 的全路径:超时 120s、状态机显式建 FAILED / TIMED_OUT 边、重试加 repair→escalate 救场阶梯、idempotency_key 去重、giveup 留证据,并标出 2026-06 已真实发生的"二厂系通道全废 → new-api 多通道兜底"。链路 B 给异步与跨表:最终一致 + 补偿(非分布式事务)、唯一一处真原子发布(project 把合规裁决 + 出包 + 入流串成事务)、补偿 job 兜底、幂等四范式。链路 C 给钱的事:账户恒等式永远成立、"发起 ≠ 终态"、双路驱动 + CAS、对账锚点链。 - -这张图最该让人记住的,是底部那条**关键反差**:打款的降级口径与生成 / 广告**正好相反**——广告渠道缺失降级 mock 可接受(少算收入),但打款渠道缺失若降级 mock 就是吞真钱,必须 fail-fast、绝不发假钱;审核降级则一律朝保守(阿里云超时降到 review、绝不降到 pass)。图底那张"据图 review 的六条检查清单"是这张系统级图存在的意义:每个外部 / 异步 / 资金动作是否都有幂等键、失败是否在状态机里显式建边、补偿 job 是否扫得到、降级方向对不对、抽象是否做对(mock ↔ 真实现可零改业务码切换)、对账锚点链是否端到端串得起来——凡有一格答不上,就是一处可靠性裂缝,据图就能定位,不必逐文件翻。图上也诚实标了打款 mock 桩、订阅自助购买被支付闸门阻塞这些现状缺口。 - -### 图 6 · 端到端鉴权信任边界总图〔SVG·架·现〕 - -![图 6 端到端鉴权信任边界总图](assets/06-鉴权信任边界总图.svg) - -这张图回答"权限在系统的**哪一层、挡什么**",是系统级的信任边界视图。一个请求从前端经网关到业务,三层各管一段:**① 前端**的路由守卫只改善体验(anon_id 让 Feed / Play / Share 匿名可达、即刷即玩不挡门),但它挡不住直连 API——前端守卫形同虚设,任何"前端藏了按钮就安全"的想法都击穿信任;**② 网关**在 MVP 单体下只做软转发(剥除外部伪造的 login-user 头、有 token 才注入可信头、无 token 也放行),它本身没有路径级 RBAC,所谓"网关双重校验"是微服务拆分后的未来态;**③ 业务服务 huijing-server 是权限的唯一真强制点**——TokenAuthenticationFilter 校验 token 的 userType 与 URL 前缀一致,然后 B 端走声明式 RBAC(@PreAuthorize + 角色菜单交集)、C 端走 Service 归属隔离(eq 谓词把当前用户钉进 WHERE),再叠一层创作白名单(A2 内测准入)和匿名读三纪律。 - -这张图与后端图说图 7 是**互补、不重复**的:后端图 7 是模块类图(讲两套权限模型在代码里的类与方法),本图是**系统边界**(讲前端→网关→业务三层各挡什么、职责怎么切分)。它最该让人看清的,是那条贯穿全图的铁律——**权限只在后端可信边界强制,前端拦截一律不算数**;以及职责切分的本质:网关做"身份可信化"(把不可信外部头换成可信内部头),但绝不替代服务侧的权限判定,两者是接力、不是冗余。anon_id 让玩家免登消费,但创作 / 写域必须越过后端这道墙。图上还诚实标了一处现状:控制器注释里"网关 + 服务端双重校验"是未来态,当前单体下网关甚至不在请求路径上,权限强制 100% 在服务侧单点;mock 后门是 staging 现状、生产必须关死的红线。 - -### 图 7 · 角色 × 端全景〔Mermaid·讲·现〕 - -这张图回答"**谁在哪个端做什么**"。绘境AI 把"用户"收敛成六种角色,分布在两个前端(C 端 studio / B 端 admin)上;最容易混的一对是"经营"和"运营"——**经营 = 创作者看自己作品的数据**(留存 / 完玩 / 广告转化),**运营 = 平台管理员**(审核 / 精选 / 封禁 / 经营看板),两者在需求清单里分属不同的域(OPS vs OPN),混了就会把功能归错端。 - -```mermaid -flowchart TB - subgraph C端["C 端 · game-studio(Vue3 + Vant)"] - direction LR - CR["创作者
一句话生成 / 预览迭代
发布 / 看收益(经营)"]:::role - PL["玩家
刷 feed 即点即玩
点赞分享 / 发起同款"]:::role - CB["B 端客户(询单侧)
提需求 / 看 demo / 验收"]:::roleb - end - subgraph B端["B 端 · game-admin(Vue3 + Element Plus)"] - direction LR - OPN["运营(平台管理员)
内容审核 / 精选推荐
封禁处置 / 经营看板"]:::roled - ADM["管理员
用户管理 / 权限角色
合规处置 / 数据看板"]:::roled - BD["商务 / BD
B 端定制单据流转
报价 / 进度 / 交付"]:::roled - end - COM["通用(任意端可触)
账号 / 登录 / 消息通知 / 政策页 / 申诉"]:::common - - CR -->|产出可玩游戏| PL - PL -.->|发起同款 → 变创作者(P-FED-12)| CR - CB -.->|线索 → 单据| BD - OPN -.->|审核 / 降权联动| PL - ADM -.->|白名单置位 set-creator| CR - C端 --- COM - B端 --- COM - - classDef role fill:#eff6ff,stroke:#2563eb,stroke-width:2px; - classDef roleb fill:#f5f3ff,stroke:#7c3aed,stroke-width:1.6px; - classDef roled fill:#fefce8,stroke:#ca8a04,stroke-width:2px; - classDef common fill:#f1f5f9,stroke:#475569,stroke-width:1.6px; -``` - -这张图最该让人看出的,是**两条跨角色的转化回路**:一条是玩家"发起同款"跳转工作坊变成创作者(P-FED-12)——它把"玩游戏的人"转化成"做游戏的人",是网络效应护城河的产品载体;另一条是 B 端客户的询单经商务 / BD 流转成定制单据。还有一处系统级衔接:运营对内容的审核 / 降权处置会联动 feed 曝光(影响玩家看到什么),管理员对创作者的白名单置位(set-creator)决定一个 C 端用户能不能创作——这两条"B 端动作影响 C 端体验"的链路,正是图 6 那条"权限在后端强制"与图 4 那条"feed 重排"的人侧投影。这张图没有现行 / 远期的灰度,它是当前的角色与端的真相;每个角色的完整旅程(创作者旅程、玩家旅程)在产品图说,每个端的视图地图在前端图说。 - ---- - -## 3. 领域导航(本 apex 的心脏) - -读完第 2 章,你已经有了全局轮廓。这一章把你领到七个领域的门口:每一段告诉你**这个域的图在讲什么故事、最该据图 review 的关键设计缝是哪条、链接到哪份图说**。读完这七段,你就知道了全局每一块在哪、各自的命门是什么。 - -### ① 产品域 → [产品图说](产品/产品图说.md) - -**讲什么故事**:产品域只回答一件事——这个产品对用户提供什么(WHAT),不碰"用什么技术实现"。它的九张图从最顶层的"做得出 → 有人玩 → 赚到钱"闭环讲起,展开成 19 个产品域归拢的五大块(创作 / 玩家 / 变现 / 成长 / 平台),讲清 155 条需求怎样收敛成 55 条 P0(MVP 验收口径)、产品需求与技术模块之间那张唯一的多对多映射矩阵(RTM),再落到创作者与玩家两条旅程,最后到商业定位与护城河。读完这个域,你知道"这个产品到底要给谁、给什么"。 - -**最该据图 review 的设计缝**:产品域是**定义层、不是建设层**——它描述"产品要提供什么",不是"代码建到了哪一步"。所以评审时最该盯两处诚实纪律:**第一,产品定义 ≠ 建设进度**——55 条 P0 是验收口径,不等于今天都真端到端,真实完成度以需求模块映射的现状快照与 MVP 进度总账为准;**第二,护城河话术的红线是"不假装有墙"**(图 9)——四层护城河是要在窗口期点燃的、不是已有的,任何把飞轮 / 网络效应画成"已有的墙"都击穿信任;以及第二条生成轨(tier2)在 19 域里是 future,未落代码、尚无正式 P-id,绝不能画成 MVP 范围。 - -### ② 架构域 → [架构图说](架构/架构图说.md)(+ 生成引擎子树,见下文⑦) - -**讲什么故事**:架构域回答 HOW——这套系统用什么技术、怎么搭。它的十二张图讲清六层分层、那四项最关键选型为什么选 A 不选 B、13 个后端模块各管什么又怎么单向依赖、一条"一句话生成游戏"的请求怎么异步跑通、生成任务状态机怎么管超时 / 失败 / 重试 / 取消、游戏怎么安全跑在玩家面前、推荐打分公式、工程治理(幂等 / SLO / 可观测)、以及那张**最该据图评审的契约族总览**。读完这个域,你知道"这套架构是不是想要的那个、哪里有设计缝"。 - -**最该据图 review 的设计缝**:架构域评审的命门在**图 11 契约族总览(声明 vs 现实)**——它不只画理想的"应该怎样",更如实标出五处已经漂掉的地方:DB 镜像已漂移(contracts 只含 18 个迁移、执行副本有 25 个)、CI 防漂移门缺位(文档写了四道闸、代码里全仓唯一 CI 永不触发)、Dify #6 契约已废、"第 9 契约"五套编号相撞、agent-loop / templates 在 README 目录图漏登。这五处是"声明与代码不符"的待决策位,据图就能揪。另两处状态纪律:图 2 / 图 3 里被推翻的 Dify / OpenGame / 自研壳是**决策史**(理解"为什么现在是这样",别误当现行),图 10 的四路可观测落地态是**接**(设计意图真、后端真接上大半还没做)。 - -### ③ 后端域 → [后端图说](后端/后端图说.md) - -**讲什么故事**:后端域回答服务实现视角的几件事——13 个业务模块在工程上怎么摆(两段式 -api / -server、包名自动套端前缀、错误码分段)、全平台数据长什么样、怎么跨域连、以及"谁能做什么"这条鉴权约束在代码里如何强制。它的八张图里,图 5 是**全仓唯一的 ER 权威**(据 Flyway V1–V25 真实落库的约 40 张表画出五条业务链),图 6 是五条业务链路的跨模块调用,图 7 是鉴权双模型。读完这个域,你知道"这套后端结构是不是想要的那个"。 - -**最该据图 review 的设计缝**:后端域八张图整体都是"现"(已落代码、已 e2e 验证),但现行结构里仍有几处桩与缝必须照实看清——图 5 的 **contracts DB 镜像已漂移**(只读 contracts 会漏掉 aigc 的 level/trace 列、source_project 的并发幂等唯一键、feed 的 exposure_limit)、图 6 的 **compliance 检测原子恒返回 pass / feed 游标分页是桩 / 真广告联盟 SDK 是桩**(这些缺口的共同特点是逻辑已建、卡在不可压缩的日历闸门或 W-G1 质量门)、图 7 的**网关双重校验是未来态而非现状**。还有一条贯穿全域的设计意图最该让评审看出:**全图无物理外键**(归属靠 Service 可信边界的 eq 谓词、不靠 DB 约束),以及源(game_source_project)与产物(game_version)两个存储面解耦——"改源不改包"范式在数据层的落点。 - -### ④ 前端域 → [前端图说](前端/前端图说.md) - -**讲什么故事**:前端域回答三件事——绘境AI 有哪两个前端各服务谁、它们靠一套什么设计体系撑起一致体验(token 两层 + 组件库四层、三条数据流、三大契约、主题切换防 FOUC)、以及生成出的游戏怎么在前端被真正地玩起来(真实试玩宿主)。它的九张图里八张是"现"(设计体系已全量收口合入主干),图 2 的 token 两层 + 组件库四层是整个前端域最核心的一张。读完这个域,你知道"这套前端方案是不是想要的那个"。 - -**最该据图 review 的设计缝**:前端域评审的命门在**图 8 真实试玩宿主**——它是 studio 比 demo 多出来的护城河命门,但必须看清"机制建成 ≠ 合规收口":iframe 的 CSP、sandbox 属性、sha256 校验这套机制都已建成真跑通过,但整套现在跑在**同源过渡态**(同源 srcdoc + allow-same-origin),还有三处真实债——CSP 文档口径与代码对不上、sandbox 属性三处不一致、后端下发的 sandboxAttr 前端根本没接线,收敛方向(迁独立 usercontent 子域、CSP 改 HTTP 响应头下发、sandboxAttr 真接线)必须一起做才闭合,权威细节在沙箱图说与产物执行沙箱.md §8。另一处状态边界:图 9 的 **tier2 富游戏前端承载**整图是"建"(归属已定但代码未建),钉归属、不预先设计,绝不画成已建。 - -### ⑤ 产物执行沙箱(架构域子主题)→ [产物执行沙箱图说](架构/产物执行沙箱图说.md) - -**讲什么故事**:沙箱图说回答一个被独立成档的硬问题——**一款 AI 生成的不可信游戏代码,怎么在玩家浏览器里被隔离着安全跑起来**。生成主线已从"禁代码、填参数"迁到"让 agent 写码",注入面就是模型现写的 JavaScript 本体,平台必须把每一款生成游戏当成有恶意、有 bug 的不可信输入。它的七张图讲清三层封堵(build 段静态扫描 / iframe + CSP / sandbox 属性)、取包 sha256 校验注入时序、postMessage 双向桥双校验、SDK 受控能力面、两条正交边界、CSP / 同源红线 / 产物消毒、三容器加载 / 失败 / 销毁态机。读完这个域,你知道"这套隔离方案是不是想要的那个"。 - -**最该据图 review 的设计缝**:沙箱域评审最该守的纪律点是"**别把机制已建当成已经安全**"——iframe、桥、双校验、sha256 这套都真跑通过了,但它现在跑在同源过渡态,HJ-AUDIT-001 那条"独立源就绪前禁用 allow-same-origin"的红线在代码里并未强制,origin 白名单还含字符串 'null'。还有两条最该看清的概念边界:**两条信任边界正交、别混**(沙箱边界 = iframe 壳 vs 宿主、受控面 = 游戏代码 vs 引擎,受控面在沙箱里面、沙箱在受控面外面,破一条另一条仍兜);以及 **'unsafe-eval' 看着危险但风险被纵深防御承接住了、不是漏洞**(逻辑串先经 build 段静态扫描、再加 connect-src 'none' 无出网、再加 iframe sandbox 隔离);它是现行那套声明式 gameDefinition 运行时(用 `new Function` 编译逻辑串)的产物,而 gameDefinition 只是通往 `src/` 源项目终态的中间脚手架、产线正迁移中,这条 CSP 的收敛口径以沙箱图说/生成引擎子树的最新裁定为准。 - -### ⑥ 运营域 → [运营图说](运营/运营图说.md) - -**讲什么故事**:运营域接在产品域与架构域之后,回答三件相互咬合的事——一款游戏被造出来之后,怎么合规上线、怎么把钱赚回来、怎么发行到各渠道。它的十一张图讲清 12+1 合规闸门的日历临界路径、合规死锁三环 + 双层定性破解、分账公式钱流(平台恒留 0.2N)、两平面两钱包三道硬边界、三线变现排序、四层护城河、发行双路线、一壳多游 L1 精选整包、变现端到端钱流地图、审核台双层审核。读完这个域,你知道"这套上线 / 变现 / 发行方案是不是想要的那个"。 - -**最该据图 review 的设计缝**:运营域十一张图整体都是"现"(已拍板的现行设计逻辑),但它的特殊之处在**元素级**——同一张图里常常一半是"已建真跑"、一半是"被合规日历闸门或外部资质阻塞、尚未接线 / mock / 留待后续波次"。评审最该盯的是这条纪律:**绝不能因为整图是现行设计,就把图里那些被阻塞的部分画成已建好**——变现侧的打款 mock 桩(降级即吞真钱、必须 fail-fast)、消费钱包未接、自助购买订阅被支付闸门阻塞;审核台的检测原子全是桩恒返回 pass(框架在、判定空、处置半通);渠道侧的备案锁(把"一壳多游"从灰区可行降级为高风险待律所裁)与引擎竞标 P1 段暂停。还有一处全局阻塞:8 项 C 端法定 P0 的换血边界要等律所对两核心问给出书面意见,这是仍卡着全局的最大未决项。 - -### ⑦ 生成引擎子树(架构域旗舰)→ [生成引擎子树](架构/生成引擎/README.md) - -**讲什么故事**:生成引擎是绘境AI 护城河的关键路径,内容最厚、单独成一棵子树。它回答"一句话怎么变成一款可上线的游戏"这条技术主线:现行那台被刻意设计成可靠的、固定相位的生成机器(便宜模型驱动 + 确定性图编排约束控制流 + 九门 harness 兜底),以及它要往哪走的演进路线、贯穿全程的范式原则(游戏是长生命周期源项目、LLM 是它的工作室)。本总图叙的图 3 已给出"两条生成轨现行 vs 远期"的总闸轮廓,这棵子树是它的深度展开。 - -**最该据图 review 的设计缝**:生成引擎子树评审最该带着一条**范式定性**读——这套"便宜模型 + 受限 schema + 九门验收"是一条**为超休闲轻游戏(Tier0)量身打造的可靠产线,而不是一个通用生成范式**;它最大的风险不在工程层,而在被当成通用范式去对标 demo 里那一档富交互游戏(那一档结构性地够不着,必须显式分层、复杂品类另开一轨)。子树内现行结论可能随产线化 plan(把现行的 gameDefinition JSON 中间脚手架迁成直接生成 `src/` 源项目)的推进变化,以最新裁定为准。**跨 session 协调红线:生成引擎子树正被另一条 session 在飞建设,本总图叙对它只链接、不画其内部**——它的两簇(现行 SAA 廉价线 / 远期 tier2 自治轨)的逐张图、SAA 16 节点拓扑、九门逐门、tier2 详设都在子树内,本文不重复承载。 - ---- - -## 4. 按角色读:推荐下钻路径 - -读完第 2 章系统定向图、再按你的角色选第 3 章一两个领域下钻,是最省时的读法。下面给四类角色各推荐一条 2–3 份图说的路径。 - -- **产品 / 运营 / 投资人**:先读本文第 2 章图 1(六域生命周期)+ 图 4(数据飞轮)+ 图 7(角色×端),建立"这是个什么系统、靠什么越用越聪明"的轮廓;再下钻 [产品图说](产品/产品图说.md)(产品定义与护城河)→ [运营图说](运营/运营图说.md)(合规上线 / 变现 / 发行,尤其图 9 备案锁、图 10 钱流、图 11 审核台)。命门:护城河"不假装有墙"、变现被日历闸门阻塞的部分别当已建。 - -- **工程师(新加入)**:先读本文图 2(六层分层)+ 图 3(两条生成轨边界)+ 图 6(鉴权边界);再下钻 [架构图说](架构/架构图说.md)(尤其图 11 契约现状)→ 你负责的模块在 [后端图说](后端/后端图说.md)(图 5 ER + 图 6 五条业务链)→ 若做生成主线直接进 [生成引擎子树](架构/生成引擎/README.md)。命门:契约 DB 镜像漂移、网关双校验是未来态、桩与真要分清。 - -- **前端工程师**:先读本文图 6(鉴权信任边界,前端那一层挡不住直连)+ 图 7(角色×端);再下钻 [前端图说](前端/前端图说.md)(图 2 token 两层 + 组件库四层、图 8 真实试玩宿主)→ 跨到 [产物执行沙箱图说](架构/产物执行沙箱图说.md)(图 8 宿主侧的安全隔离纵深)。命门:试玩宿主"机制建成 ≠ 合规收口"、同源过渡态三债一起收。 - -- **安全 / 可靠性评审**:先读本文图 5(失败 / 降级 / 补偿全景,据图 review 六条检查清单)+ 图 6(鉴权信任边界);再下钻 [产物执行沙箱图说](架构/产物执行沙箱图说.md)(三层封堵 + 两条正交边界)→ [后端图说](后端/后端图说.md)图 7(鉴权双模型)→ [运营图说](运营/运营图说.md)图 10(打款 fail-fast)。命门:降级方向(可降 mock vs 必须 fail-fast)、权限只在后端可信边界强制、同源红线过渡态。 - ---- - -## 5. 图清单与状态表 - -**系统级 7 图**(本总图叙内联,跨域、无单域拥有): - -| # | 图名 | 家族 | 形式 | 状态 | 覆盖内容 | -|---|---|---|---|---|---| -| 1 | 六域生命周期全景 | 架 | SVG新 | 现 | 产品→架构→后端/前端→运营→运维 六域串成产品全生命周期 + 数据回流弯成环 + 单源纪律 | -| 2 | 六层技术分层总图 | 架 | SVG新 | 现 + F | 接入→网关→业务→基座→AI→中间件 + 旁路可观测(接);先简后扩取向;Nacos/RocketMQ=future | -| 3 | 现行 SAA 廉价线 ‖ 远期 tier2 自治轨 边界 | 架 | SVG新 | 现 + F | 两条生成轨硬边界:左=现行 SAA+LittleJS 廉价线(终态产 src 源项目·gameDefinition JSON 为中间脚手架迁移中) vs 右=远期 tier2 AgentScope+Phaser 自治轨(待 spike);只经公共件交汇;远期可能收敛为一条;深度链子树 | -| 4 | 跨域数据流(越用越聪明) | 流 | SVG新 | 现 | 埋点→telemetry 聚合→质量分→Redis 候选集→feed 重排→行为回流 飞轮;error_rate 硬降权;收益语料远期 | -| 5 | 失败/降级/补偿路径全景 | 流 | SVG新 | 现 | 横切外部模型/异步/支付:超时→失败→重试→幂等→补偿→兜底;打款 fail-fast 反差;六条 review 清单 | -| 6 | 端到端鉴权信任边界总图 | 架 | SVG新 | 现 | 前端→网关→业务 各层挡什么;anon_id 衔接;网关+业务双校验职责切分(双校验=未来态) | -| 7 | 角色 × 端全景 | 讲 | Mer新 | 现 | 创作者/玩家/经营/运营/B 端/通用 各在哪个端做什么;经营≠运营;两条跨角色转化回路 | - -> **状态分布**:现 ×5、现 + F ×2(图 2 含 future-state 中间件与"接"可观测、图 3 含远期 tier2 轨)。系统级图与领域图**互补不重复**:本 7 图都是跨域才画得出、无单域拥有的图;领域已画的图(产品闭环、13 模块依赖、生成请求时序、全平台 ER=后端图 5、契约现状=架构图 11、护城河/三线变现=运营、部署/观测=运维、生成引擎深度=生成引擎子树)一律只在散文里链接、不重画。**远期/已废标注**:tier2 轨(图 3,= AgentScope+Phaser)= 远期·待 spike 虚线、远期可能与左轨收敛为一条;Nacos/RocketMQ(图 2)= future-state 未部署;观测(图 2)= 接;收益回流语料(图 4)= 远期·待建;打款 mock(图 5)= 现状桩;网关双校验(图 6)= 未来态;Dify/OpenGame/玩法填参模板/15KB = 已废;gameDefinition JSON 数据壳 = 中间脚手架·正迁移成直接生成 `src/` 源项目;均不画成现行终态。 - -**指向七领域图说的索引**(下钻入口): - -| 领域 | 图说 | 一句话 + 命门 | -|---|---|---| -| ① 产品 | [产品图说](产品/产品图说.md) | 产品对用户提供什么(WHAT);命门=产品定义 ≠ 建设进度、护城河"不假装有墙" | -| ② 架构 | [架构图说](架构/架构图说.md) | 系统用什么技术怎么搭(HOW);命门=图 11 契约五处现实缝、决策史别当现行 | -| ③ 后端 | [后端图说](后端/后端图说.md) | 13 模块工程落地 + 数据 + 鉴权;命门=DB 镜像漂移、桩与真分清、全图无物理外键 | -| ④ 前端 | [前端图说](前端/前端图说.md) | 两前端设计体系 + 真实试玩;命门=试玩宿主机制建成 ≠ 合规收口、tier2 承载待建 | -| ⑤ 产物执行沙箱 | [产物执行沙箱图说](架构/产物执行沙箱图说.md) | 不可信代码怎么隔离着安全跑;命门=别把机制已建当已安全、两条正交边界别混 | -| ⑥ 运营 | [运营图说](运营/运营图说.md) | 合规上线/变现/发行;命门=元素级被阻塞的部分别画成已建、8 项法定 P0 待律所 | -| ⑦ 生成引擎子树 | [生成引擎子树](架构/生成引擎/README.md) | 一句话怎么变成可上线游戏;命门=Tier0 可靠产线非通用范式、子树在飞建设只链不画 | - -> **防漂移门**:本图说 frontmatter 记九份主要源档的 commit hash(architecture/README @ ec7fda2c、架构/README @ 38357c3d、产品/README @ 366a44b4、运营/README @ 3e71715a、运维/README @ 15b707fd、后端/数据模型 @ 38357c3d、后端/鉴权与权限 @ 38357c3d、架构/契约总览 @ 38357c3d、生成引擎/README @ 37f5eddb),源档变更即比对,hash 对不上则本图说与对应 SVG 标"待复核"。 -> -> **PNG 后续**:7 图中 6 张为 SVG(图 1–6),SVG 入仓后在 mini-desktop 批量转 PNG(6c6g 禁 chrome);1 张 Mermaid(图 7)GitHub 直接渲染、无需转换。 diff --git a/docs/architecture/运维/运维图说.md b/docs/architecture/运维/运维图说.md deleted file mode 100644 index 12cdea57..00000000 --- a/docs/architecture/运维/运维图说.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -date: 2026-06-22 -topic: 运维域图说——把「部署在哪、怎么变成在跑的服务、靠什么持续看见它」画成一套图 + 讲解(架构图集金样板·运维域首件) -status: 现行 + 待接(观测)+ 缓做(k3s)三态并存 · 防漂移门记源档 commit hash -映射源档: - - docs/architecture/运维/README.md @ 15b707fd - - docs/architecture/运维/观测体系.md @ 3e71715a - - docs/architecture/运维/k8s迁移.md @ 15b707fd ---- - -# 运维域图说 - -> **这是什么**:绘境AI 运维域的「看图入口」。运维域回答三件事——这套系统**部署在哪些机器上**、代码**怎么从一行 commit 变成在运行的服务**、靠**什么确认它还活着并且活得够好**。这份图说把这三件事,连同两件「设计已出但还没接上」的事(线上观测体系、k3s 迁移),用一套图加配套讲解画清楚。复杂、跨机的用手绘 SVG 大图,简单、线性的用 Mermaid,已有的图一律单源引用、不复制。 -> -> **给谁看**:负责部署与值守的工程师、做发版的人、排查线上故障的人,以及关心可用性与运维成本的创始人。判断「这套运维方案是不是想要的那个」看这份图就够建立全局轮廓,逐字节的命令仍去 `deploy/` 脚本与 `.agents/skills/staging-ops.md`。 - ---- - -## 0. 阅读约定与同步纪律 - -- **映射的设计档**:本图说不另立设计,只把运维域三份 canonical 设计档画出来——部署链与四机分工出自 [`运维/README.md`](README.md),线上观测体系出自 [`观测体系.md`](观测体系.md),k3s 迁移出自 [`k8s迁移.md`](k8s迁移.md)。frontmatter 里记了这三份的当前 commit hash,作为**防漂移门**:源档一旦变更、hash 对不上,这份图说与对应 SVG 即标「待复核」,由收口脚本比对。 -- **同步纪律**:**设计一变动,本图说与对应 SVG 必须同步更新**,每张 SVG 脚注注明映射源档与状态。已并入 wave 收口清单。 -- **三种状态贯穿全图,必须看清**:运维域的特殊之处是它同时承载「现行已建」「设计已出待接线」「收窄后缓做」三类内容,绝不能把后两类画成已建。图 6(观测)整体是**接**(待接线)、图 8(k3s)整体是**缓**(缓做、排在 W-G1 之后),其余六张是**现**。每张图内部还会用实心块 / 虚线框 + 「待接线」「缓做」小标把单个元素的状态再标一层。 -- **产物形式**:SVG 入仓(GitHub 与编辑器直接渲染矢量),PNG 在 mini-desktop 转(6c6g 禁 chrome、无转换器)。本图说只出 SVG。 - -## 1. 全图通用图例 - -**生产维度徽章**(用小色块 chip + 白字标在每张图相关的维度上,运维域主要关心其中四个): - -- **可靠**(`#16a34a`):隔离验证安全变体、备份 JAR 一键回滚、冒烟门退出码可卡口、观测旁路降级、k8s 共存保护与 rollout undo。 -- **可观测**(`#0ea5e9`):OTel 统一采集、Grafana 三联下钻、夜莺告警必达——这是图 6 的主轴。 -- **安全**(`#dc2626`):push 走 Gitea 中转(scp/rsync 被分类器拦)、Collector 集中脱敏、admin 观测入口权限守门不裸暴露后端。 -- **成本**(`#16a34a`):内网物理机 + 人工部署而非重 CI、不硬凑第二台机器上多节点、缓存命中率哨兵——都是 < ¥5,000/月成本盒子的取舍。 - -(另两个维度 **数据** `#475569`、**伸缩** `#7c3aed` 在 k3s 图上点到:声明式编排与平滑长成多节点 = 伸缩。) - -**复用 / 边界三线语义**: - -- **实线**(`#334155`):现成直接用、同步动作或命令。 -- **虚线**(`#64748b`,`stroke-dasharray="6 4"`):自建 / 解耦 / 回执 / 待接线 / 缓做的目标态。 -- **双线 / 加粗框**:跨域共享的公共件或需要强调的边界。 - -> 运维域图说里,虚线还额外承担一层**状态语义**:凡是「待接线」(观测)或「缓做」(k3s)的元素,一律用虚线框 + 文字小标画出,与「现行已建」的实心块在视觉上一眼区分开。这是本域最该守的纪律点——观测体系和 k3s 迁移都是「设计已出、代码未接」,绝不能因为画得好看就让人误以为已经建好了。 - ---- - -## 2. 图集 - -### 图 1 · 四机分工拓扑〔引·现〕 - -> 单源引用,不复制:四机分工拓扑的 Mermaid 已在 **[运维/README.md §1.1](README.md#11-跑在哪里四台机器各司其职)** 内嵌,是该图的唯一来源。这里只链接、不抄一份进来——同一张图存两处、改一处漏一处,正是这份图集要消灭的漂移面。 - -这张图回答「服务跑在哪里」。绘境AI 在内网阶段刻意不上云托管、不上 Kubernetes,而是用一组通过 Tailscale 连起来的物理机,按角色硬分工:**lili-mac** 是创作者本地工作站,跑开发主会话与快构建,但它会休眠合盖,所以不进 Tailscale 调度、也不承担任何「门」;**mini-desktop** 是权威验收机,staging 全栈、重型构建、浏览器端到端测试都在这里,且与生产同构;**mini-infra** 是共享基建机,Gitea / PostgreSQL / Redis / MinIO / new-api 都在这台,它绝不跑项目 app 或重型构建;**6c6g** 是常驻无人值守机,跑常开的编排、定时任务、git 操作,但禁跑重活(重型前端构建会 OOM)。 - -读这张图要抓住一条最硬的边界:**Mac 是 ARM 架构,产出的 JAR 与前端产物架构中立可用,但 Docker 镜像与冒烟门必须在 x86 的 mini-desktop 出**,否则镜像架构与生产不同构,等于半假的门。这条边界在后面图 8(k3s 迁移)里会再次出现并延伸——到了 k8s 时代,「进集群的镜像必须 mini-desktop x86 出」依然成立。这张图没有现行/远期的灰度,它就是当前真相;唯一的「设计缝」其实在别处——README §1.1 自己点明 mini-infra 上跑的关系型库是 PostgreSQL,而 mini-desktop 上是隔离的 staging MySQL,两者不是一回事,端点口径以凭据 SoT《内网凭据与端点.md》为准,别混。 - -### 图 2 · 部署链:push → pull → 重建 → 验门〔SVG新·时序·现〕 - -![图 2 部署链 push→pull→重建→验门](assets/02-部署链时序.svg) - -这张时序图回答「代码怎么变成在跑的服务」。它要先破除一个误解:开发团队版文档里那套「push 触发 CI、自动构建镜像、滚动更新」是**目标态蓝图,与现实系统性脱节**(那份文档顶部自带审计横幅说明这点)。现行的部署没有 docker-compose、也没有自建 CI 流水线,而是**人工经标准序在 mini-desktop 上完成**——图里把它画成四条泳道之间的一串动作,并在底部用一个虚线框单独把这套蓝图标成「现实未建·勿当现状」,免得有人照着蓝图去找不存在的 CI。 - -核心三步在图上从左到右展开。**第一步同步代码**:代码经 `git push` 推到 Aliyun Gitea(默认远程,SSH `:2222`),mini-desktop 再从同一个 Gitea 以匿名 HTTP `:3000` 拉取(注意是 HTTP 只读端口、不是 SSH)。这里画出了两条已经踩过的坑:直接 `scp / rsync` 源码树会被分类器拦下,所以必须走 Gitea 中转;大资产 push 在途时再发 push 会撞 Gitea 的 ref 锁,所以同仓 push 要串行。**第二步重建**:在 mini-desktop 上 `git reset --hard` 到目标分支、`mvn clean install` 出新的 fat JAR,收口前**以字节码实证**新 JAR 确实含本次改动(曾因「构建源 ≠ 运行的 JAR」翻过车,图上单画一格强调)。**第三步验门**:重启后验 health 200、四模板就绪、Flyway up-to-date,再跑冒烟门(即图 4)。 - -整张图最该让人看出的,是中间那个橙色虚线框——**隔离验证安全变体**。高风险或整机变更不直接动 live,而是先用新 JAR 在隔离端口 `:48090` 起一个实例验全,**live 的 `:48080` 全程不动**,验全过才切;任一步失败就用 `/tmp` 里的备份 JAR 把 `:48080` 恢复回去。这套「验全才切、失败即回」的安全变体是运维域可靠性的支点,后面图 8 把它原样平移到了 k8s 的隔离端口切换。图底还有一条红线值得记住:**API 全绿 ≠ UI 通**,编排器旁路会掩盖 UI 缺陷,用户可见波次收口前必须另做一次真浏览器 UI 走查。 - -### 图 3 · 隔离验证安全变体〔Mer新·流程·现〕 - -这张图把图 2 里那个橙框单独放大,讲清「为什么验全才切、失败怎么退」。它是一条状态/分支流程:新 JAR 不直接顶替 live,而是先在隔离端口 `:48090` 起一个实例,跑完整验证(health、四模板、Flyway、冒烟门 12 项);只有**全部通过**才停旧实例、释放端口、把新实例切到 `:48080`;**任一步失败,立即用 `/tmp` 备份 JAR 把 `:48080` 恢复**,回到改动前的形态,全程 live 不受影响。这条流程让一次高风险发版的下行风险被牢牢兜住——最坏情况也只是「白验一次、live 没动」,而不是「切了一半、服务挂了、退不回去」。 - -```mermaid -flowchart TB - START["新 JAR 已构建
(字节码实证含本次改动)"] --> ISO["在隔离端口 :48090
起一个新实例"] - LIVE["live 后端 :48080
全程不动 · 继续服务"]:::live - ISO --> V{"在 :48090 验全?
health 200 · 四模板就绪
Flyway up-to-date · 冒烟门 12 项"} - V -- "全过" --> SWITCH["停旧实例 → 释放端口
把新实例切到 :48080"] - V -- "任一失败" --> ROLLBACK["/tmp 备份 JAR
把 :48080 恢复回去
(5 分钟内 · live 本就没动)"]:::danger - SWITCH --> REVERIFY["切后复验冒烟门
+ 真 UI 走查"] - SWITCH -.->|稳定观察一两天后| CLEAN["再删 /tmp 备份 JAR"] - ROLLBACK --> BACK["回到改动前形态
排查后重来"] - - classDef live fill:#eff6ff,stroke:#2563eb,stroke-width:2px; - classDef danger fill:#fee2e2,stroke:#dc2626,stroke-width:2px; -``` - -这张图没有现行/远期边界,它就是现行高风险发版的标准动作;它和图 2 的关系是「图 2 给全链路、图 3 把其中最关键的安全机制放大」。要注意的设计缝只有一个:这套变体是**人工经标准序**执行的,不是声明式的——这正是图 8(k3s)想用 `kubectl rollout undo` 改进的地方,但在 k8s 落地前、乃至落地后的过渡期,这条人工兜底路始终保留。 - -### 图 4 · 冒烟门 12(+1)项〔SVG新·流程·现〕 - -![图 4 冒烟门 12+1 项](assets/04-冒烟门12项.svg) - -这张图回答「靠什么确认它健康」。每次 staging 部署之后跑一遍 `deploy/smoke-test.sh`——它是「部署后是否健康」的**就绪检查**(秒级、以读为主、可反复跑),逐项 PASS / FAIL,末尾给总判与退出码(0 = 全过,1 = 有 FAIL),所以能直接挂到部署脚本里当卡口。图上把 12 个默认项按「基础设施 / 鉴权 + 五条端到端闭环链路」分组画出来,让人一眼看出冒烟门验的不是零散接口,而是**五条业务链的关键 API 是否都在岗**:① 创作→生成→预览、② 发布→审核→游戏流、③ 试玩→互动→分享、④ 广告→收益→钱包、⑤ 数据回路(遥测→质量分→feed 重排)。 - -读这张图要抓三个细节。其一,**12 项里唯一的写操作**是建一条无副作用的草稿(`POST /app-api/studio/draft`,status 0、不发布、无下游副作用),用于验证创作入口 + 鉴权 + gameId 分配在岗;其余全是读。其二,`--deep` 才加的第 13 项(图上用橙色虚线框单独标「按需」)走真实写路径,触发一次生成入队 + 遥测落库,默认不跑、只验入队 `200/code=0`,不等生成完成。其三,图上用红框钉死了一个反复出现的坑:**feed 列表项的 `packageUrl` 字段恒为 null**,真实取游戏包要走 `GET /app-api/runtime/package/{versionId}`,别拿 `packageUrl` 当取包入口。 - -这张图的「设计缝」画在右下角那个虚线框里:冒烟门是「部署那一刻的就绪点检」,它**不是**深度 e2e(深度 e2e 由 agent-loop 编排器批跑,职责不重叠),也**还没有**常态监控来补它的另一半——这正好衔接到图 6 的观测体系。衔接点写在图上:冒烟门那 12 项的结果可以打成指标推给 Prometheus,让「每次部署的健康度」也进可观测历史,而不是只在部署当时的终端里闪一下。 - -### 图 5 · 可用性 SLO / Error Budget〔Mer新·讲解·现〕 - -这张图回答「健康到什么程度才算达标」。运维域的硬指标是**服务可用性 ≥ 99.5%**,与之配套的是 MVP 基础设施成本控制在 **< ¥5,000/月**——这两个数字是运维域所有取舍(不上云托管、单体而非微服务、人工部署而非重 CI)的约束来源。图把可用性目标拆成三档 SLO(来自 `security-and-reliability.md` §5.1:游戏流 99.5%/月 ≈ error budget 3.6 小时、AI 生成 99%/月 ≈ 7.2 小时、支付 99.9%/月 ≈ 43 分钟),并点明这些目标怎么落进成本盒子。 - -```mermaid -flowchart TB - GOAL["可用性目标 ≥ 99.5%(MVP)
+ 基础设施成本 ¥5,000/月以内"]:::goal - subgraph SLO["三档 SLO + Error Budget(security-and-reliability §5.1)"] - direction LR - S1["游戏流
99.5% / 月
容错预算 ≈ 3.6 小时"] - S2["AI 生成
99% / 月
容错预算 ≈ 7.2 小时"] - S3["支付
99.9% / 月
容错预算 ≈ 43 分钟"] - end - subgraph COST["成本盒子怎么撑住目标"] - direction LR - C1["内网物理机
不上云托管"] - C2["单体
而非微服务"] - C3["人工部署
而非重 CI"] - end - GOAL --> SLO - GOAL --> COST - SLO -.->|"现状:只有一个目标数字,
没有东西在持续度量它"| GAP["缺口:error budget 无人消耗与记录
→ 由观测体系落地(图 6)
用 health 成功率算实际可用性 + burn rate 看板"]:::gap - - classDef goal fill:#f0fdf4,stroke:#16a34a,stroke-width:2px; - classDef gap fill:#fff7ed,stroke:#d97706,stroke-width:2px,stroke-dasharray:5 4; -``` - -这张图最该让人看出的是那条虚线指向的**缺口**:≥99.5% 现在是一个**目标数字**,但没有任何东西在持续度量它、没有 error budget 在被消耗和记录。换句话说,达标线画出来了,举证手段还没建。这个缺口的归宿就是图 6 的观测体系——用 health 探测的成功率算游戏流 API 的实际可用性,对着三档 SLO 做 burn rate 看板和告警,让「达没达标」从一句承诺变成一张有数据、有容错预算余量的看板。所以图 5 和图 6 是一对:图 5 立目标、点缺口,图 6 给落地手段。 - -### 图 6 · 观测体系:OTel → Grafana / 夜莺〔SVG新·架构·接(设计已出·待接线)〕 - -![图 6 观测体系 OTel→Grafana/夜莺](assets/06-观测体系.svg) - -这张图回答「线上靠什么持续观测」——也是本域**最该验状态纪律**的一张。它整体是**接**(待接线):创始人 2026-06-21 定了栈(OTel 统一采集 + Prometheus + Grafana 主看板 + 夜莺主告警),正式设计稿已出,但**代码大半没接、没后端**。所以图上做了一件最要紧的事:把「现在已经有什么」和「还得新建什么」用实心块 / 虚线框严格分开,绝不让人误以为观测已经建好。 - -图按「采集 → 管道 → 存储 → 看板/告警」一条链画,逐段标状态。**唯一已就位的实心块**在图正下方高亮:aigc-server 已引入 `spring-ai-alibaba-starter-graph-observation`,SAA 裸图每个节点已发 `spring.ai.alibaba.graph.node.` 的 Micrometer observation(带 trace 和耗时,失败反映在指标里)——这是后端唯一一段已经在以标准产出、且依赖已就位的观测信号。但图上同样标清楚:它缺一个把它收走的后端、缺一个非 NOOP 的 `ObservationRegistry`(要 actuator/micrometer 装配在席)。**其余全是虚线框 + 「待接线」**:OTel Java Agent(自动 span,monitor starter 当前根本没进 aigc 部署单元)、五个生成业务指标(`gen_task_total` / `gen_duration_seconds` / `gen_queue_depth` / `gen_gate_fail_total` / `llm_cost`,目前在代码里**全部零命中**,要补 MeterRegistry 注册才有数据,其中成本指标还额外依赖一个尚未落地的 new-api `logs.quota` 采集薄片)、OTel Collector、Prometheus / trace 后端 / 日志后端(trace 倾向 Tempo、日志倾向 Loki,但都标「选型待定」)、Grafana、夜莺、通知通道、admin 观测入口。 - -读这张图要抓住三层设计意图。其一,**为什么 Collector 居中而不是各组件直连后端**:让每个进程直接推各自后端会把地址、协议、脱敏逻辑硬编码进每个应用,上 k8s 或换后端就得改一圈;中间放一个 Collector 当统一入口,脱敏(手机号/token)、批处理、采样、路由全在这一层集中做,换后端只改 Collector 导出配置、应用零改动——这正是「先单体可跑、后平滑上 k8s」能成立的技术支点。其二,**Grafana 和夜莺不是冗余而是分工**:Grafana 负责「你主动去看时看得清」(三联下钻),夜莺负责「你没在看时它把你叫来」(把 P0–P3 升级链变成真规则真通道,补上「告警通道一根没接」这个最大的空);创始人点名的三类关键告警都在图上有家——可用性走 P0、错误率(5xx>2%)走 P1、生成失败率(<70%)走 P1。其三,图底那条红线是观测体系的底线:**埋点开关默认关、经 profile 显式开,agent 异步批量上报、Collector 不可达时本地丢弃**,硬验收是「停掉 Collector,主链路与各 API 行为不变、冒烟门仍全绿」——观测栈本身挂掉,绝不能影响被观测的服务。 - -除图上画出的「trace/日志后端选型、admin 嵌入方式与 SSO(详见图 7)」这几条待核外,源档《观测体系.md》§6 还留了两条待创始人拍板的口径,读图时一并记住、别当已定:一是**观测数据保留期与成本**——trace 与 metrics 存多久直接吃 mini-desktop 的磁盘,要对一下账「预算里留的那笔监控冗余够不够这套栈的存储」;二是**生成成功率告警阈值**——现在 P1 写的是「成功率 < 70%」(已经很糟该紧急处理的线),而验收线是 ≥80%,是否要在 70% 之外再加一条 80% 的趋势预警,让成功率从 80% 往下掉时就先有提醒、而不是等到 70% 才报,也待拍。 - -### 图 7 · admin 观测入口〔Mer新·流程·接(待接线)〕 - -这张图把图 6 右侧「admin 观测入口」那一块放大,讲清运营怎么从后台一键进监控大盘。它整体也是**接**——而且这块的现状是**零**,要整套新建:`game-admin/src` 下没有任何观测相关的路由、菜单或组件,后端也没有任何给 admin 用的观测查询接口。所以「控制台一键进盘」不是接一根线,而是要补齐前端菜单/路由/组件、后端管理员专属查询接口、嵌入安全处理三件,缺一不可。图用虚线把这三件标成「待接线」,并画出 admin 进盘的链路与那个必须解决的体验问题:**别让运营进个监控还要再登一次 Grafana**。 - -```mermaid -flowchart LR - OPS["运营
(system_users 已登录 admin)"] --> ADMIN["game-admin 控制台
『观测』菜单(待建)"]:::todo - ADMIN -->|"观测大图:嵌入只读大盘"| EMBED["Grafana 嵌入面板
可用性/错误率/生成成功率"]:::todo - ADMIN -->|"深挖跳转"| PROXY["admin 后端 auth-proxy
透传 system_users 身份头(待建)"]:::todo - PROXY -->|"可信头免登(SSO)"| GRAF["Grafana 完整大盘
trace/metrics/log 三联下钻"] - ADMIN -.->|"告警概览 API"| N9E["夜莺告警列表
最近告警 / 值班状态"] - EMBED --> GRAF - - classDef todo fill:#fff7ed,stroke:#d97706,stroke-width:1.5px,stroke-dasharray:5 4; -``` - -这张图要让人看出两处尚未拍板的设计缝,都还**待核**,不能当成已定。其一,**嵌入方式**:iframe 嵌 Grafana 不是写个 `