## 为什么做这一步 权威文档 74 份此前**只在本机**,评审者 clone 分支后看不到任何设计文档;而仓库里那两份同名目录 是 **2026-09-16 之前的过期副本,连文件名都是旧的**(无体系编号)。本次按「**权威覆盖过期**」入库。 ## 入库内容 | 目录 | 文件数 | 体积 | 说明 | |---|---|---|---| | `客服agent/` | 24 | 0.77 MB | `D2.1`~`D2.6` 对外交付四件套 + 演示脚本/答辩报告 + `_build` 构建工具 | | `开发文档/` | 50 | 2.16 MB | `D1.x` 索引与决策、`D3.x` 方案、`D4.x` 清除与重构留痕、`D5.x` 业务流程、`D6.x` 业务事实基座、`D7.x` 交付物、`D8.x` 规范 | **旧的过期副本整体移除**(`客服Agent执行Todolist.md` → `D2.1-客服Agent执行Todolist.md` 之类 的改名 + 新增 `D2.5`/`D2.6`),入库后目录内容与权威副本**逐文件一致(零差异,已复核)**。 ## 入库前的安全扫描(必须留痕) - 扫描规则:`sk-` 类密钥 / `Bearer` 长串 / `password=`、`api_key=` 赋值 / 会话中出现过的两把明文 key 片段。 - 结论:**真实密钥只出现在 `.env`**(已被 `.gitignore` 命中,未入库);`.env.example` 与 `config/risk.env.example` 只有**空占位**。 - 文档内唯一命中是 `D3.1` 里一处**截断的示例 JWT**(`Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`), 末尾带省略号,是接口文档的示意值,**不是可用凭据**。
83 lines
2.4 KiB
HTML
83 lines
2.4 KiB
HTML
</div><!-- end doc-container -->
|
|
</main>
|
|
|
|
<script>
|
|
mermaid.initialize({
|
|
startOnLoad: true,
|
|
theme: 'base',
|
|
themeVariables: {
|
|
primaryColor: '#e3f2fd',
|
|
primaryTextColor: '#1e293b',
|
|
primaryBorderColor: '#1976d2',
|
|
lineColor: '#64748b',
|
|
secondaryColor: '#fff3e0',
|
|
tertiaryColor: '#f4f1eb',
|
|
fontFamily: 'Inter, sans-serif',
|
|
fontSize: '14px',
|
|
},
|
|
flowchart: { curve: 'basis', padding: 15 },
|
|
securityLevel: 'loose',
|
|
});
|
|
|
|
// ── Progress bar ──
|
|
const bar = document.getElementById('progress-bar');
|
|
function updateProgress() {
|
|
const h = document.documentElement;
|
|
const pct = ((h.scrollTop) / (h.scrollHeight - h.clientHeight)) * 100;
|
|
bar.style.width = Math.min(100, pct) + '%';
|
|
}
|
|
window.addEventListener('scroll', updateProgress);
|
|
|
|
// ── Topbar title ──
|
|
const topbarTitle = document.getElementById('topbar-title');
|
|
const headings = document.querySelectorAll('.doc-container h1[id], .doc-container h2[id], .doc-container h3[id], .doc-container h4[id]');
|
|
function updateTopbar() {
|
|
let current = '';
|
|
headings.forEach(h => {
|
|
if (h.getBoundingClientRect().top <= 120) current = h.textContent;
|
|
});
|
|
if (current) topbarTitle.textContent = current;
|
|
}
|
|
window.addEventListener('scroll', updateTopbar);
|
|
|
|
// ── TOC active state ──
|
|
const tocLinks = document.querySelectorAll('.toc-item');
|
|
function updateToc() {
|
|
let activeId = '';
|
|
headings.forEach(h => {
|
|
if (h.getBoundingClientRect().top <= 120) activeId = h.id;
|
|
});
|
|
tocLinks.forEach(link => {
|
|
link.classList.toggle('active', link.getAttribute('href') === '#' + activeId);
|
|
});
|
|
}
|
|
window.addEventListener('scroll', updateToc);
|
|
|
|
// ── Collapsible TOC groups ──
|
|
document.querySelectorAll('.toc-toggle').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const kids = btn.nextElementSibling;
|
|
const arr = btn.querySelector('.arr');
|
|
btn.classList.toggle('active');
|
|
kids.classList.toggle('open');
|
|
arr.classList.toggle('open');
|
|
});
|
|
});
|
|
|
|
// ── TOC search ──
|
|
const tocSearch = document.getElementById('toc-search');
|
|
tocSearch.addEventListener('input', () => {
|
|
const q = tocSearch.value.toLowerCase();
|
|
document.querySelectorAll('.toc-item, .toc-toggle').forEach(item => {
|
|
item.style.display = (!q || item.textContent.toLowerCase().includes(q)) ? '' : 'none';
|
|
});
|
|
});
|
|
|
|
// ── Initial ──
|
|
updateProgress();
|
|
updateTopbar();
|
|
updateToc();
|
|
</script>
|
|
</body>
|
|
</html>
|