docs(explainer): 完成 muse-cloud 架构讲解包
This commit is contained in:
parent
f4c3cde515
commit
e068e0efff
36
docs/explainers/muse-cloud/README.md
Normal file
36
docs/explainers/muse-cloud/README.md
Normal 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 完成证据。
|
||||
112
docs/explainers/muse-cloud/assets/app.js
Normal file
112
docs/explainers/muse-cloud/assets/app.js
Normal 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);
|
||||
}
|
||||
})();
|
||||
587
docs/explainers/muse-cloud/assets/styles.css
Normal file
587
docs/explainers/muse-cloud/assets/styles.css
Normal 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;
|
||||
}
|
||||
}
|
||||
45
docs/explainers/muse-cloud/content/architecture.md
Normal file
45
docs/explainers/muse-cloud/content/architecture.md
Normal 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 返回类型、响应断言。
|
||||
13
docs/explainers/muse-cloud/content/domain-owners.md
Normal file
13
docs/explainers/muse-cloud/content/domain-owners.md
Normal 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、任务事件、跨端事件 | 不返回固定假事件 |
|
||||
17
docs/explainers/muse-cloud/content/openapi-paths.md
Normal file
17
docs/explainers/muse-cloud/content/openapi-paths.md
Normal 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 标为完成。
|
||||
14
docs/explainers/muse-cloud/content/p1r-stages.md
Normal file
14
docs/explainers/muse-cloud/content/p1r-stages.md
Normal file
@ -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;后续阶段必须用真实业务行为、真实外部闭环和真实验收证据证明完成。
|
||||
16
docs/explainers/muse-cloud/content/request-lifecycle.md
Normal file
16
docs/explainers/muse-cloud/content/request-lifecycle.md
Normal 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 / Adapter:Mapper、外部 adapter、outbox、任务执行器。
|
||||
7. Assembler:读模型和 OpenAPI response DTO。
|
||||
8. CommonResult:对外 JSON 字段 `code/data/msg`。
|
||||
|
||||
每个节点需要给出常见问题、定位证据和下一步,方便初级工程师按层排障。
|
||||
12
docs/explainers/muse-cloud/content/sources.md
Normal file
12
docs/explainers/muse-cloud/content/sources.md
Normal 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 区块、维护备注和上游证据对应起来。
|
||||
14
docs/explainers/muse-cloud/content/troubleshooting.md
Normal file
14
docs/explainers/muse-cloud/content/troubleshooting.md
Normal file
@ -0,0 +1,14 @@
|
||||
# 排障维护备注
|
||||
|
||||
HTML 主入口至少覆盖以下八类症状,并给出优先定位层、检查项、下一步和证据。
|
||||
|
||||
1. 404。
|
||||
2. 400。
|
||||
3. 401/403。
|
||||
4. 409。
|
||||
5. 空列表但应有数据。
|
||||
6. accepted 但不终态。
|
||||
7. 外部服务失败。
|
||||
8. 响应字段不对。
|
||||
|
||||
排障规则:先定位请求落在哪一层,再找能证明该层事实的文件、日志、测试或运行结果;没有证据时不宣称完成。
|
||||
785
docs/explainers/muse-cloud/index.html
Normal file
785
docs/explainers/muse-cloud/index.html
Normal 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>
|
||||
Loading…
x
Reference in New Issue
Block a user