Files
aurak/docs/system-overview-zh.html
Developer 0a9588abb7 feat: implement QuestionBank CRUD with pagination and template query
- Add pagination support to findAll (page, limit query params)
- Add findByTemplateId method to service
- Add GET /by-template/:templateId endpoint to controller
- Service already includes CRUD for QuestionBank and QuestionBankItem
2026-04-23 17:19:11 +08:00

780 lines
27 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Knowledge Base - 系统概览</title>
<style>
:root {
--primary: #6366f1;
--primary-light: #818cf8;
--primary-dark: #4f46e5;
--bg: #0f172a;
--bg-card: #1e293b;
--bg-code: #0d1117;
--text: #e2e8f0;
--text-muted: #94a3b8;
--border: #334155;
--accent-green: #34d399;
--accent-blue: #38bdf8;
--accent-orange: #fb923c;
--accent-pink: #f472b6;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.8;
}
.hero {
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 30%, #0f172a 70%, #0c0a09 100%);
padding: 80px 20px 60px;
text-align: center;
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle at 30% 50%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
radial-gradient(circle at 70% 50%, rgba(56, 189, 248, 0.1) 0%, transparent 50%);
animation: pulse 8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
.hero h1 {
font-size: 2.8rem;
font-weight: 800;
background: linear-gradient(135deg, #c7d2fe, #818cf8, #38bdf8);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
position: relative;
margin-bottom: 12px;
}
.hero .subtitle {
font-size: 1.15rem;
color: var(--text-muted);
position: relative;
max-width: 700px;
margin: 0 auto;
}
.badge-row {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
margin-top: 24px;
position: relative;
}
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 14px;
border-radius: 999px;
font-size: 0.82rem;
font-weight: 600;
border: 1px solid;
}
.badge.react { color: #61dafb; border-color: #61dafb33; background: #61dafb10; }
.badge.nest { color: #e0234e; border-color: #e0234e33; background: #e0234e10; }
.badge.rag { color: var(--accent-green); border-color: #34d39933; background: #34d39910; }
.badge.ts { color: #3178c6; border-color: #3178c633; background: #3178c610; }
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 20px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin: -40px auto 60px;
position: relative;
z-index: 1;
}
.feature-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 28px;
transition: transform 0.2s, border-color 0.2s;
}
.feature-card:hover {
transform: translateY(-3px);
border-color: var(--primary);
}
.feature-card .icon {
width: 44px;
height: 44px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3rem;
margin-bottom: 16px;
}
.feature-card h3 {
font-size: 1.05rem;
margin-bottom: 8px;
color: #f1f5f9;
}
.feature-card p {
font-size: 0.9rem;
color: var(--text-muted);
}
.icon-blue { background: #38bdf818; }
.icon-green { background: #34d39918; }
.icon-orange { background: #fb923c18; }
.icon-pink { background: #f472b618; }
.icon-purple { background: #a78bfa18; }
.icon-yellow { background: #fbbf2418; }
section {
margin-bottom: 56px;
}
.section-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 24px;
padding-bottom: 12px;
border-bottom: 2px solid var(--border);
display: flex;
align-items: center;
gap: 10px;
}
.section-title .num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 8px;
background: var(--primary);
font-size: 0.85rem;
font-weight: 700;
flex-shrink: 0;
}
/* Architecture Diagram */
.arch-diagram {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 32px;
overflow-x: auto;
}
.arch-row {
display: flex;
justify-content: center;
gap: 16px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.arch-box {
padding: 14px 22px;
border-radius: 10px;
font-size: 0.85rem;
font-weight: 600;
text-align: center;
min-width: 140px;
border: 1px solid;
}
.arch-box.frontend { background: #61dafb12; border-color: #61dafb33; color: #61dafb; }
.arch-box.backend { background: #e0234e12; border-color: #e0234e33; color: #e0234e; }
.arch-box.infra { background: #34d39912; border-color: #34d39933; color: #34d399; }
.arch-box.ai { background: #a78bfa12; border-color: #a78bfa33; color: #a78bfa; }
.arch-box.data { background: #fb923c12; border-color: #fb923c33; color: #fb923c; }
.arch-arrow {
text-align: center;
color: var(--text-muted);
font-size: 1.2rem;
margin-bottom: 16px;
}
.arch-label {
text-align: center;
font-size: 0.78rem;
color: var(--text-muted);
letter-spacing: 1.5px;
margin-bottom: 10px;
}
/* File Tree */
.file-tree {
background: var(--bg-code);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px 28px;
font-family: 'Cascadia Code', 'Fira Code', monospace;
font-size: 0.85rem;
line-height: 1.9;
overflow-x: auto;
}
.file-tree .dir { color: var(--accent-blue); font-weight: 600; }
.file-tree .comment { color: #64748b; font-style: italic; }
.file-tree .file { color: var(--text); }
/* Pipeline */
.pipeline {
display: flex;
align-items: center;
gap: 0;
flex-wrap: wrap;
justify-content: center;
margin: 20px 0;
}
.pipeline-step {
padding: 12px 20px;
border-radius: 10px;
font-size: 0.85rem;
font-weight: 600;
text-align: center;
border: 1px solid var(--border);
background: var(--bg-card);
}
.pipeline-arrow {
color: var(--text-muted);
font-size: 1.1rem;
padding: 0 6px;
}
.pipeline-step.active {
border-color: var(--primary);
background: #6366f118;
color: var(--primary-light);
}
/* Tables */
.info-table {
width: 100%;
border-collapse: collapse;
margin: 12px 0;
}
.info-table th, .info-table td {
padding: 10px 16px;
text-align: left;
border-bottom: 1px solid var(--border);
font-size: 0.9rem;
}
.info-table th {
color: var(--text-muted);
font-weight: 600;
font-size: 0.82rem;
letter-spacing: 0.5px;
}
.info-table .port {
font-family: 'Cascadia Code', monospace;
color: var(--accent-orange);
font-weight: 600;
}
.info-table .env-key {
font-family: 'Cascadia Code', monospace;
color: var(--accent-green);
font-size: 0.83rem;
}
/* Code Block */
.code-block {
background: var(--bg-code);
border: 1px solid var(--border);
border-radius: 10px;
padding: 18px 22px;
font-family: 'Cascadia Code', 'Fira Code', monospace;
font-size: 0.83rem;
line-height: 1.8;
overflow-x: auto;
margin: 12px 0;
}
.code-block .cmd { color: var(--accent-green); }
.code-block .comment { color: #64748b; }
/* Two Column Layout */
.two-col {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
@media (max-width: 768px) {
.two-col { grid-template-columns: 1fr; }
.hero h1 { font-size: 2rem; }
.features-grid { grid-template-columns: 1fr; }
}
.card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 14px;
padding: 24px;
}
.card h4 {
font-size: 1rem;
margin-bottom: 14px;
color: #f1f5f9;
}
.card ul {
list-style: none;
padding: 0;
}
.card ul li {
padding: 6px 0;
font-size: 0.88rem;
color: var(--text-muted);
display: flex;
align-items: flex-start;
gap: 8px;
}
.card ul li::before {
content: '';
color: var(--primary-light);
font-weight: 700;
flex-shrink: 0;
}
.step-list {
list-style: none;
padding: 0;
counter-reset: step;
}
.step-list li {
counter-increment: step;
padding: 8px 0;
font-size: 0.88rem;
color: var(--text-muted);
display: flex;
align-items: flex-start;
gap: 10px;
}
.step-list li::before {
content: counter(step);
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 6px;
background: var(--primary);
color: white;
font-size: 0.72rem;
font-weight: 700;
flex-shrink: 0;
}
.troubleshoot-item {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px 20px;
margin-bottom: 10px;
}
.troubleshoot-item strong {
color: #f1f5f9;
font-size: 0.92rem;
}
.troubleshoot-item p {
color: var(--text-muted);
font-size: 0.85rem;
margin-top: 4px;
}
footer {
text-align: center;
padding: 40px 20px;
color: var(--text-muted);
font-size: 0.82rem;
border-top: 1px solid var(--border);
}
</style>
</head>
<body>
<!-- Hero -->
<div class="hero">
<h1>Simple Knowledge Base</h1>
<p class="subtitle">全栈 RAG 问答系统 &mdash; 基于 React 19 + NestJS 的检索增强生成技术</p>
<div class="badge-row">
<span class="badge react">React 19</span>
<span class="badge nest">NestJS</span>
<span class="badge rag">RAG 系统</span>
<span class="badge ts">TypeScript</span>
</div>
</div>
<div class="container">
<!-- 核心特性 -->
<div class="features-grid">
<div class="feature-card">
<div class="icon icon-purple"> </div>
<h3>多模型支持</h3>
<p>兼容 OpenAI APIOpenAI、DeepSeek、Claude 等)+ Google Gemini 原生 SDK,可配置 LLM、Embedding 和 Rerank 模型。</p>
</div>
<div class="feature-card">
<div class="icon icon-blue"></div>
<h3>双处理模式</h3>
<p>快速模式通过 Apache Tika 提取文本,高精度模式通过视觉管线处理图文混合文档。</p>
</div>
<div class="feature-card">
<div class="icon icon-green"> </div>
<h3>混合检索</h3>
<p>基于 Elasticsearch 的向量 + 关键词混合搜索,支持来源引用、相似度评分,可配置分块大小与重叠。</p>
</div>
<div class="feature-card">
<div class="icon icon-orange"> </div>
<h3>用户隔离</h3>
<p>JWT 认证 + 每用户独立知识库,数据与配置完全隔离,保障隐私安全。</p>
</div>
<div class="feature-card">
<div class="icon icon-pink"> </div>
<h3>流式响应</h3>
<p>基于 Server-Sent Events (SSE) 的实时流式输出,提供流畅低延迟的对话体验。</p>
</div>
<div class="feature-card">
<div class="icon icon-yellow"> </div>
<h3>多语言支持</h3>
<p>界面支持日语、中文和英文,错误信息和 API 响应消息全面国际化。</p>
</div>
</div>
<!-- 系统架构 -->
<section>
<h2 class="section-title"><span class="num">1</span> 系统架构概览</h2>
<div class="arch-diagram">
<div class="arch-label">前端层</div>
<div class="arch-row">
<div class="arch-box frontend">React 19 + Vite<br><small style="opacity:0.7">端口 13001(开发)/ 80(生产)</small></div>
</div>
<div class="arch-arrow"></div>
<div class="arch-label">后端层</div>
<div class="arch-row">
<div class="arch-box backend">NestJS API<br><small style="opacity:0.7">端口 3001</small></div>
<div class="arch-box backend">JWT 认证</div>
<div class="arch-box backend">对话 / RAG</div>
<div class="arch-box backend">视觉管线</div>
</div>
<div class="arch-arrow"></div>
<div class="arch-label">AI 与数据层</div>
<div class="arch-row">
<div class="arch-box ai">OpenAI / Gemini<br><small style="opacity:0.7">LLM + 向量嵌入</small></div>
<div class="arch-box infra">Elasticsearch<br><small style="opacity:0.7">端口 9200</small></div>
<div class="arch-box infra">Apache Tika<br><small style="opacity:0.7">端口 9998</small></div>
<div class="arch-box infra">LibreOffice<br><small style="opacity:0.7">端口 8100</small></div>
<div class="arch-box data">SQLite<br><small style="opacity:0.7">元数据存储</small></div>
</div>
</div>
<!-- 处理管线 -->
<h4 style="margin-top:28px; margin-bottom:14px; color:#f1f5f9;">双处理管线</h4>
<div class="two-col">
<div class="card">
<h4>快速模式(Tika</h4>
<div class="pipeline">
<div class="pipeline-step active">上传</div>
<span class="pipeline-arrow"></span>
<div class="pipeline-step active">Tika 提取</div>
<span class="pipeline-arrow"></span>
<div class="pipeline-step active">向量化</div>
<span class="pipeline-arrow"></span>
<div class="pipeline-step active">存储</div>
</div>
<p style="font-size:0.85rem; color:var(--text-muted); text-align:center;">快速文本提取,无 API 调用成本</p>
</div>
<div class="card">
<h4>高精度模式(视觉管线)</h4>
<div class="pipeline">
<div class="pipeline-step active">上传</div>
<span class="pipeline-arrow"></span>
<div class="pipeline-step">LibreOffice</div>
<span class="pipeline-arrow"></span>
<div class="pipeline-step">PDF 转图片</div>
<span class="pipeline-arrow"></span>
<div class="pipeline-step">视觉模型</div>
</div>
<p style="font-size:0.85rem; color:var(--text-muted); text-align:center;">保留原始排版、图表和图片信息</p>
</div>
</div>
</section>
<!-- 项目结构 -->
<section>
<h2 class="section-title"><span class="num">2</span> 项目结构</h2>
<div class="file-tree">
<span class="dir">simple-kb/</span><br>
├── <span class="dir">web/</span> <span class="comment"># React 前端(Vite</span><br>
│ ├── <span class="dir">components/</span> <span class="comment"># UI 组件(ChatInterface, ConfigPanel 等)</span><br>
│ ├── <span class="dir">contexts/</span> <span class="comment"># React Context 提供者</span><br>
│ ├── <span class="dir">services/</span> <span class="comment"># API 客户端服务</span><br>
│ └── <span class="dir">utils/</span> <span class="comment"># 工具函数</span><br>
├── <span class="dir">server/</span> <span class="comment"># NestJS 后端</span><br>
│ ├── <span class="dir">src/</span><br>
│ │ ├── <span class="dir">ai/</span> <span class="comment"># AI 服务(向量嵌入等)</span><br>
│ │ ├── <span class="dir">api/</span> <span class="comment"># API 模块</span><br>
│ │ ├── <span class="dir">auth/</span> <span class="comment"># JWT 认证</span><br>
│ │ ├── <span class="dir">chat/</span> <span class="comment"># 对话 / RAG 模块</span><br>
│ │ ├── <span class="dir">elasticsearch/</span> <span class="comment"># Elasticsearch 集成</span><br>
│ │ ├── <span class="dir">import-task/</span> <span class="comment"># 导入任务管理</span><br>
│ │ ├── <span class="dir">knowledge-base/</span> <span class="comment"># 知识库管理</span><br>
│ │ ├── <span class="dir">libreoffice/</span> <span class="comment"># LibreOffice 集成</span><br>
│ │ ├── <span class="dir">model-config/</span> <span class="comment"># 模型配置管理</span><br>
│ │ ├── <span class="dir">vision/</span> <span class="comment"># 视觉模型集成</span><br>
│ │ └── <span class="dir">vision-pipeline/</span> <span class="comment"># 视觉管线编排</span><br>
│ ├── <span class="dir">data/</span> <span class="comment"># SQLite 数据库存储</span><br>
│ ├── <span class="dir">uploads/</span> <span class="comment"># 上传文件存储</span><br>
│ └── <span class="dir">temp/</span> <span class="comment"># 临时文件</span><br>
├── <span class="dir">docs/</span> <span class="comment"># 项目文档</span><br>
├── <span class="dir">nginx/</span> <span class="comment"># Nginx 配置</span><br>
├── <span class="dir">libreoffice-server/</span> <span class="comment"># LibreOffice 转换服务(Python/FastAPI</span><br>
└── <span class="file">docker-compose.yml</span> <span class="comment"># Docker 编排配置</span>
</div>
</section>
<!-- 开发环境搭建 -->
<section>
<h2 class="section-title"><span class="num">3</span> 开发环境搭建</h2>
<h4 style="margin-bottom:10px; color:#f1f5f9;">前置条件</h4>
<div class="card" style="margin-bottom:20px;">
<ul>
<li>Node.js 18+</li>
<li>Yarn 包管理器</li>
<li>Docker &amp; Docker Compose</li>
</ul>
</div>
<h4 style="margin-bottom:10px; color:#f1f5f9;">快速启动</h4>
<div class="code-block">
<span class="comment"># 安装依赖</span><br>
<span class="cmd">yarn install</span><br><br>
<span class="comment"># 启动基础设施服务</span><br>
<span class="cmd">docker-compose up -d elasticsearch tika libreoffice</span><br><br>
<span class="comment"># 配置环境变量</span><br>
<span class="cmd">cp server/.env.sample server/.env</span><br><br>
<span class="comment"># 同时启动前后端</span><br>
<span class="cmd">yarn dev</span>
</div>
<h4 style="margin:20px 0 10px; color:#f1f5f9;">开发命令</h4>
<div class="code-block">
<span class="comment"># 仅启动前端(端口 13001</span><br>
<span class="cmd">cd web && yarn dev</span><br><br>
<span class="comment"># 仅启动后端(端口 3001</span><br>
<span class="cmd">cd server && yarn start:dev</span><br><br>
<span class="comment"># 运行测试</span><br>
<span class="cmd">cd server && yarn test</span><br>
<span class="cmd">cd server && yarn test:e2e</span><br><br>
<span class="comment"># 代码检查和格式化</span><br>
<span class="cmd">cd server && yarn lint</span><br>
<span class="cmd">cd server && yarn format</span>
</div>
</section>
<!-- Docker 服务 -->
<section>
<h2 class="section-title"><span class="num">4</span> Docker 服务与端口</h2>
<div class="card">
<table class="info-table">
<thead>
<tr><th>服务</th><th>端口</th><th>用途</th></tr>
</thead>
<tbody>
<tr><td>Elasticsearch</td><td class="port">9200</td><td>向量存储与混合检索</td></tr>
<tr><td>Apache Tika</td><td class="port">9998</td><td>文档文本提取</td></tr>
<tr><td>LibreOffice Server</td><td class="port">8100</td><td>文档格式转换</td></tr>
<tr><td>后端 API</td><td class="port">3001</td><td>NestJS REST API</td></tr>
<tr><td>前端(开发)</td><td class="port">13001</td><td>Vite 开发服务器</td></tr>
<tr><td>前端(生产)</td><td class="port">80 / 443</td><td>Nginx 反向代理</td></tr>
</tbody>
</table>
</div>
</section>
<!-- 环境配置 -->
<section>
<h2 class="section-title"><span class="num">5</span> 环境配置</h2>
<div class="card">
<p style="font-size:0.88rem; color:var(--text-muted); margin-bottom:14px;">关键环境变量,配置于 <code style="color:var(--accent-green); background:#0d1117; padding:2px 8px; border-radius:4px; font-size:0.83rem;">server/.env</code></p>
<table class="info-table">
<thead>
<tr><th>变量名</th><th>默认值</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td class="env-key">OPENAI_API_KEY</td><td>&mdash;</td><td>OpenAI 兼容 API 密钥</td></tr>
<tr><td class="env-key">GEMINI_API_KEY</td><td>&mdash;</td><td>Google Gemini API 密钥</td></tr>
<tr><td class="env-key">ELASTICSEARCH_HOST</td><td>http://localhost:9200</td><td>Elasticsearch 地址</td></tr>
<tr><td class="env-key">TIKA_HOST</td><td>http://localhost:9998</td><td>Apache Tika 地址</td></tr>
<tr><td class="env-key">LIBREOFFICE_URL</td><td>http://localhost:8100</td><td>LibreOffice 服务器地址</td></tr>
<tr><td class="env-key">JWT_SECRET</td><td>&mdash;</td><td>JWT 签名密钥</td></tr>
</tbody>
</table>
</div>
</section>
<!-- 代码规范 -->
<section>
<h2 class="section-title"><span class="num">6</span> 代码规范</h2>
<div class="two-col">
<div class="card">
<h4>语言要求</h4>
<ul>
<li>代码注释必须使用英文</li>
<li>日志消息必须使用英文</li>
<li>错误消息必须支持国际化</li>
<li>API 响应消息必须支持国际化</li>
<li>界面支持日语、中文和英文</li>
</ul>
</div>
<div class="card">
<h4>代码质量</h4>
<ul>
<li>后端使用 Jest 进行单元测试和端到端测试</li>
<li>后端已配置 ESLint 和 Prettier</li>
<li>格式化:<code style="color:var(--accent-green);">cd server && yarn format</code></li>
<li>检查:<code style="color:var(--accent-green);">cd server && yarn lint</code></li>
</ul>
</div>
</div>
</section>
<!-- 常见开发任务 -->
<section>
<h2 class="section-title"><span class="num">7</span> 常见开发任务</h2>
<div class="two-col">
<div class="card">
<h4>添加新的 API 端点</h4>
<ol class="step-list">
<li><code style="color:var(--accent-blue);">server/src/</code> 对应模块下创建 Controller</li>
<li>添加 Service 方法,使用英文注释</li>
<li>更新 DTO 和参数校验</li>
<li><code style="color:var(--accent-blue);">*.spec.ts</code> 文件中添加测试</li>
</ol>
</div>
<div class="card">
<h4>添加新的前端组件</h4>
<ol class="step-list">
<li><code style="color:var(--accent-blue);">web/components/</code> 下创建组件</li>
<li><code style="color:var(--accent-blue);">web/types.ts</code> 中添加 TypeScript 接口</li>
<li>使用 Tailwind CSS 进行样式开发</li>
<li><code style="color:var(--accent-blue);">web/services/</code> 中连接后端服务</li>
</ol>
</div>
</div>
</section>
<!-- 部署 -->
<section>
<h2 class="section-title"><span class="num">8</span> 部署</h2>
<div class="two-col">
<div class="card">
<h4>开发环境</h4>
<div class="code-block">
<span class="cmd">docker-compose up -d elasticsearch tika libreoffice</span><br>
<span class="cmd">yarn dev</span>
</div>
</div>
<div class="card">
<h4>生产环境</h4>
<div class="code-block">
<span class="comment"># 构建并启动所有服务</span><br>
<span class="cmd">docker-compose up -d</span>
</div>
</div>
</div>
</section>
<!-- 故障排查 -->
<section>
<h2 class="section-title"><span class="num">9</span> 故障排查</h2>
<div class="troubleshoot-item">
<strong>Elasticsearch 无法启动</strong>
<p>检查 docker-compose.yml 中的内存限制配置</p>
</div>
<div class="troubleshoot-item">
<strong>文件上传失败</strong>
<p>确保 <code style="color:var(--accent-green);">uploads/</code><code style="color:var(--accent-green);">temp/</code> 目录存在且具有正确的读写权限</p>
</div>
<div class="troubleshoot-item">
<strong>视觉管线报错</strong>
<p>确认 LibreOffice 服务正在运行且 8100 端口可访问</p>
</div>
<div class="troubleshoot-item">
<strong>API 密钥错误</strong>
<p>检查 <code style="color:var(--accent-green);">server/.env</code> 中的环境变量配置</p>
</div>
<div class="troubleshoot-item">
<strong>数据库重置</strong>
<p>删除 <code style="color:var(--accent-green);">server/data/metadata.db</code> 及 Elasticsearch 数据卷</p>
</div>
</section>
<!-- 调试 -->
<section>
<h2 class="section-title"><span class="num">10</span> 调试与健康检查</h2>
<div class="code-block">
<span class="comment"># 检查 Elasticsearch 状态</span><br>
<span class="cmd">curl http://localhost:9200/_cat/indices</span><br><br>
<span class="comment"># 检查 Tika 状态</span><br>
<span class="cmd">curl http://localhost:9998/tika</span><br><br>
<span class="comment"># 检查 LibreOffice 状态</span><br>
<span class="cmd">curl http://localhost:8100/health</span>
</div>
</section>
</div>
<footer>
Simple Knowledge Base &mdash; 全栈 RAG 问答系统 &mdash; React 19 + NestJS + Elasticsearch
</footer>
</body>
</html>