docs(explainer): 完成 muse-cloud 架构讲解包

This commit is contained in:
zizi 2026-05-25 23:16:40 +08:00
parent f4c3cde515
commit e068e0efff
11 changed files with 1651 additions and 0 deletions

View File

@ -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<T>` 形态JSON 字段是 `code/data/msg`
## 不能算 P1R 完成的形态
- catch-all 合同入口兜底。
- 通用持久化响应替代领域服务。
- placeholder SSE 或固定事件。
- 空列表替代真实读模型。
- accepted task 没有进入成功、失败、取消或超时终态。
这些形态可以作为过渡保护网或基线识别结果,但不能作为真实业务 API 完成证据。

View File

@ -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);
}
})();

View File

@ -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;
}
}

View File

@ -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 返回类型、响应断言。

View File

@ -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、任务事件、跨端事件 | 不返回固定假事件 |

View File

@ -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 标为完成。

View File

@ -0,0 +1,14 @@
# P1R 阶段维护备注
P1R 采用总 spec、阶段 spec、阶段 plan 的执行模型。HTML 主入口需要把每个阶段的负责领域、真实能力、假完成形态和验收证据讲清楚。
1. P1R-0API 基线门禁。
2. P1R-1Content Real API。
3. P1R-2Meta Real API。
4. P1R-3Account Real API。
5. P1R-4AI Real API。
6. P1R-5Knowledge Real API。
7. P1R-6Market Real API。
8. P1R-7End-to-End Acceptance。
维护重点P1R-0 不输出 completed后续阶段必须用真实业务行为、真实外部闭环和真实验收证据证明完成。

View File

@ -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 / AdapterMapper、外部 adapter、outbox、任务执行器。
7. Assembler读模型和 OpenAPI response DTO。
8. CommonResult对外 JSON 字段 `code/data/msg`
每个节点需要给出常见问题、定位证据和下一步,方便初级工程师按层排障。

View File

@ -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 区块、维护备注和上游证据对应起来。

View File

@ -0,0 +1,14 @@
# 排障维护备注
HTML 主入口至少覆盖以下八类症状,并给出优先定位层、检查项、下一步和证据。
1. 404。
2. 400。
3. 401/403。
4. 409。
5. 空列表但应有数据。
6. accepted 但不终态。
7. 外部服务失败。
8. 响应字段不对。
排障规则:先定位请求落在哪一层,再找能证明该层事实的文件、日志、测试或运行结果;没有证据时不宣称完成。

View File

@ -0,0 +1,785 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>muse-cloud 架构讲解包</title>
<link rel="stylesheet" href="assets/styles.css" />
</head>
<body>
<header class="site-header">
<div>
<p class="eyebrow">muse-cloud P1R Explainer</p>
<h1>muse-cloud 架构讲解包</h1>
<p class="lead">
面向初级后端工程师的主阅读入口:从真实 API 标准、OpenAPI 合同路径、请求生命周期、领域 owner、
P1R 阶段地图到排障流程,一次读完整。
</p>
</div>
<div class="header-facts" aria-label="关键口径">
<span>HTML authoritative</span>
<span>X-API-Version: 1</span>
<span>code/data/msg</span>
</div>
</header>
<div class="page-shell">
<nav class="side-nav" aria-label="讲解目录">
<a href="#overview">阅读口径</a>
<a href="#architecture">目标架构</a>
<a href="#openapi">OpenAPI 路径</a>
<a href="#lifecycle">请求生命周期</a>
<a href="#owners">领域 owner</a>
<a href="#stages">P1R 阶段</a>
<a href="#troubleshooting">快速排障</a>
<a href="#maintenance">维护规则</a>
</nav>
<aside class="quick-panel" aria-labelledby="quick-triage-title">
<div class="quick-panel-inner">
<p class="eyebrow">Quick triage</p>
<h2 id="quick-triage-title">先按症状缩小层级</h2>
<p>
选择症状会只显示匹配诊断项。移动端这里会出现在正文前,便于现场排障时先定位。
</p>
<div class="triage-actions" role="group" aria-label="诊断筛选">
<button type="button" data-filter="404">404</button>
<button type="button" data-filter="400">400</button>
<button type="button" data-filter="401-403">401/403</button>
<button type="button" data-filter="409">409</button>
<button type="button" data-filter="empty-list">空列表</button>
<button type="button" data-filter="accepted">accepted</button>
<button type="button" data-filter="adapter-failure">外部失败</button>
<button type="button" data-filter="response-shape">响应字段</button>
<button class="clear-filter" type="button" data-clear-filter>清除筛选</button>
</div>
<p class="quick-status" data-filter-status aria-live="polite">当前显示全部诊断项。</p>
</div>
</aside>
<main class="content">
<section
id="overview"
class="manual-section"
data-section
data-source="content/sources.md"
data-upstream="P1R real API design section 5 and README"
>
<div class="section-heading">
<p class="eyebrow">阅读口径</p>
<h2>这是讲解包,不是 API 完成证明</h2>
</div>
<p>
本页解释 P1R 阶段 muse-cloud 应如何落到真实后端架构。它可以帮助你读代码、查合同、定位问题,
但不能替代 API 完成矩阵、测试报告、真实环境验收记录。
</p>
<div class="warning-band" role="note">
<strong>以下形态不能算 P1R 完成:</strong>
catch-all 合同入口兜底、通用持久化响应替代领域服务、placeholder SSE 或固定事件、
空列表替代真实读模型、accepted task 没有进入成功/失败/取消/超时终态。
</div>
<dl class="definition-grid">
<div>
<dt>已验证事实</dt>
<dd>来自 OpenAPI 合同、当前 Java 类、规格文档或校验命令。</dd>
</div>
<div>
<dt>推断</dt>
<dd>基于架构规则给出的合理排障方向,仍需用代码、日志或测试证明。</dd>
</div>
<div>
<dt>维护备注</dt>
<dd><code>content/*.md</code> 只用于维护对照,不是权威正文。</dd>
</div>
</dl>
<p class="source-note">
来源:<code>content/sources.md</code>上游P1R real API design section 5 and README。
</p>
</section>
<section
id="architecture"
class="manual-section"
data-section
data-source="content/architecture.md"
data-upstream="P1R real API design section 7"
>
<div class="section-heading">
<p class="eyebrow">Architecture</p>
<h2>系统分层先看责任边界</h2>
</div>
<p>
P1R 的真实 API 不靠 Controller 直接拼响应,也不靠通用持久化层伪造完成。每一层都有明确职责、
禁止事项和定位证据。
</p>
<div class="architecture-flow" aria-label="muse-cloud 目标架构流向">
<span>Controller</span>
<span>Application Service</span>
<span>Domain</span>
<span>Persistence / Adapter</span>
<span>Assembler</span>
<span>CommonResult</span>
</div>
<div class="architecture-grid">
<article class="architecture-layer">
<h3>Controller</h3>
<dl>
<dt>负责什么</dt>
<dd>鉴权入口、参数校验、<code>X-API-Version: 1</code> 校验、DTO 转换和统一响应封装。</dd>
<dt>不应负责什么</dt>
<dd>业务规则、数据库访问、外部服务调用、手写业务 JSON。</dd>
<dt>定位证据</dt>
<dd>Controller 路由映射、OpenAPI operationId、版本头参数、响应类型断言。</dd>
</dl>
</article>
<article class="architecture-layer">
<h3>Application Service</h3>
<dl>
<dt>负责什么</dt>
<dd>用例编排、事务、幂等、权限摘要、跨模块 facade、任务创建、错误归一和响应组装。</dd>
<dt>不应负责什么</dt>
<dd>绕过领域 owner 直接写其他模块事实,或把通用 operation record 当业务结果。</dd>
<dt>定位证据</dt>
<dd><code>ContentAppService</code>、实现类、事务边界、幂等记录和用例测试。</dd>
</dl>
</article>
<article class="architecture-layer">
<h3>Domain</h3>
<dl>
<dt>负责什么</dt>
<dd>聚合状态机、不变式、版本冲突、授权快照消费规则和状态变化决策。</dd>
<dt>不应负责什么</dt>
<dd>依赖 Controller DTO、Yudao Web DTO 或外部 API DTO。</dd>
<dt>定位证据</dt>
<dd>领域服务、聚合规则、状态机测试、revision / expectedVersion 校验。</dd>
</dl>
</article>
<article class="architecture-layer">
<h3>Query / Assembler</h3>
<dl>
<dt>负责什么</dt>
<dd>读模型查询、DTO / VO 组装、OpenAPI response 对齐。</dd>
<dt>不应负责什么</dt>
<dd>把数据库表行、operation record 或 workflow task 原样暴露给前端。</dd>
<dt>定位证据</dt>
<dd>Query Service、Assembler、OpenAPI schema、响应快照或契约测试。</dd>
</dl>
</article>
<article class="architecture-layer">
<h3>Persistence Mapper</h3>
<dl>
<dt>负责什么</dt>
<dd>MyBatis Mapper、事务内持久化、唯一约束、owner / tenant 条件和查询 SQL。</dd>
<dt>不应负责什么</dt>
<dd>业务状态机决策、外部服务语义或跨 owner 的业务写入。</dd>
<dt>定位证据</dt>
<dd>Mapper、SQL、Flyway、唯一索引、owner 和 tenant 条件。</dd>
</dl>
</article>
<article class="architecture-layer">
<h3>External Adapter</h3>
<dl>
<dt>负责什么</dt>
<dd>New-API、RAGFlow、文件服务、SSE、任务执行器等外部边界。</dd>
<dt>不应负责什么</dt>
<dd>伪造外部成功、吞掉可恢复失败、隐藏超时和重试语义。</dd>
<dt>定位证据</dt>
<dd>adapter client、超时配置、错误映射、重试策略、集成测试。</dd>
</dl>
</article>
<article class="architecture-layer">
<h3>CommonResult</h3>
<dl>
<dt>负责什么</dt>
<dd>对外统一响应形态JSON 字段为 <code>code/data/msg</code></dd>
<dt>不应负责什么</dt>
<dd><code>message</code> 替代 <code>msg</code>,或用通用 JSON 行替代合同 DTO。</dd>
<dt>定位证据</dt>
<dd>OpenAPI base schema、Controller 返回类型、响应断言。</dd>
</dl>
</article>
</div>
<p class="source-note">
来源:<code>content/architecture.md</code>上游P1R real API design section 7。
</p>
</section>
<section
id="openapi"
class="manual-section"
data-section
data-source="content/openapi-paths.md"
data-upstream="docs/api-contracts/**/openapi.yaml"
>
<div class="section-heading">
<p class="eyebrow">OpenAPI</p>
<h2>合同 paths 是完整外部路径</h2>
</div>
<p>
<code>docs/api-contracts/**/openapi.yaml</code><code>paths</code> 键已经是完整外部路径。
讲解、排障和完成矩阵都应直接引用这些键,不应再描述为运行时追加入口前缀。
</p>
<div class="path-proof">
<div>
<span class="step-label">合同文件</span>
<code>docs/api-contracts/content/openapi.yaml</code>
</div>
<div>
<span class="step-label">完整路径</span>
<code>/app-api/muse/works</code>
<button type="button" class="copy-button" data-copy="/app-api/muse/works">复制</button>
</div>
<div>
<span class="step-label">operationId</span>
<code>listWorks</code>
<button type="button" class="copy-button" data-copy="listWorks">复制</button>
</div>
<div>
<span class="step-label">代码入口</span>
<code>AppContentController</code>
<code>ContentAppService</code>
</div>
</div>
<dl class="definition-grid">
<div>
<dt>入口侧</dt>
<dd><code>/app-api/muse/**</code> 是 app 入口,<code>/admin-api/muse/**</code> 是 admin 入口。</dd>
</div>
<div>
<dt>领域归属</dt>
<dd>app/admin 不决定 owner业务事实只能由 Content、Meta、Account 等领域模块拥有。</dd>
</div>
<div>
<dt>完成判断</dt>
<dd>operationId、Controller、Application Service、领域事实、测试和验收证据要一起看。</dd>
</div>
</dl>
<p class="source-note">
来源:<code>content/openapi-paths.md</code>;上游:<code>docs/api-contracts/content/openapi.yaml</code>
</p>
</section>
<section
id="lifecycle"
class="manual-section"
data-section
data-source="content/request-lifecycle.md"
data-upstream="P1R real API design section 5 and 7"
>
<div class="section-heading">
<p class="eyebrow">Lifecycle</p>
<h2>一次请求从入口走到 CommonResult</h2>
</div>
<div class="lifecycle-grid">
<article class="lifecycle-node">
<h3>HTTP Request</h3>
<dl>
<dt>常见问题</dt>
<dd>URL 写错、HTTP method 不匹配、app/admin 入口混用。</dd>
<dt>定位证据</dt>
<dd>OpenAPI path、Controller mapping、网关访问日志。</dd>
<dt>下一步</dt>
<dd>先核对合同完整路径,再看请求是否到达目标 Controller。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>Version/Auth</h3>
<dl>
<dt>常见问题</dt>
<dd>缺少 <code>X-API-Version: 1</code>、登录态失效、RBAC 或 owner 拒绝。</dd>
<dt>定位证据</dt>
<dd>请求 header、Security 配置、权限日志、owner / tenant 条件。</dd>
<dt>下一步</dt>
<dd>补齐版本头后复测,再区分 401 登录问题和 403 权限问题。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>Controller DTO</h3>
<dl>
<dt>常见问题</dt>
<dd>必填字段、枚举、分页参数或 schema 不符合合同。</dd>
<dt>定位证据</dt>
<dd>Request DTO、Bean Validation、OpenAPI requestBody 和 parameters。</dd>
<dt>下一步</dt>
<dd>用合同样例构造最小请求,确认错误来自 DTO 而不是业务层。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>Application</h3>
<dl>
<dt>常见问题</dt>
<dd>事务边界缺失、幂等键重复、跨模块 facade 使用不当。</dd>
<dt>定位证据</dt>
<dd>Application Service 方法、事务注解、commandId 记录、用例测试。</dd>
<dt>下一步</dt>
<dd>确认用例是否进入专用服务,避免落到通用占位处理。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>Domain</h3>
<dl>
<dt>常见问题</dt>
<dd>revision、expectedVersion、expectedStatus 或状态机前置条件冲突。</dd>
<dt>定位证据</dt>
<dd>领域规则、聚合状态、冲突错误码、状态机测试。</dd>
<dt>下一步</dt>
<dd>读取当前业务事实和版本,再判断应重试、刷新还是返回 409。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>DB/Adapter</h3>
<dl>
<dt>常见问题</dt>
<dd>Mapper 条件漏 owner、外部服务超时、任务没有被执行器消费。</dd>
<dt>定位证据</dt>
<dd>SQL、Flyway、adapter 日志、outbox、workflow task。</dd>
<dt>下一步</dt>
<dd>区分数据库事实不存在、查询条件过滤掉、外部 adapter 失败三类问题。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>Assembler</h3>
<dl>
<dt>常见问题</dt>
<dd>DTO 字段缺失、读模型和 OpenAPI response 不一致。</dd>
<dt>定位证据</dt>
<dd>Assembler、Query Service、OpenAPI response schema、契约测试。</dd>
<dt>下一步</dt>
<dd>不要直接改表结构响应,先对齐读模型和合同 DTO。</dd>
</dl>
</article>
<article class="lifecycle-node">
<h3>CommonResult</h3>
<dl>
<dt>常见问题</dt>
<dd>返回 <code>message</code>、遗漏 <code>data</code>、错误码和业务错误混乱。</dd>
<dt>定位证据</dt>
<dd>Controller 返回类型、OpenAPI base schema、响应断言。</dd>
<dt>下一步</dt>
<dd>保持 <code>code/data/msg</code> 外壳稳定,再定位 data 内部 DTO。</dd>
</dl>
</article>
</div>
<p class="source-note">
来源:<code>content/request-lifecycle.md</code>上游P1R real API design section 5 and 7。
</p>
</section>
<section
id="owners"
class="manual-section"
data-section
data-source="content/domain-owners.md"
data-upstream="P1R real API design section 5.3"
>
<div class="section-heading">
<p class="eyebrow">Owner Boundaries</p>
<h2>app/admin 不是领域 owner</h2>
</div>
<p>
入口侧只表示调用场景。业务事实的写入、状态推进和验收证据必须回到所属领域模块。
</p>
<div class="owner-table-wrap" aria-label="领域 owner 表格">
<table class="owner-table">
<thead>
<tr>
<th>领域</th>
<th>负责事实</th>
<th>典型依赖</th>
<th>不应越界</th>
</tr>
</thead>
<tbody>
<tr>
<th>Content</th>
<td>作品、章节、Block、导入导出、Suggestion Merge</td>
<td>File、AI suggestion</td>
<td>不直接拥有市场授权</td>
</tr>
<tr>
<th>Meta</th>
<td>MetaSchema、保护节点、功能链治理</td>
<td>PostgreSQL、审计</td>
<td>不把保护节点降级为用户槽位</td>
</tr>
<tr>
<th>Account</th>
<td>用户资料、权益、配额、用量、New-API 归因</td>
<td>New-API、账本</td>
<td>不允许请求体伪造归因</td>
</tr>
<tr>
<th>AI</th>
<td>Prompt、Agent、Tool Grant、任务、质量治理</td>
<td>New-API、SSE</td>
<td>不伪造 AI 调用成功</td>
</tr>
<tr>
<th>Knowledge</th>
<td>知识库、文档、切片、索引、图谱</td>
<td>RAGFlow、File</td>
<td>不用空列表代替索引结果</td>
</tr>
<tr>
<th>Market</th>
<td>资产、授权、安装、发布、申诉、handoff</td>
<td>Account、授权快照</td>
<td>不写目标领域绑定事实</td>
</tr>
<tr>
<th>Events</th>
<td>SSE、任务事件、跨端事件</td>
<td>Redis、SSE</td>
<td>不返回固定假事件</td>
</tr>
</tbody>
</table>
</div>
<div class="owner-cards" aria-label="移动端领域 owner 摘要">
<article>
<h3>Content</h3>
<p>作品、章节、Block、导入导出、Suggestion Merge不直接拥有市场授权。</p>
</article>
<article>
<h3>Meta</h3>
<p>MetaSchema、保护节点、功能链治理保护节点不能被用户槽位覆盖。</p>
</article>
<article>
<h3>Account</h3>
<p>用户资料、权益、配额、用量、New-API 归因;不信任请求体伪造归因。</p>
</article>
<article>
<h3>AI</h3>
<p>Prompt、Agent、Tool Grant、任务、质量治理不伪造 AI 调用成功。</p>
</article>
<article>
<h3>Knowledge</h3>
<p>知识库、文档、切片、索引、图谱;不用空列表代替索引结果。</p>
</article>
<article>
<h3>Market</h3>
<p>资产、授权、安装、发布、申诉、handoff不写目标领域绑定事实。</p>
</article>
<article>
<h3>Events</h3>
<p>SSE、任务事件、跨端事件不返回固定假事件。</p>
</article>
</div>
<p class="source-note">
来源:<code>content/domain-owners.md</code>上游P1R real API design section 5.3。
</p>
</section>
<section
id="stages"
class="manual-section"
data-section
data-source="content/p1r-stages.md"
data-upstream="P1R real API design section 10"
>
<div class="section-heading">
<p class="eyebrow">P1R Map</p>
<h2>阶段地图按证据推进,不按感觉打勾</h2>
</div>
<div class="stage-grid">
<article class="stage-card">
<h3>P1R-0 Baseline Gate</h3>
<dl>
<dt>负责领域</dt>
<dd>全量 OpenAPI operation 清点和完成矩阵。</dd>
<dt>真实能力</dt>
<dd>标注 owner、side、method、路径、写命令、外部依赖、异步属性和当前实现状态。</dd>
<dt>假完成形态</dt>
<dd>把基线审计结果直接标为 completed。</dd>
<dt>验收证据</dt>
<dd>机器可读矩阵、人类可审报告、可复跑检查命令。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-1 Content Real API</h3>
<dl>
<dt>负责领域</dt>
<dd>作品、章节、Block、导入导出、动态字段、来源归因、Suggestion Merge。</dd>
<dt>真实能力</dt>
<dd>核心内容 API 回到 OpenAPI DTO文件导入和导出产生真实产物。</dd>
<dt>假完成形态</dt>
<dd>空作品列表、固定详情、通用持久化行或未消费的 accepted task。</dd>
<dt>验收证据</dt>
<dd>Controller、ContentAppService、Mapper、文件任务、契约测试和真实环境冒烟。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-2 Meta Real API</h3>
<dl>
<dt>负责领域</dt>
<dd>MetaSchema、保护节点、功能链治理。</dd>
<dt>真实能力</dt>
<dd>版本、草稿、验证、影响预览、发布、激活、回滚、废弃和灰度规则。</dd>
<dt>假完成形态</dt>
<dd>保护节点被当作普通用户槽位,或激活版本没有唯一约束。</dd>
<dt>验收证据</dt>
<dd>expectedVersion、validationResultId、impactPreviewId、审计和状态机测试。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-3 Account Real API</h3>
<dl>
<dt>负责领域</dt>
<dd>用户资料、权益、配额、用量、购买/授权/发布记录聚合和 New-API 归因。</dd>
<dt>真实能力</dt>
<dd>绑定重验、调用归因、用量查询、导出下载和安全事件落到真实事实。</dd>
<dt>假完成形态</dt>
<dd>请求体任意声明归因,或用余额样例替代账本事实。</dd>
<dt>验收证据</dt>
<dd>真实 correlation/call 记录、owner 校验、导出凭证、账户聚合测试。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-4 AI Real API</h3>
<dl>
<dt>负责领域</dt>
<dd>Prompt、Agent、Tool Grant、任务、质量治理、访问日志和审计。</dd>
<dt>真实能力</dt>
<dd>AI task 调用真实 New-API任务支持成功、失败、取消、重试SSE 推送真实事件。</dd>
<dt>假完成形态</dt>
<dd>固定 AI 文本、placeholder SSE、runtime 自授权。</dd>
<dt>验收证据</dt>
<dd>New-API 调用记录、SSE/轮询终态、授权隔离测试、质量治理审计。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-5 Knowledge Real API</h3>
<dl>
<dt>负责领域</dt>
<dd>全局知识库、用户知识库、文档版本、切片、索引、图谱和知识草稿。</dd>
<dt>真实能力</dt>
<dd>文档进入 RAGFlow / 知识引擎,检索结果回到 Knowledge API。</dd>
<dt>假完成形态</dt>
<dd>上传后只存文件名,检索永远空列表,知识草稿不写 Canonical 事实。</dd>
<dt>验收证据</dt>
<dd>入库、切片、索引、检索、草稿确认和授权快照消费记录。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-6 Market Real API</h3>
<dl>
<dt>负责领域</dt>
<dd>资产、分类、推荐、收藏、购买、安装、发布、申诉和 handoff。</dd>
<dt>真实能力</dt>
<dd>购买和安装写授权、安装记录和账户聚合handoff 只交付来源授权摘要和 token。</dd>
<dt>假完成形态</dt>
<dd>Market 直接写目标领域绑定事实,或未完成 ADR 就开放作品资产高阶模式。</dd>
<dt>验收证据</dt>
<dd>授权快照、安装记录、发布审核、申诉审计、目标 owner 消费 precheck。</dd>
</dl>
</article>
<article class="stage-card">
<h3>P1R-7 End-to-End Acceptance</h3>
<dl>
<dt>负责领域</dt>
<dd>跨 Content、AI、Knowledge、Market、Account、Events 的真实环境闭环。</dd>
<dt>真实能力</dt>
<dd>创建作品、AI suggestion、知识索引、市场购买安装、账户用量和治理审计形成链路。</dd>
<dt>假完成形态</dt>
<dd>只跑单元测试或只看接口返回 200没有真实外部依赖和业务终态。</dd>
<dt>验收证据</dt>
<dd>真实 PG/Redis 启动、New-API 调用、RAG 检索、SSE/轮询终态、端到端报告。</dd>
</dl>
</article>
</div>
<p class="source-note">
来源:<code>content/p1r-stages.md</code>上游P1R real API design section 10。
</p>
</section>
<section
id="troubleshooting"
class="manual-section"
data-section
data-source="content/troubleshooting.md"
data-upstream="explainer design section 7.6"
>
<div class="section-heading">
<p class="eyebrow">Troubleshooting</p>
<h2>按症状进入排障,不跳层猜原因</h2>
</div>
<div class="diagnostic-list" data-diagnostic-list>
<article class="diagnostic-row" data-symptom="404">
<h3>404</h3>
<dl>
<dt>症状</dt>
<dd>请求没有命中目标 API或被 catch-all / 网关兜底吞掉。</dd>
<dt>优先定位层</dt>
<dd>OpenAPI 路径 / Controller。</dd>
<dt>检查项</dt>
<dd>合同完整路径、HTTP method、<code>/muse</code>、Controller mapping、路由注册。</dd>
<dt>下一步</dt>
<dd>先用 <code>/app-api/muse/works</code> 这类合同完整路径复测,再查 Controller。</dd>
<dt>证据</dt>
<dd><code>docs/api-contracts/**/openapi.yaml</code>、Controller 类、访问日志。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="400">
<h3>400</h3>
<dl>
<dt>症状</dt>
<dd>参数校验失败、版本头缺失、请求体不符合 schema。</dd>
<dt>优先定位层</dt>
<dd>Header / DTO。</dd>
<dt>检查项</dt>
<dd><code>X-API-Version: 1</code>、必填字段、枚举、分页参数、Bean Validation。</dd>
<dt>下一步</dt>
<dd>构造合同最小请求,确认是入口校验失败还是业务拒绝。</dd>
<dt>证据</dt>
<dd>OpenAPI parameters、requestBody、Request DTO、校验错误日志。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="401-403">
<h3>401/403</h3>
<dl>
<dt>症状</dt>
<dd>未登录、token 失效、权限不足、owner / tenant 不匹配。</dd>
<dt>优先定位层</dt>
<dd>Security。</dd>
<dt>检查项</dt>
<dd>登录态、RBAC、owner、tenant、来源授权、高危动作权限。</dd>
<dt>下一步</dt>
<dd>先区分认证失败和授权失败,再看后端条件是否只靠前端隐藏入口。</dd>
<dt>证据</dt>
<dd>Security 配置、权限日志、SQL owner 条件、审计记录。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="409">
<h3>409</h3>
<dl>
<dt>症状</dt>
<dd>重复命令、版本冲突、状态机前置条件不满足。</dd>
<dt>优先定位层</dt>
<dd>Domain / Idempotency。</dd>
<dt>检查项</dt>
<dd><code>commandId</code>、revision、expectedVersion、expectedStatus、状态机。</dd>
<dt>下一步</dt>
<dd>读取当前业务事实和幂等记录,判断应返回历史结果还是提示刷新。</dd>
<dt>证据</dt>
<dd>领域状态、幂等表、唯一索引、状态机测试。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="empty-list">
<h3>空列表但应有数据</h3>
<dl>
<dt>症状</dt>
<dd>接口返回成功但列表为空,实际应存在业务事实。</dd>
<dt>优先定位层</dt>
<dd>Query / Persistence。</dd>
<dt>检查项</dt>
<dd>owner 条件、tenant 条件、读模型刷新、mapper SQL、分页参数。</dd>
<dt>下一步</dt>
<dd>用同一用户和 tenant 查表,再确认 assembler 是否过滤或映射错误。</dd>
<dt>证据</dt>
<dd>数据库事实、Mapper SQL、Query Service、响应 DTO。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="accepted">
<h3>accepted 但不终态</h3>
<dl>
<dt>症状</dt>
<dd>创建任务返回 accepted但后续查询一直没有成功、失败、取消或超时终态。</dd>
<dt>优先定位层</dt>
<dd>Job / Adapter。</dd>
<dt>检查项</dt>
<dd>workflow task、outbox、执行器、外部服务回调、重试和补偿。</dd>
<dt>下一步</dt>
<dd>跟踪任务状态变化和执行器日志,不把 accepted 当完成。</dd>
<dt>证据</dt>
<dd>workflow task 记录、outbox、adapter 日志、任务状态测试。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="adapter-failure">
<h3>外部服务失败</h3>
<dl>
<dt>症状</dt>
<dd>New-API、RAGFlow、文件服务或 SSE 失败,业务无法形成真实闭环。</dd>
<dt>优先定位层</dt>
<dd>Adapter。</dd>
<dt>检查项</dt>
<dd>超时、认证、错误分类、重试、补偿、外部服务版本和凭据。</dd>
<dt>下一步</dt>
<dd>先诊断 root cause外部服务不可用时相关 API 只能标 blocked。</dd>
<dt>证据</dt>
<dd>adapter client 日志、配置、集成测试、外部调用记录。</dd>
</dl>
</article>
<article class="diagnostic-row" data-symptom="response-shape">
<h3>响应字段不对</h3>
<dl>
<dt>症状</dt>
<dd>返回外壳或 data 字段与 OpenAPI response 不一致。</dd>
<dt>优先定位层</dt>
<dd>DTO / CommonResult。</dd>
<dt>检查项</dt>
<dd><code>code/data/msg</code>、assembler、OpenAPI schema、禁止 <code>message</code> 替代 <code>msg</code></dd>
<dt>下一步</dt>
<dd>先固定 CommonResult 外壳,再逐项对齐 data 内的业务 DTO。</dd>
<dt>证据</dt>
<dd>OpenAPI base schema、Controller 返回类型、契约测试、响应快照。</dd>
</dl>
</article>
</div>
<p class="source-note">
来源:<code>content/troubleshooting.md</code>上游explainer design section 7.6。
</p>
</section>
<section
id="maintenance"
class="manual-section"
data-section
data-source="content/sources.md"
data-upstream="explainer maintenance rules"
>
<div class="section-heading">
<p class="eyebrow">Maintenance</p>
<h2>HTML 是权威阅读面Markdown 是非权威维护备注</h2>
</div>
<p>
后续规格、合同或代码变化时,先核对上游证据,再同步本页。<code>content/*.md</code> 用来提示维护边界,
不能覆盖 OpenAPI、P1R spec 或当前代码事实。
</p>
<div class="source-map">
<div><strong>architecture</strong><span>content/architecture.md</span><span>P1R real API design section 7</span></div>
<div><strong>openapi</strong><span>content/openapi-paths.md</span><span>docs/api-contracts/**/openapi.yaml</span></div>
<div><strong>lifecycle</strong><span>content/request-lifecycle.md</span><span>P1R real API design section 5 and 7</span></div>
<div><strong>owners</strong><span>content/domain-owners.md</span><span>P1R real API design section 5.3</span></div>
<div><strong>stages</strong><span>content/p1r-stages.md</span><span>P1R real API design section 10</span></div>
<div><strong>troubleshooting</strong><span>content/troubleshooting.md</span><span>explainer design section 7.6</span></div>
</div>
<p class="source-note">
来源:<code>content/sources.md</code>上游explainer maintenance rules。
</p>
</section>
</main>
</div>
<script src="assets/app.js" defer></script>
</body>
</html>