feat(admin): Implement System Knowledge Base management module

Features:

- Backend: SystemKbService with full CRUD (knowledge bases + documents)

- Backend: 8 RESTful API endpoints (list/detail/create/update/delete/upload/download)

- Backend: OSS storage integration (system/knowledge-bases/{kbId}/{docId})

- Backend: RAG engine integration (document parsing, chunking, vectorization)

- Frontend: SystemKbListPage with card-based layout

- Frontend: SystemKbDetailPage with document management table

- Frontend: Master-Detail UX pattern for better user experience

- Document upload (single/batch), download (preserving original filename), delete

Technical:

- Database migration for system_knowledge_bases and system_kb_documents tables

- OSSAdapter.getSignedUrl with Content-Disposition for original filename

- Reuse RAG engine from common/rag for document processing

Tested: Local environment verified, all features working
This commit is contained in:
2026-01-28 21:57:44 +08:00
parent 3a4aa9123c
commit 0d9e6b9922
28 changed files with 2827 additions and 247 deletions

104
backend/test-oss-upload.cjs Normal file
View File

@@ -0,0 +1,104 @@
/**
* OSS 上传下载完整性测试
*
* 测试流程:
* 1. 读取本地 PDF 文件
* 2. 上传到 OSS
* 3. 从 OSS 下载
* 4. 比较 MD5 值
*/
require('dotenv').config();
const fs = require('fs');
const crypto = require('crypto');
const OSS = require('ali-oss');
const testFile = 'D:\\MyCursor\\AIclinicalresearch\\docs\\06-测试文档\\近红外光谱NIRS队列研究举例.pdf';
const testKey = 'test/oss-integrity-test.pdf';
async function main() {
console.log('='.repeat(60));
console.log('OSS Upload/Download Integrity Test');
console.log('='.repeat(60));
// 1. 读取本地文件
console.log('\n[1] Reading local file...');
const localBuffer = fs.readFileSync(testFile);
const localMd5 = crypto.createHash('md5').update(localBuffer).digest('hex');
console.log(` File size: ${localBuffer.length} bytes`);
console.log(` MD5: ${localMd5}`);
console.log(` First 20 bytes: ${localBuffer.slice(0, 20).toString('hex')}`);
// 2. 创建 OSS 客户端
console.log('\n[2] Creating OSS client...');
const client = new OSS({
region: process.env.OSS_REGION,
bucket: process.env.OSS_BUCKET,
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
endpoint: `${process.env.OSS_REGION}.aliyuncs.com`,
secure: true,
});
console.log(` Region: ${process.env.OSS_REGION}`);
console.log(` Bucket: ${process.env.OSS_BUCKET}`);
// 3. 上传到 OSS
console.log('\n[3] Uploading to OSS...');
try {
const uploadResult = await client.put(testKey, localBuffer, {
headers: {
'Content-Type': 'application/pdf',
},
});
console.log(` Upload success: ${uploadResult.name}`);
} catch (error) {
console.error(` Upload failed:`, error.message);
return;
}
// 4. 从 OSS 下载
console.log('\n[4] Downloading from OSS...');
try {
const downloadResult = await client.get(testKey);
const downloadBuffer = downloadResult.content;
const downloadMd5 = crypto.createHash('md5').update(downloadBuffer).digest('hex');
console.log(` Download size: ${downloadBuffer.length} bytes`);
console.log(` MD5: ${downloadMd5}`);
console.log(` First 20 bytes: ${downloadBuffer.slice(0, 20).toString('hex')}`);
// 5. 比较
console.log('\n[5] Comparing...');
if (localMd5 === downloadMd5) {
console.log(' ✅ MD5 MATCH - File integrity OK!');
} else {
console.log(' ❌ MD5 MISMATCH - File corrupted!');
console.log(` Local: ${localMd5}`);
console.log(` Download: ${downloadMd5}`);
}
// 保存下载的文件用于对比
const outputPath = 'D:\\MyCursor\\AIclinicalresearch\\docs\\06-测试文档\\oss-downloaded-test.pdf';
fs.writeFileSync(outputPath, downloadBuffer);
console.log(`\n Downloaded file saved to: ${outputPath}`);
} catch (error) {
console.error(` Download failed:`, error.message);
return;
}
// 6. 清理
console.log('\n[6] Cleanup...');
try {
await client.delete(testKey);
console.log(' Test file deleted from OSS');
} catch (error) {
console.log(` Cleanup failed: ${error.message}`);
}
console.log('\n' + '='.repeat(60));
console.log('Test Complete!');
console.log('='.repeat(60));
}
main().catch(console.error);