From e068e0efffb06109b02d2ec65123f40dc0a6b0c3 Mon Sep 17 00:00:00 2001 From: zizi Date: Mon, 25 May 2026 23:16:40 +0800 Subject: [PATCH] =?UTF-8?q?docs(explainer):=20=E5=AE=8C=E6=88=90=20muse-cl?= =?UTF-8?q?oud=20=E6=9E=B6=E6=9E=84=E8=AE=B2=E8=A7=A3=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/explainers/muse-cloud/README.md | 36 + docs/explainers/muse-cloud/assets/app.js | 112 +++ docs/explainers/muse-cloud/assets/styles.css | 587 +++++++++++++ .../muse-cloud/content/architecture.md | 45 + .../muse-cloud/content/domain-owners.md | 13 + .../muse-cloud/content/openapi-paths.md | 17 + .../muse-cloud/content/p1r-stages.md | 14 + .../muse-cloud/content/request-lifecycle.md | 16 + docs/explainers/muse-cloud/content/sources.md | 12 + .../muse-cloud/content/troubleshooting.md | 14 + docs/explainers/muse-cloud/index.html | 785 ++++++++++++++++++ 11 files changed, 1651 insertions(+) create mode 100644 docs/explainers/muse-cloud/README.md create mode 100644 docs/explainers/muse-cloud/assets/app.js create mode 100644 docs/explainers/muse-cloud/assets/styles.css create mode 100644 docs/explainers/muse-cloud/content/architecture.md create mode 100644 docs/explainers/muse-cloud/content/domain-owners.md create mode 100644 docs/explainers/muse-cloud/content/openapi-paths.md create mode 100644 docs/explainers/muse-cloud/content/p1r-stages.md create mode 100644 docs/explainers/muse-cloud/content/request-lifecycle.md create mode 100644 docs/explainers/muse-cloud/content/sources.md create mode 100644 docs/explainers/muse-cloud/content/troubleshooting.md create mode 100644 docs/explainers/muse-cloud/index.html diff --git a/docs/explainers/muse-cloud/README.md b/docs/explainers/muse-cloud/README.md new file mode 100644 index 00000000..c8e94c12 --- /dev/null +++ b/docs/explainers/muse-cloud/README.md @@ -0,0 +1,36 @@ +# muse-cloud 架构讲解包 + +本目录是面向初级后端工程师的 HTML-first 架构讲解包,帮助阅读者理解 P1R 阶段的 muse-cloud 目标架构、OpenAPI 合同路径、请求生命周期、领域 owner、阶段地图和排障路径。 + +## 阅读入口 + +- 权威阅读入口:`index.html` +- 维护备注:`content/*.md` +- 样式和本地交互:`assets/styles.css`、`assets/app.js` + +`index.html` 是完整主阅读面。`content/*.md` 只作为维护备注和 review 对照,不是权威正文;如果 HTML 与维护备注冲突,应按 P1R spec、OpenAPI 合同和当前代码事实修正 HTML。 + +## 来源优先级 + +1. `docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` +2. `docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md` +3. `docs/api-contracts/**/openapi.yaml` +4. 当前 `muse-cloud/` 代码事实 +5. 本目录 `content/*.md` 维护备注 + +## 关键口径 + +- OpenAPI `paths` 已经是完整外部路径,例如 `/app-api/muse/works`,不要再把它讲成运行时二次拼接。 +- `admin-api` 和 `app-api` 只是入口差异,不是领域 owner。 +- `X-API-Version: 1`、权限、owner、tenant、DTO、幂等、状态机和真实外部闭环都属于 P1R 验收口径。 +- 对外响应统一为 Yudao `CommonResult` 形态,JSON 字段是 `code/data/msg`。 + +## 不能算 P1R 完成的形态 + +- catch-all 合同入口兜底。 +- 通用持久化响应替代领域服务。 +- placeholder SSE 或固定事件。 +- 空列表替代真实读模型。 +- accepted task 没有进入成功、失败、取消或超时终态。 + +这些形态可以作为过渡保护网或基线识别结果,但不能作为真实业务 API 完成证据。 diff --git a/docs/explainers/muse-cloud/assets/app.js b/docs/explainers/muse-cloud/assets/app.js new file mode 100644 index 00000000..54cbed7d --- /dev/null +++ b/docs/explainers/muse-cloud/assets/app.js @@ -0,0 +1,112 @@ +(() => { + const navLinks = Array.from(document.querySelectorAll(".side-nav a")); + const sections = Array.from(document.querySelectorAll("[data-section]")); + const filterButtons = Array.from(document.querySelectorAll("[data-filter]")); + const clearButton = document.querySelector("[data-clear-filter]"); + const filterStatus = document.querySelector("[data-filter-status]"); + const diagnosticRows = Array.from(document.querySelectorAll(".diagnostic-row")); + + const setActiveLink = (id) => { + navLinks.forEach((link) => { + const isActive = link.getAttribute("href") === `#${id}`; + link.classList.toggle("is-active", isActive); + }); + }; + + // 导航高亮只读当前滚动位置,不改变 URL,避免干扰文档分享。 + if ("IntersectionObserver" in window) { + const observer = new IntersectionObserver( + (entries) => { + const visible = entries + .filter((entry) => entry.isIntersecting) + .sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0]; + if (visible) { + setActiveLink(visible.target.id); + } + }, + { rootMargin: "-20% 0px -65% 0px", threshold: [0.1, 0.35, 0.6] } + ); + + sections.forEach((section) => observer.observe(section)); + } else if (sections[0]) { + setActiveLink(sections[0].id); + } + + const resetCopyButton = (button, label) => { + window.setTimeout(() => { + button.textContent = label; + button.removeAttribute("aria-label"); + }, 2200); + }; + + // 复制失败时直接把值显示在按钮上,现场排障仍能手动选中。 + document.querySelectorAll("[data-copy]").forEach((button) => { + const defaultLabel = button.textContent; + button.addEventListener("click", async () => { + const value = button.getAttribute("data-copy") || ""; + try { + if (!navigator.clipboard || !navigator.clipboard.writeText) { + throw new Error("clipboard unavailable"); + } + await navigator.clipboard.writeText(value); + button.textContent = "已复制"; + button.setAttribute("aria-label", `已复制 ${value}`); + } catch (error) { + button.textContent = value; + button.setAttribute("aria-label", `复制失败,值为 ${value}`); + } + resetCopyButton(button, defaultLabel); + }); + }); + + const symptomLabel = { + "404": "404", + "400": "400", + "401-403": "401/403", + "409": "409", + "empty-list": "空列表但应有数据", + accepted: "accepted 但不终态", + "adapter-failure": "外部服务失败", + "response-shape": "响应字段不对", + }; + + const applyFilter = (symptom) => { + diagnosticRows.forEach((row) => { + row.classList.toggle("is-hidden", row.getAttribute("data-symptom") !== symptom); + }); + + filterButtons.forEach((button) => { + button.classList.toggle("is-selected", button.getAttribute("data-filter") === symptom); + }); + + if (filterStatus) { + filterStatus.textContent = `当前只显示:${symptomLabel[symptom] || symptom}。`; + } + + const troubleshooting = document.querySelector("#troubleshooting"); + if (troubleshooting) { + troubleshooting.scrollIntoView({ behavior: "smooth", block: "start" }); + } + }; + + const clearFilter = () => { + diagnosticRows.forEach((row) => row.classList.remove("is-hidden")); + filterButtons.forEach((button) => button.classList.remove("is-selected")); + if (filterStatus) { + filterStatus.textContent = "当前显示全部诊断项。"; + } + }; + + filterButtons.forEach((button) => { + button.addEventListener("click", () => { + const symptom = button.getAttribute("data-filter"); + if (symptom) { + applyFilter(symptom); + } + }); + }); + + if (clearButton) { + clearButton.addEventListener("click", clearFilter); + } +})(); diff --git a/docs/explainers/muse-cloud/assets/styles.css b/docs/explainers/muse-cloud/assets/styles.css new file mode 100644 index 00000000..4326efe0 --- /dev/null +++ b/docs/explainers/muse-cloud/assets/styles.css @@ -0,0 +1,587 @@ +:root { + --bg: #f6f4ef; + --surface: #fffdf8; + --surface-soft: #ebe7dc; + --text: #202124; + --muted: #626665; + --line: #d8d2c4; + --accent: #0f766e; + --accent-strong: #115e59; + --danger: #a43f2d; + --code: #17324d; + --shadow: 0 1px 2px rgba(32, 33, 36, 0.08); + --radius: 8px; + --small-radius: 6px; + --mono: "SFMono-Regular", "Cascadia Code", "Liberation Mono", Menlo, monospace; + --sans: "Avenir Next", "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); + font-family: var(--sans); + font-size: 16px; + line-height: 1.7; + letter-spacing: 0; +} + +a { + color: inherit; +} + +code { + border: 1px solid #d4dfdc; + border-radius: 5px; + background: #eef5f1; + color: var(--code); + font-family: var(--mono); + font-size: 0.92em; + padding: 0.08rem 0.28rem; +} + +button, +a { + transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, transform 160ms ease; +} + +button:focus-visible, +a:focus-visible { + outline: 3px solid rgba(15, 118, 110, 0.28); + outline-offset: 3px; +} + +.site-header { + display: flex; + justify-content: space-between; + gap: 2rem; + padding: 3rem clamp(1rem, 4vw, 3rem) 2rem; + border-bottom: 1px solid var(--line); + background: var(--surface); +} + +.site-header h1 { + margin: 0.2rem 0 0.75rem; + font-size: clamp(2rem, 4vw, 4.3rem); + line-height: 1.08; + font-weight: 780; +} + +.lead { + max-width: 760px; + margin: 0; + color: var(--muted); + font-size: 1.05rem; +} + +.eyebrow { + margin: 0 0 0.4rem; + color: var(--accent-strong); + font-size: 0.78rem; + font-weight: 760; + letter-spacing: 0; + text-transform: uppercase; +} + +.header-facts { + align-self: flex-end; + display: grid; + gap: 0.45rem; + min-width: 210px; +} + +.header-facts span { + border: 1px solid var(--line); + border-radius: var(--small-radius); + background: #f8f7f2; + color: var(--muted); + font-family: var(--mono); + font-size: 0.78rem; + padding: 0.4rem 0.55rem; +} + +.page-shell { + display: grid; + grid-template-columns: minmax(180px, 240px) minmax(0, 1fr) minmax(230px, 300px); + grid-template-areas: "nav main quick"; + gap: clamp(1rem, 2vw, 2rem); + max-width: 1640px; + margin: 0 auto; + padding: 1.5rem clamp(1rem, 3vw, 2.5rem) 4rem; +} + +.side-nav { + grid-area: nav; + position: sticky; + top: 1rem; + align-self: start; + display: grid; + gap: 0.25rem; + padding: 0.75rem; + border: 1px solid var(--line); + border-radius: var(--radius); + background: rgba(255, 253, 248, 0.9); + box-shadow: var(--shadow); +} + +.side-nav a { + border-radius: var(--small-radius); + color: var(--muted); + font-size: 0.95rem; + font-weight: 650; + padding: 0.45rem 0.55rem; + text-decoration: none; +} + +.side-nav a:hover, +.side-nav a.is-active { + background: #e3f2ed; + color: var(--accent-strong); +} + +.quick-panel { + grid-area: quick; + position: sticky; + top: 1rem; + align-self: start; +} + +.quick-panel-inner { + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); + padding: 1rem; + box-shadow: var(--shadow); +} + +.quick-panel h2 { + margin: 0 0 0.5rem; + font-size: 1.15rem; + line-height: 1.25; +} + +.quick-panel p { + margin: 0 0 0.85rem; + color: var(--muted); + font-size: 0.92rem; +} + +.triage-actions { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; +} + +.triage-actions button, +.copy-button { + border: 1px solid var(--line); + border-radius: var(--small-radius); + background: #f9f8f3; + color: var(--text); + cursor: pointer; + font: inherit; + font-size: 0.86rem; + font-weight: 700; + padding: 0.38rem 0.55rem; +} + +.triage-actions button:hover, +.copy-button:hover { + border-color: var(--accent); + color: var(--accent-strong); + transform: translateY(-1px); +} + +.triage-actions button.is-selected { + background: var(--accent); + border-color: var(--accent); + color: #fff; +} + +.triage-actions .clear-filter { + width: 100%; + background: #fff7ed; + color: #8b4a1f; +} + +.quick-status { + margin-top: 0.8rem; + min-height: 2.8em; +} + +.content { + grid-area: main; + min-width: 0; +} + +.manual-section { + scroll-margin-top: 1rem; + padding: 2rem 0; + border-bottom: 1px solid var(--line); +} + +.manual-section:first-child { + padding-top: 0.5rem; +} + +.section-heading { + margin-bottom: 1rem; +} + +.section-heading h2 { + margin: 0; + font-size: clamp(1.45rem, 2vw, 2.3rem); + line-height: 1.2; +} + +.warning-band, +.source-note, +.path-proof, +.architecture-flow { + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); + box-shadow: var(--shadow); +} + +.warning-band { + margin: 1.2rem 0; + border-color: #e2b3a8; + color: #5d2b21; + padding: 0.9rem 1rem; +} + +.source-note { + margin: 1.3rem 0 0; + color: var(--muted); + font-size: 0.88rem; + padding: 0.7rem 0.85rem; +} + +.definition-grid, +.architecture-grid, +.lifecycle-grid, +.stage-grid, +.owner-cards { + display: grid; + gap: 0.9rem; +} + +.definition-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin: 1rem 0; +} + +.definition-grid > div, +.architecture-layer, +.lifecycle-node, +.stage-card, +.diagnostic-row, +.owner-cards article { + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); + padding: 1rem; + box-shadow: var(--shadow); +} + +dt { + color: var(--text); + font-weight: 780; +} + +dd { + margin: 0 0 0.75rem; + color: var(--muted); +} + +dd:last-child { + margin-bottom: 0; +} + +.architecture-flow { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: 0.5rem; + margin: 1.2rem 0; + padding: 0.8rem; +} + +.architecture-flow span { + display: grid; + min-height: 54px; + place-items: center; + border: 1px solid #cad8d4; + border-radius: var(--small-radius); + background: #edf6f2; + color: var(--accent-strong); + font-size: 0.86rem; + font-weight: 780; + text-align: center; +} + +.architecture-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.architecture-layer h3, +.lifecycle-node h3, +.stage-card h3, +.diagnostic-row h3, +.owner-cards h3 { + margin: 0 0 0.7rem; + font-size: 1.05rem; + line-height: 1.25; +} + +.path-proof { + display: grid; + gap: 0.75rem; + margin: 1rem 0; + padding: 1rem; +} + +.path-proof > div { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.55rem; + min-height: 42px; + border-bottom: 1px solid #ece5d8; + padding-bottom: 0.75rem; +} + +.path-proof > div:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +.step-label { + min-width: 5.8rem; + color: var(--muted); + font-size: 0.84rem; + font-weight: 760; +} + +.lifecycle-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.owner-table-wrap { + margin: 1rem 0; + overflow-x: auto; +} + +.owner-table { + width: 100%; + min-width: 760px; + border-collapse: collapse; + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); + overflow: hidden; +} + +.owner-table th, +.owner-table td { + border-bottom: 1px solid var(--line); + padding: 0.75rem; + text-align: left; + vertical-align: top; +} + +.owner-table thead th { + background: var(--surface-soft); + color: var(--text); +} + +.owner-table tbody th { + color: var(--accent-strong); +} + +.owner-table tr:last-child th, +.owner-table tr:last-child td { + border-bottom: 0; +} + +.owner-cards { + display: none; +} + +.stage-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.diagnostic-list { + display: grid; + gap: 0.9rem; +} + +.diagnostic-row { + display: grid; + grid-template-columns: minmax(120px, 180px) minmax(0, 1fr); + gap: 1rem; +} + +.diagnostic-row h3 { + color: var(--danger); +} + +.diagnostic-row.is-hidden { + display: none; +} + +.source-map { + display: grid; + gap: 0.55rem; + margin-top: 1rem; +} + +.source-map div { + display: grid; + grid-template-columns: 150px minmax(0, 1fr) minmax(0, 1.4fr); + gap: 0.75rem; + border: 1px solid var(--line); + border-radius: var(--small-radius); + background: var(--surface); + padding: 0.75rem; +} + +.source-map span { + color: var(--muted); + overflow-wrap: anywhere; +} + +@media (max-width: 1180px) { + .page-shell { + grid-template-columns: minmax(170px, 220px) minmax(0, 1fr); + grid-template-areas: + "nav quick" + "nav main"; + } + + .quick-panel { + position: static; + } + + .lifecycle-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .architecture-flow { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 820px) { + .site-header { + display: grid; + padding: 2rem 1rem 1.25rem; + } + + .header-facts { + align-self: start; + min-width: 0; + } + + .page-shell { + grid-template-columns: 1fr; + grid-template-areas: + "nav" + "quick" + "main"; + padding: 1rem 1rem 3rem; + } + + .side-nav { + position: static; + display: flex; + overflow-x: auto; + padding: 0.55rem; + } + + .side-nav a { + flex: 0 0 auto; + } + + .definition-grid, + .architecture-grid, + .lifecycle-grid, + .stage-grid { + grid-template-columns: 1fr; + } + + .architecture-flow { + grid-template-columns: 1fr; + } + + .owner-table-wrap { + display: none; + } + + .owner-cards { + display: grid; + grid-template-columns: 1fr; + } + + .diagnostic-row { + grid-template-columns: 1fr; + } + + .source-map div { + grid-template-columns: 1fr; + } +} + +@media (max-width: 520px) { + body { + font-size: 15px; + } + + .manual-section { + padding: 1.6rem 0; + } + + .path-proof > div { + align-items: flex-start; + flex-direction: column; + } + + .copy-button { + max-width: 100%; + overflow-wrap: anywhere; + text-align: left; + } +} + +@media print { + .side-nav, + .quick-panel, + .copy-button { + display: none; + } + + .site-header, + .manual-section, + .definition-grid > div, + .architecture-layer, + .lifecycle-node, + .stage-card, + .diagnostic-row { + box-shadow: none; + } + + .page-shell { + display: block; + max-width: none; + padding: 0; + } +} diff --git a/docs/explainers/muse-cloud/content/architecture.md b/docs/explainers/muse-cloud/content/architecture.md new file mode 100644 index 00000000..01cc9d50 --- /dev/null +++ b/docs/explainers/muse-cloud/content/architecture.md @@ -0,0 +1,45 @@ +# 架构维护备注 + +HTML 主入口中的架构层说明以 P1R 真实 API 规格第 7 节为来源。这里仅保留维护时需要核对的简版边界。 + +## Controller + +- 负责:鉴权入口、参数校验、`X-API-Version: 1` 校验、DTO 转换和统一响应封装。 +- 不负责:业务规则、数据库访问、外部服务调用、手写业务 JSON。 +- 证据:Controller 路由映射、OpenAPI operationId、版本头参数。 + +## Application Service + +- 负责:用例编排、事务、幂等、权限摘要、跨模块 facade、任务创建、错误归一和响应组装。 +- 不负责:绕过领域 owner 直接写其他模块事实。 +- 证据:Application Service 接口和实现、事务边界、幂等记录。 + +## Domain + +- 负责:聚合状态机、不变式、版本冲突、授权快照消费规则和状态变化决策。 +- 不负责:依赖 Controller DTO、Yudao Web DTO 或外部 API DTO。 +- 证据:领域服务、聚合规则、状态机测试。 + +## Query / Assembler + +- 负责:读模型查询、DTO / VO 组装、OpenAPI response 对齐。 +- 不负责:把数据库表行、operation record 或 workflow task 原样暴露给前端。 +- 证据:Query Service、Assembler、OpenAPI schema。 + +## Persistence Mapper + +- 负责:MyBatis Mapper、事务内持久化、唯一约束、owner / tenant 条件和查询 SQL。 +- 不负责:业务状态机决策或外部服务语义。 +- 证据:Mapper、SQL、Flyway、唯一索引。 + +## External Adapter + +- 负责:New-API、RAGFlow、文件服务、SSE、任务执行器等外部边界,暴露超时、错误分类、重试和补偿语义。 +- 不负责:伪造外部成功或吞掉可恢复失败。 +- 证据:adapter client、超时配置、错误映射、集成测试。 + +## CommonResult + +- 负责:对外统一响应形态,字段为 `code/data/msg`。 +- 不负责:用 `message` 替代 `msg`,或用通用 JSON 行替代合同 DTO。 +- 证据:OpenAPI base schema、Controller 返回类型、响应断言。 diff --git a/docs/explainers/muse-cloud/content/domain-owners.md b/docs/explainers/muse-cloud/content/domain-owners.md new file mode 100644 index 00000000..6c4b1e6f --- /dev/null +++ b/docs/explainers/muse-cloud/content/domain-owners.md @@ -0,0 +1,13 @@ +# 领域 owner 维护备注 + +P1R 的 owner 边界来自真实 API 规格第 5.3 节和阶段拆分。`admin-api` / `app-api` 只是入口侧,不拆分领域事实。 + +| 领域 | 负责事实 | 不应越界 | +|------|----------|----------| +| Content | 作品、章节、Block、导入导出、Suggestion Merge | 不直接拥有市场授权 | +| Meta | MetaSchema、保护节点、功能链治理 | 不把保护节点降级为用户槽位 | +| Account | 用户资料、权益、配额、用量、New-API 归因 | 不允许请求体伪造归因 | +| AI | Prompt、Agent、Tool Grant、任务、质量治理 | 不伪造 AI 调用成功 | +| Knowledge | 知识库、文档、切片、索引、图谱 | 不用空列表替代索引结果 | +| Market | 资产、授权、安装、发布、申诉、handoff | 不写目标领域绑定事实 | +| Events | SSE、任务事件、跨端事件 | 不返回固定假事件 | diff --git a/docs/explainers/muse-cloud/content/openapi-paths.md b/docs/explainers/muse-cloud/content/openapi-paths.md new file mode 100644 index 00000000..094fd414 --- /dev/null +++ b/docs/explainers/muse-cloud/content/openapi-paths.md @@ -0,0 +1,17 @@ +# OpenAPI 路径维护备注 + +当前 `docs/api-contracts/**/openapi.yaml` 的 `paths` 键已经是完整外部路径,不需要也不应该在讲解包中描述为运行时再追加入口前缀。 + +已核实示例: + +- `docs/api-contracts/content/openapi.yaml` +- 路径:`/app-api/muse/works` +- operationId:`listWorks` +- 当前代码事实:`AppContentController`、`ContentAppService`、`ContentAppServiceImpl` + +维护规则: + +1. 讲解合同路径时直接引用 OpenAPI `paths` 的完整键。 +2. `app-api` 和 `admin-api` 只说明入口侧,不说明领域归属。 +3. 后端完成矩阵应以 operationId、Controller、Application Service 和真实业务证据共同判断。 +4. 不把仍由 catch-all 或通用持久化处理的 operation 标为完成。 diff --git a/docs/explainers/muse-cloud/content/p1r-stages.md b/docs/explainers/muse-cloud/content/p1r-stages.md new file mode 100644 index 00000000..c0386c8f --- /dev/null +++ b/docs/explainers/muse-cloud/content/p1r-stages.md @@ -0,0 +1,14 @@ +# P1R 阶段维护备注 + +P1R 采用总 spec、阶段 spec、阶段 plan 的执行模型。HTML 主入口需要把每个阶段的负责领域、真实能力、假完成形态和验收证据讲清楚。 + +1. P1R-0:API 基线门禁。 +2. P1R-1:Content Real API。 +3. P1R-2:Meta Real API。 +4. P1R-3:Account Real API。 +5. P1R-4:AI Real API。 +6. P1R-5:Knowledge Real API。 +7. P1R-6:Market Real API。 +8. P1R-7:End-to-End Acceptance。 + +维护重点:P1R-0 不输出 completed;后续阶段必须用真实业务行为、真实外部闭环和真实验收证据证明完成。 diff --git a/docs/explainers/muse-cloud/content/request-lifecycle.md b/docs/explainers/muse-cloud/content/request-lifecycle.md new file mode 100644 index 00000000..1ed7ba39 --- /dev/null +++ b/docs/explainers/muse-cloud/content/request-lifecycle.md @@ -0,0 +1,16 @@ +# 请求生命周期维护备注 + +HTML 主入口需要把一次请求从入口到响应讲完整,不能只列 Controller 或 URL。 + +建议顺序: + +1. HTTP Request:路径、方法、认证信息和请求体。 +2. Version / Auth:`X-API-Version: 1`、登录态、RBAC、owner、tenant。 +3. Controller DTO:参数校验、DTO 转换、统一响应包装。 +4. Application:事务、幂等、跨模块 facade、任务或外部编排。 +5. Domain:状态机、不变式、revision / expectedVersion / expectedStatus。 +6. DB / Adapter:Mapper、外部 adapter、outbox、任务执行器。 +7. Assembler:读模型和 OpenAPI response DTO。 +8. CommonResult:对外 JSON 字段 `code/data/msg`。 + +每个节点需要给出常见问题、定位证据和下一步,方便初级工程师按层排障。 diff --git a/docs/explainers/muse-cloud/content/sources.md b/docs/explainers/muse-cloud/content/sources.md new file mode 100644 index 00000000..e60d8a57 --- /dev/null +++ b/docs/explainers/muse-cloud/content/sources.md @@ -0,0 +1,12 @@ +# 来源映射维护备注 + +| HTML section | Maintenance note | Upstream source | +|--------------|------------------|-----------------| +| architecture | content/architecture.md | P1R real API design section 7 | +| openapi | content/openapi-paths.md | docs/api-contracts/**/openapi.yaml | +| lifecycle | content/request-lifecycle.md | P1R real API design section 5 and 7 | +| owners | content/domain-owners.md | P1R real API design section 5.3 | +| stages | content/p1r-stages.md | P1R real API design section 10 | +| troubleshooting | content/troubleshooting.md | explainer design section 7.6 | + +维护时先看 upstream source,再同步 HTML;本文件只负责把 HTML 区块、维护备注和上游证据对应起来。 diff --git a/docs/explainers/muse-cloud/content/troubleshooting.md b/docs/explainers/muse-cloud/content/troubleshooting.md new file mode 100644 index 00000000..9addf60f --- /dev/null +++ b/docs/explainers/muse-cloud/content/troubleshooting.md @@ -0,0 +1,14 @@ +# 排障维护备注 + +HTML 主入口至少覆盖以下八类症状,并给出优先定位层、检查项、下一步和证据。 + +1. 404。 +2. 400。 +3. 401/403。 +4. 409。 +5. 空列表但应有数据。 +6. accepted 但不终态。 +7. 外部服务失败。 +8. 响应字段不对。 + +排障规则:先定位请求落在哪一层,再找能证明该层事实的文件、日志、测试或运行结果;没有证据时不宣称完成。 diff --git a/docs/explainers/muse-cloud/index.html b/docs/explainers/muse-cloud/index.html new file mode 100644 index 00000000..d9e9edcf --- /dev/null +++ b/docs/explainers/muse-cloud/index.html @@ -0,0 +1,785 @@ + + + + + + muse-cloud 架构讲解包 + + + + + +
+ + + + +
+
+
+

阅读口径

+

这是讲解包,不是 API 完成证明

+
+

+ 本页解释 P1R 阶段 muse-cloud 应如何落到真实后端架构。它可以帮助你读代码、查合同、定位问题, + 但不能替代 API 完成矩阵、测试报告、真实环境验收记录。 +

+
+ 以下形态不能算 P1R 完成: + catch-all 合同入口兜底、通用持久化响应替代领域服务、placeholder SSE 或固定事件、 + 空列表替代真实读模型、accepted task 没有进入成功/失败/取消/超时终态。 +
+
+
+
已验证事实
+
来自 OpenAPI 合同、当前 Java 类、规格文档或校验命令。
+
+
+
推断
+
基于架构规则给出的合理排障方向,仍需用代码、日志或测试证明。
+
+
+
维护备注
+
content/*.md 只用于维护对照,不是权威正文。
+
+
+

+ 来源:content/sources.md;上游:P1R real API design section 5 and README。 +

+
+ +
+
+

Architecture

+

系统分层先看责任边界

+
+

+ P1R 的真实 API 不靠 Controller 直接拼响应,也不靠通用持久化层伪造完成。每一层都有明确职责、 + 禁止事项和定位证据。 +

+ +
+ Controller + Application Service + Domain + Persistence / Adapter + Assembler + CommonResult +
+ +
+
+

Controller

+
+
负责什么
+
鉴权入口、参数校验、X-API-Version: 1 校验、DTO 转换和统一响应封装。
+
不应负责什么
+
业务规则、数据库访问、外部服务调用、手写业务 JSON。
+
定位证据
+
Controller 路由映射、OpenAPI operationId、版本头参数、响应类型断言。
+
+
+ +
+

Application Service

+
+
负责什么
+
用例编排、事务、幂等、权限摘要、跨模块 facade、任务创建、错误归一和响应组装。
+
不应负责什么
+
绕过领域 owner 直接写其他模块事实,或把通用 operation record 当业务结果。
+
定位证据
+
ContentAppService、实现类、事务边界、幂等记录和用例测试。
+
+
+ +
+

Domain

+
+
负责什么
+
聚合状态机、不变式、版本冲突、授权快照消费规则和状态变化决策。
+
不应负责什么
+
依赖 Controller DTO、Yudao Web DTO 或外部 API DTO。
+
定位证据
+
领域服务、聚合规则、状态机测试、revision / expectedVersion 校验。
+
+
+ +
+

Query / Assembler

+
+
负责什么
+
读模型查询、DTO / VO 组装、OpenAPI response 对齐。
+
不应负责什么
+
把数据库表行、operation record 或 workflow task 原样暴露给前端。
+
定位证据
+
Query Service、Assembler、OpenAPI schema、响应快照或契约测试。
+
+
+ +
+

Persistence Mapper

+
+
负责什么
+
MyBatis Mapper、事务内持久化、唯一约束、owner / tenant 条件和查询 SQL。
+
不应负责什么
+
业务状态机决策、外部服务语义或跨 owner 的业务写入。
+
定位证据
+
Mapper、SQL、Flyway、唯一索引、owner 和 tenant 条件。
+
+
+ +
+

External Adapter

+
+
负责什么
+
New-API、RAGFlow、文件服务、SSE、任务执行器等外部边界。
+
不应负责什么
+
伪造外部成功、吞掉可恢复失败、隐藏超时和重试语义。
+
定位证据
+
adapter client、超时配置、错误映射、重试策略、集成测试。
+
+
+ +
+

CommonResult

+
+
负责什么
+
对外统一响应形态,JSON 字段为 code/data/msg
+
不应负责什么
+
message 替代 msg,或用通用 JSON 行替代合同 DTO。
+
定位证据
+
OpenAPI base schema、Controller 返回类型、响应断言。
+
+
+
+

+ 来源:content/architecture.md;上游:P1R real API design section 7。 +

+
+ +
+
+

OpenAPI

+

合同 paths 是完整外部路径

+
+

+ docs/api-contracts/**/openapi.yamlpaths 键已经是完整外部路径。 + 讲解、排障和完成矩阵都应直接引用这些键,不应再描述为运行时追加入口前缀。 +

+ +
+
+ 合同文件 + docs/api-contracts/content/openapi.yaml +
+
+ 完整路径 + /app-api/muse/works + +
+
+ operationId + listWorks + +
+
+ 代码入口 + AppContentController + ContentAppService +
+
+ +
+
+
入口侧
+
/app-api/muse/** 是 app 入口,/admin-api/muse/** 是 admin 入口。
+
+
+
领域归属
+
app/admin 不决定 owner;业务事实只能由 Content、Meta、Account 等领域模块拥有。
+
+
+
完成判断
+
operationId、Controller、Application Service、领域事实、测试和验收证据要一起看。
+
+
+

+ 来源:content/openapi-paths.md;上游:docs/api-contracts/content/openapi.yaml。 +

+
+ +
+
+

Lifecycle

+

一次请求从入口走到 CommonResult

+
+
+
+

HTTP Request

+
+
常见问题
+
URL 写错、HTTP method 不匹配、app/admin 入口混用。
+
定位证据
+
OpenAPI path、Controller mapping、网关访问日志。
+
下一步
+
先核对合同完整路径,再看请求是否到达目标 Controller。
+
+
+
+

Version/Auth

+
+
常见问题
+
缺少 X-API-Version: 1、登录态失效、RBAC 或 owner 拒绝。
+
定位证据
+
请求 header、Security 配置、权限日志、owner / tenant 条件。
+
下一步
+
补齐版本头后复测,再区分 401 登录问题和 403 权限问题。
+
+
+
+

Controller DTO

+
+
常见问题
+
必填字段、枚举、分页参数或 schema 不符合合同。
+
定位证据
+
Request DTO、Bean Validation、OpenAPI requestBody 和 parameters。
+
下一步
+
用合同样例构造最小请求,确认错误来自 DTO 而不是业务层。
+
+
+
+

Application

+
+
常见问题
+
事务边界缺失、幂等键重复、跨模块 facade 使用不当。
+
定位证据
+
Application Service 方法、事务注解、commandId 记录、用例测试。
+
下一步
+
确认用例是否进入专用服务,避免落到通用占位处理。
+
+
+
+

Domain

+
+
常见问题
+
revision、expectedVersion、expectedStatus 或状态机前置条件冲突。
+
定位证据
+
领域规则、聚合状态、冲突错误码、状态机测试。
+
下一步
+
读取当前业务事实和版本,再判断应重试、刷新还是返回 409。
+
+
+
+

DB/Adapter

+
+
常见问题
+
Mapper 条件漏 owner、外部服务超时、任务没有被执行器消费。
+
定位证据
+
SQL、Flyway、adapter 日志、outbox、workflow task。
+
下一步
+
区分数据库事实不存在、查询条件过滤掉、外部 adapter 失败三类问题。
+
+
+
+

Assembler

+
+
常见问题
+
DTO 字段缺失、读模型和 OpenAPI response 不一致。
+
定位证据
+
Assembler、Query Service、OpenAPI response schema、契约测试。
+
下一步
+
不要直接改表结构响应,先对齐读模型和合同 DTO。
+
+
+
+

CommonResult

+
+
常见问题
+
返回 message、遗漏 data、错误码和业务错误混乱。
+
定位证据
+
Controller 返回类型、OpenAPI base schema、响应断言。
+
下一步
+
保持 code/data/msg 外壳稳定,再定位 data 内部 DTO。
+
+
+
+

+ 来源:content/request-lifecycle.md;上游:P1R real API design section 5 and 7。 +

+
+ +
+
+

Owner Boundaries

+

app/admin 不是领域 owner

+
+

+ 入口侧只表示调用场景。业务事实的写入、状态推进和验收证据必须回到所属领域模块。 +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
领域负责事实典型依赖不应越界
Content作品、章节、Block、导入导出、Suggestion MergeFile、AI suggestion不直接拥有市场授权
MetaMetaSchema、保护节点、功能链治理PostgreSQL、审计不把保护节点降级为用户槽位
Account用户资料、权益、配额、用量、New-API 归因New-API、账本不允许请求体伪造归因
AIPrompt、Agent、Tool Grant、任务、质量治理New-API、SSE不伪造 AI 调用成功
Knowledge知识库、文档、切片、索引、图谱RAGFlow、File不用空列表代替索引结果
Market资产、授权、安装、发布、申诉、handoffAccount、授权快照不写目标领域绑定事实
EventsSSE、任务事件、跨端事件Redis、SSE不返回固定假事件
+
+ +
+
+

Content

+

作品、章节、Block、导入导出、Suggestion Merge;不直接拥有市场授权。

+
+
+

Meta

+

MetaSchema、保护节点、功能链治理;保护节点不能被用户槽位覆盖。

+
+
+

Account

+

用户资料、权益、配额、用量、New-API 归因;不信任请求体伪造归因。

+
+
+

AI

+

Prompt、Agent、Tool Grant、任务、质量治理;不伪造 AI 调用成功。

+
+
+

Knowledge

+

知识库、文档、切片、索引、图谱;不用空列表代替索引结果。

+
+
+

Market

+

资产、授权、安装、发布、申诉、handoff;不写目标领域绑定事实。

+
+
+

Events

+

SSE、任务事件、跨端事件;不返回固定假事件。

+
+
+ +

+ 来源:content/domain-owners.md;上游:P1R real API design section 5.3。 +

+
+ +
+
+

P1R Map

+

阶段地图按证据推进,不按感觉打勾

+
+
+
+

P1R-0 Baseline Gate

+
+
负责领域
+
全量 OpenAPI operation 清点和完成矩阵。
+
真实能力
+
标注 owner、side、method、路径、写命令、外部依赖、异步属性和当前实现状态。
+
假完成形态
+
把基线审计结果直接标为 completed。
+
验收证据
+
机器可读矩阵、人类可审报告、可复跑检查命令。
+
+
+
+

P1R-1 Content Real API

+
+
负责领域
+
作品、章节、Block、导入导出、动态字段、来源归因、Suggestion Merge。
+
真实能力
+
核心内容 API 回到 OpenAPI DTO,文件导入和导出产生真实产物。
+
假完成形态
+
空作品列表、固定详情、通用持久化行或未消费的 accepted task。
+
验收证据
+
Controller、ContentAppService、Mapper、文件任务、契约测试和真实环境冒烟。
+
+
+
+

P1R-2 Meta Real API

+
+
负责领域
+
MetaSchema、保护节点、功能链治理。
+
真实能力
+
版本、草稿、验证、影响预览、发布、激活、回滚、废弃和灰度规则。
+
假完成形态
+
保护节点被当作普通用户槽位,或激活版本没有唯一约束。
+
验收证据
+
expectedVersion、validationResultId、impactPreviewId、审计和状态机测试。
+
+
+
+

P1R-3 Account Real API

+
+
负责领域
+
用户资料、权益、配额、用量、购买/授权/发布记录聚合和 New-API 归因。
+
真实能力
+
绑定重验、调用归因、用量查询、导出下载和安全事件落到真实事实。
+
假完成形态
+
请求体任意声明归因,或用余额样例替代账本事实。
+
验收证据
+
真实 correlation/call 记录、owner 校验、导出凭证、账户聚合测试。
+
+
+
+

P1R-4 AI Real API

+
+
负责领域
+
Prompt、Agent、Tool Grant、任务、质量治理、访问日志和审计。
+
真实能力
+
AI task 调用真实 New-API,任务支持成功、失败、取消、重试,SSE 推送真实事件。
+
假完成形态
+
固定 AI 文本、placeholder SSE、runtime 自授权。
+
验收证据
+
New-API 调用记录、SSE/轮询终态、授权隔离测试、质量治理审计。
+
+
+
+

P1R-5 Knowledge Real API

+
+
负责领域
+
全局知识库、用户知识库、文档版本、切片、索引、图谱和知识草稿。
+
真实能力
+
文档进入 RAGFlow / 知识引擎,检索结果回到 Knowledge API。
+
假完成形态
+
上传后只存文件名,检索永远空列表,知识草稿不写 Canonical 事实。
+
验收证据
+
入库、切片、索引、检索、草稿确认和授权快照消费记录。
+
+
+
+

P1R-6 Market Real API

+
+
负责领域
+
资产、分类、推荐、收藏、购买、安装、发布、申诉和 handoff。
+
真实能力
+
购买和安装写授权、安装记录和账户聚合;handoff 只交付来源授权摘要和 token。
+
假完成形态
+
Market 直接写目标领域绑定事实,或未完成 ADR 就开放作品资产高阶模式。
+
验收证据
+
授权快照、安装记录、发布审核、申诉审计、目标 owner 消费 precheck。
+
+
+
+

P1R-7 End-to-End Acceptance

+
+
负责领域
+
跨 Content、AI、Knowledge、Market、Account、Events 的真实环境闭环。
+
真实能力
+
创建作品、AI suggestion、知识索引、市场购买安装、账户用量和治理审计形成链路。
+
假完成形态
+
只跑单元测试或只看接口返回 200,没有真实外部依赖和业务终态。
+
验收证据
+
真实 PG/Redis 启动、New-API 调用、RAG 检索、SSE/轮询终态、端到端报告。
+
+
+
+

+ 来源:content/p1r-stages.md;上游:P1R real API design section 10。 +

+
+ +
+
+

Troubleshooting

+

按症状进入排障,不跳层猜原因

+
+
+
+

404

+
+
症状
+
请求没有命中目标 API,或被 catch-all / 网关兜底吞掉。
+
优先定位层
+
OpenAPI 路径 / Controller。
+
检查项
+
合同完整路径、HTTP method、/muse、Controller mapping、路由注册。
+
下一步
+
先用 /app-api/muse/works 这类合同完整路径复测,再查 Controller。
+
证据
+
docs/api-contracts/**/openapi.yaml、Controller 类、访问日志。
+
+
+ +
+

400

+
+
症状
+
参数校验失败、版本头缺失、请求体不符合 schema。
+
优先定位层
+
Header / DTO。
+
检查项
+
X-API-Version: 1、必填字段、枚举、分页参数、Bean Validation。
+
下一步
+
构造合同最小请求,确认是入口校验失败还是业务拒绝。
+
证据
+
OpenAPI parameters、requestBody、Request DTO、校验错误日志。
+
+
+ +
+

401/403

+
+
症状
+
未登录、token 失效、权限不足、owner / tenant 不匹配。
+
优先定位层
+
Security。
+
检查项
+
登录态、RBAC、owner、tenant、来源授权、高危动作权限。
+
下一步
+
先区分认证失败和授权失败,再看后端条件是否只靠前端隐藏入口。
+
证据
+
Security 配置、权限日志、SQL owner 条件、审计记录。
+
+
+ +
+

409

+
+
症状
+
重复命令、版本冲突、状态机前置条件不满足。
+
优先定位层
+
Domain / Idempotency。
+
检查项
+
commandId、revision、expectedVersion、expectedStatus、状态机。
+
下一步
+
读取当前业务事实和幂等记录,判断应返回历史结果还是提示刷新。
+
证据
+
领域状态、幂等表、唯一索引、状态机测试。
+
+
+ +
+

空列表但应有数据

+
+
症状
+
接口返回成功但列表为空,实际应存在业务事实。
+
优先定位层
+
Query / Persistence。
+
检查项
+
owner 条件、tenant 条件、读模型刷新、mapper SQL、分页参数。
+
下一步
+
用同一用户和 tenant 查表,再确认 assembler 是否过滤或映射错误。
+
证据
+
数据库事实、Mapper SQL、Query Service、响应 DTO。
+
+
+ +
+

accepted 但不终态

+
+
症状
+
创建任务返回 accepted,但后续查询一直没有成功、失败、取消或超时终态。
+
优先定位层
+
Job / Adapter。
+
检查项
+
workflow task、outbox、执行器、外部服务回调、重试和补偿。
+
下一步
+
跟踪任务状态变化和执行器日志,不把 accepted 当完成。
+
证据
+
workflow task 记录、outbox、adapter 日志、任务状态测试。
+
+
+ +
+

外部服务失败

+
+
症状
+
New-API、RAGFlow、文件服务或 SSE 失败,业务无法形成真实闭环。
+
优先定位层
+
Adapter。
+
检查项
+
超时、认证、错误分类、重试、补偿、外部服务版本和凭据。
+
下一步
+
先诊断 root cause;外部服务不可用时相关 API 只能标 blocked。
+
证据
+
adapter client 日志、配置、集成测试、外部调用记录。
+
+
+ +
+

响应字段不对

+
+
症状
+
返回外壳或 data 字段与 OpenAPI response 不一致。
+
优先定位层
+
DTO / CommonResult。
+
检查项
+
code/data/msg、assembler、OpenAPI schema、禁止 message 替代 msg
+
下一步
+
先固定 CommonResult 外壳,再逐项对齐 data 内的业务 DTO。
+
证据
+
OpenAPI base schema、Controller 返回类型、契约测试、响应快照。
+
+
+
+

+ 来源:content/troubleshooting.md;上游:explainer design section 7.6。 +

+
+ +
+
+

Maintenance

+

HTML 是权威阅读面,Markdown 是非权威维护备注

+
+

+ 后续规格、合同或代码变化时,先核对上游证据,再同步本页。content/*.md 用来提示维护边界, + 不能覆盖 OpenAPI、P1R spec 或当前代码事实。 +

+
+
architecturecontent/architecture.mdP1R real API design section 7
+
openapicontent/openapi-paths.mddocs/api-contracts/**/openapi.yaml
+
lifecyclecontent/request-lifecycle.mdP1R real API design section 5 and 7
+
ownerscontent/domain-owners.mdP1R real API design section 5.3
+
stagescontent/p1r-stages.mdP1R real API design section 10
+
troubleshootingcontent/troubleshooting.mdexplainer design section 7.6
+
+

+ 来源:content/sources.md;上游:explainer maintenance rules。 +

+
+
+
+ + + +