chore: add remaining test docs, scripts and temp files

- Add Git commit preparation checklist
- Add Phase testing guides and issue tracking
- Add utility scripts (env setup, test data initialization)
- Add temp migration SQL files (for reference)
- Update startup scripts and README
- Remove obsolete scripts
This commit is contained in:
2025-11-16 15:44:55 +08:00
parent 1992232fda
commit 855d142fec
32 changed files with 9125 additions and 113 deletions

View File

@@ -0,0 +1,155 @@
-- ========================================
-- 简化版验证脚本
-- ========================================
-- 验证10个Schema是否创建
DO $$
DECLARE
schema_count INTEGER;
BEGIN
SELECT COUNT(*) INTO schema_count
FROM information_schema.schemata
WHERE schema_name IN (
'platform_schema', 'aia_schema', 'pkb_schema',
'asl_schema', 'common_schema', 'dc_schema',
'rvw_schema', 'admin_schema', 'ssa_schema', 'st_schema'
);
RAISE NOTICE '========================================';
IF schema_count = 10 THEN
RAISE NOTICE '✅ 10个Schema全部创建成功';
ELSE
RAISE WARNING '⚠️ Schema数量异常: 预期10个实际%个', schema_count;
END IF;
RAISE NOTICE '========================================';
END $$;
-- 验证表数量
DO $$
DECLARE
platform_tables INTEGER;
aia_tables INTEGER;
pkb_tables INTEGER;
BEGIN
SELECT COUNT(*) INTO platform_tables FROM pg_tables WHERE schemaname = 'platform_schema';
SELECT COUNT(*) INTO aia_tables FROM pg_tables WHERE schemaname = 'aia_schema';
SELECT COUNT(*) INTO pkb_tables FROM pg_tables WHERE schemaname = 'pkb_schema';
RAISE NOTICE '表数量统计:';
RAISE NOTICE 'platform_schema: % 个表 (预期1个)', platform_tables;
RAISE NOTICE 'aia_schema: % 个表 (预期5个)', aia_tables;
RAISE NOTICE 'pkb_schema: % 个表 (预期5个)', pkb_tables;
IF platform_tables = 1 AND aia_tables = 5 AND pkb_tables = 5 THEN
RAISE NOTICE '✅ 所有表创建成功';
ELSE
RAISE WARNING '⚠️ 表数量存在异常';
END IF;
RAISE NOTICE '========================================';
END $$;
-- 验证数据量
DO $$
DECLARE
public_users INTEGER;
platform_users INTEGER;
public_projects INTEGER;
aia_projects INTEGER;
public_kb INTEGER;
pkb_kb INTEGER;
BEGIN
-- 统计users
SELECT COUNT(*) INTO public_users FROM public.users;
SELECT COUNT(*) INTO platform_users FROM platform_schema.users;
-- 统计projects
SELECT COUNT(*) INTO public_projects FROM public.projects;
SELECT COUNT(*) INTO aia_projects FROM aia_schema.projects;
-- 统计knowledge_bases
SELECT COUNT(*) INTO public_kb FROM public.knowledge_bases;
SELECT COUNT(*) INTO pkb_kb FROM pkb_schema.knowledge_bases;
RAISE NOTICE '数据量对比:';
RAISE NOTICE 'users: public.% -> platform_schema.%', public_users, platform_users;
RAISE NOTICE 'projects: public.% -> aia_schema.%', public_projects, aia_projects;
RAISE NOTICE 'knowledge_bases: public.% -> pkb_schema.%', public_kb, pkb_kb;
IF public_users = platform_users AND
public_projects = aia_projects AND
public_kb = pkb_kb THEN
RAISE NOTICE '✅ 数据迁移完整';
ELSE
RAISE WARNING '⚠️ 数据量存在差异';
END IF;
RAISE NOTICE '========================================';
END $$;
-- 验证外键约束
DO $$
DECLARE
fk_count INTEGER;
BEGIN
SELECT COUNT(*) INTO fk_count
FROM information_schema.table_constraints
WHERE constraint_type = 'FOREIGN KEY'
AND table_schema IN ('platform_schema', 'aia_schema', 'pkb_schema');
RAISE NOTICE '外键约束统计:';
RAISE NOTICE '外键约束总数:%', fk_count;
RAISE NOTICE '✅ 外键约束已建立';
RAISE NOTICE '========================================';
END $$;
-- 验证跨Schema引用
DO $$
DECLARE
invalid_aia_projects INTEGER;
invalid_pkb_kb INTEGER;
BEGIN
-- 验证aia_schema.projects
SELECT COUNT(*) INTO invalid_aia_projects
FROM aia_schema.projects p
LEFT JOIN platform_schema.users u ON p.user_id = u.id
WHERE u.id IS NULL;
-- 验证pkb_schema.knowledge_bases
SELECT COUNT(*) INTO invalid_pkb_kb
FROM pkb_schema.knowledge_bases kb
LEFT JOIN platform_schema.users u ON kb.user_id = u.id
WHERE u.id IS NULL;
RAISE NOTICE '跨Schema引用验证';
IF invalid_aia_projects = 0 THEN
RAISE NOTICE '✅ aia_schema.projects外键全部有效';
ELSE
RAISE WARNING '⚠️ aia_schema.projects有%条无效user_id', invalid_aia_projects;
END IF;
IF invalid_pkb_kb = 0 THEN
RAISE NOTICE '✅ pkb_schema.knowledge_bases外键全部有效';
ELSE
RAISE WARNING '⚠️ pkb_schema.knowledge_bases有%条无效user_id', invalid_pkb_kb;
END IF;
RAISE NOTICE '========================================';
END $$;
-- 最终总结
DO $$
BEGIN
RAISE NOTICE '';
RAISE NOTICE '🎉🎉🎉 Schema迁移验证完成 🎉🎉🎉';
RAISE NOTICE '';
RAISE NOTICE '下一步:';
RAISE NOTICE '1. 更新Prisma配置schema.prisma';
RAISE NOTICE '2. 生成Prisma Client';
RAISE NOTICE '3. 更新代码以使用新Schema';
RAISE NOTICE '4. 测试现有功能';
RAISE NOTICE '';
RAISE NOTICE '========================================';
END $$;

View File

@@ -0,0 +1,17 @@
-- 快速检查Schema和数据
SELECT
'Schema检查' as check_type,
schema_name,
(SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = s.schema_name) as table_count
FROM information_schema.schemata s
WHERE schema_name IN (
'platform_schema', 'aia_schema', 'pkb_schema',
'asl_schema', 'common_schema', 'dc_schema',
'rvw_schema', 'admin_schema', 'ssa_schema', 'st_schema'
)
ORDER BY schema_name;

252
backend/test-batch-api.js Normal file
View File

@@ -0,0 +1,252 @@
/**
* Phase 3: 批处理API测试脚本
*
* 测试所有批处理API端点
*/
import axios from 'axios';
const BASE_URL = 'http://localhost:3001/api/v1';
// 等待服务器启动
async function waitForServer(maxAttempts = 10) {
console.log('⏳ 等待服务器启动...');
for (let i = 0; i < maxAttempts; i++) {
try {
await axios.get(`${BASE_URL}`);
console.log('✅ 服务器已就绪\n');
return true;
} catch (error) {
if (i < maxAttempts - 1) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
}
console.error('❌ 服务器启动超时');
return false;
}
// 测试1: 获取所有预设模板
async function testGetTemplates() {
console.log('📋 测试1: 获取所有预设模板');
console.log('GET /api/v1/batch/templates\n');
try {
const response = await axios.get(`${BASE_URL}/batch/templates`);
console.log('✅ 成功');
console.log('返回数据:', JSON.stringify(response.data, null, 2));
console.log('\n' + '='.repeat(60) + '\n');
return response.data.data;
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
console.log('\n' + '='.repeat(60) + '\n');
return null;
}
}
// 测试2: 执行批处理任务需要真实的知识库和文档ID
async function testExecuteBatch(kbId, documentIds) {
console.log('📦 测试2: 执行批处理任务');
console.log('POST /api/v1/batch/execute\n');
const requestData = {
kb_id: kbId,
document_ids: documentIds,
template_type: 'preset',
template_id: 'clinical_research',
model_type: 'deepseek-v3',
task_name: '批处理测试任务',
};
console.log('请求数据:', JSON.stringify(requestData, null, 2));
try {
const response = await axios.post(`${BASE_URL}/batch/execute`, requestData);
console.log('\n✅ 成功');
console.log('返回数据:', JSON.stringify(response.data, null, 2));
console.log('\n' + '='.repeat(60) + '\n');
return response.data;
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
console.log('\n' + '='.repeat(60) + '\n');
return null;
}
}
// 测试3: 获取任务状态
async function testGetTask(taskId) {
console.log('📊 测试3: 获取任务状态');
console.log(`GET /api/v1/batch/tasks/${taskId}\n`);
try {
const response = await axios.get(`${BASE_URL}/batch/tasks/${taskId}`);
console.log('✅ 成功');
console.log('返回数据:', JSON.stringify(response.data, null, 2));
console.log('\n' + '='.repeat(60) + '\n');
return response.data.data;
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
console.log('\n' + '='.repeat(60) + '\n');
return null;
}
}
// 测试4: 获取任务结果
async function testGetTaskResults(taskId) {
console.log('📄 测试4: 获取任务结果');
console.log(`GET /api/v1/batch/tasks/${taskId}/results\n`);
try {
const response = await axios.get(`${BASE_URL}/batch/tasks/${taskId}/results`);
console.log('✅ 成功');
console.log('任务信息:', JSON.stringify(response.data.data.task, null, 2));
console.log(`\n结果数量: ${response.data.data.results.length}`);
// 显示前3个结果的摘要
console.log('\n前3个结果摘要:');
response.data.data.results.slice(0, 3).forEach((r, i) => {
console.log(`\n结果 ${i + 1}:`);
console.log(` - 文档: ${r.document_name}`);
console.log(` - 状态: ${r.status}`);
console.log(` - 处理时间: ${r.processing_time_ms}ms`);
if (r.status === 'success' && r.data) {
console.log(` - 数据字段:`, Object.keys(r.data).join(', '));
}
if (r.error_message) {
console.log(` - 错误: ${r.error_message}`);
}
});
console.log('\n' + '='.repeat(60) + '\n');
return response.data.data;
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
console.log('\n' + '='.repeat(60) + '\n');
return null;
}
}
// 测试5: 重试失败的文档
async function testRetryFailed(taskId) {
console.log('🔄 测试5: 重试失败的文档');
console.log(`POST /api/v1/batch/tasks/${taskId}/retry-failed\n`);
try {
const response = await axios.post(`${BASE_URL}/batch/tasks/${taskId}/retry-failed`);
console.log('✅ 成功');
console.log('返回数据:', JSON.stringify(response.data, null, 2));
console.log('\n' + '='.repeat(60) + '\n');
return response.data;
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
console.log('\n' + '='.repeat(60) + '\n');
return null;
}
}
// 主测试流程
async function main() {
console.log('🧪 Phase 3 批处理API测试\n');
console.log('='.repeat(60));
console.log('测试服务器: ' + BASE_URL);
console.log('='.repeat(60) + '\n');
// 等待服务器启动
const serverReady = await waitForServer();
if (!serverReady) {
process.exit(1);
}
// 测试1: 获取模板
const templates = await testGetTemplates();
// 检查是否有测试数据
console.log('⚠️ 测试2-5需要真实的知识库和文档数据\n');
console.log('请手动提供以下信息来继续测试:\n');
console.log('示例命令:');
console.log('node test-batch-api.js <知识库ID> <文档ID1> <文档ID2> <文档ID3>\n');
const args = process.argv.slice(2);
if (args.length < 4) {
console.log('📝 跳过测试2-5需要提供知识库ID和至少3个文档ID\n');
console.log('✅ 测试1完成');
return;
}
const kbId = args[0];
const documentIds = args.slice(1);
console.log(`\n📌 使用测试数据:`);
console.log(` 知识库ID: ${kbId}`);
console.log(` 文档数量: ${documentIds.length}`);
console.log(` 文档IDs: ${documentIds.slice(0, 3).join(', ')}${documentIds.length > 3 ? '...' : ''}\n`);
console.log('='.repeat(60) + '\n');
// 测试2: 执行批处理
const executeResult = await testExecuteBatch(kbId, documentIds);
if (!executeResult || !executeResult.data || !executeResult.data.task_id) {
console.log('❌ 批处理任务创建失败或未返回task_id停止测试');
return;
}
const taskId = executeResult.data.task_id;
console.log(`✅ 获取到任务ID: ${taskId}\n`);
// 等待一段时间让任务开始执行
console.log('⏳ 等待5秒让任务开始执行...\n');
await new Promise(resolve => setTimeout(resolve, 5000));
// 测试3: 获取任务状态
const taskStatus = await testGetTask(taskId);
// 等待任务完成最多等待2分钟
if (taskStatus && taskStatus.status === 'processing') {
console.log('⏳ 任务仍在处理中等待完成最多2分钟...\n');
for (let i = 0; i < 24; i++) { // 24次 * 5秒 = 2分钟
await new Promise(resolve => setTimeout(resolve, 5000));
const currentStatus = await testGetTask(taskId);
if (currentStatus && currentStatus.status !== 'processing') {
console.log(`✅ 任务已完成,状态: ${currentStatus.status}\n`);
break;
}
console.log(`⏳ 仍在处理中... (${currentStatus.completed_count}/${currentStatus.total_documents})\n`);
}
}
// 测试4: 获取任务结果
const taskResults = await testGetTaskResults(taskId);
// 测试5: 重试失败的文档(如果有失败的)
if (taskResults && taskResults.task.failed_count > 0) {
console.log(`\n⚠️ 发现 ${taskResults.task.failed_count} 个失败的文档,测试重试功能\n`);
await testRetryFailed(taskId);
} else {
console.log('\n✅ 所有文档处理成功,跳过重试测试\n');
}
console.log('🎉 所有API测试完成');
}
// 运行测试
main().catch(error => {
console.error('💥 测试执行失败:', error);
process.exit(1);
});

409
backend/test-review-api.js Normal file
View File

@@ -0,0 +1,409 @@
/**
* 稿件审查API测试脚本
* 测试5个API端点的功能
*/
import axios from 'axios';
import FormData from 'form-data';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const API_BASE_URL = 'http://localhost:3001/api/v1';
const TEST_FILE_PATH = path.join(__dirname, '../docs/稿约规范性评估标准.txt'); // 使用现有文本文件作为测试
// 颜色输出
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
};
function log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function separator() {
console.log('\n' + '='.repeat(80) + '\n');
}
// 延迟函数
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// ==================== 测试函数 ====================
/**
* 测试1: 上传稿件
*/
async function testUploadManuscript() {
log('📤 测试1: 上传稿件 (POST /review/upload)', 'cyan');
try {
// 检查测试文件是否存在
if (!fs.existsSync(TEST_FILE_PATH)) {
log(`❌ 测试文件不存在: ${TEST_FILE_PATH}`, 'red');
log('💡 提示请准备一个Word文档.doc或.docx作为测试文件', 'yellow');
return null;
}
const formData = new FormData();
formData.append('file', fs.createReadStream(TEST_FILE_PATH));
formData.append('modelType', 'deepseek-v3');
log(`📄 测试文件: ${path.basename(TEST_FILE_PATH)}`, 'blue');
log(`🤖 使用模型: deepseek-v3`, 'blue');
const response = await axios.post(
`${API_BASE_URL}/review/upload`,
formData,
{
headers: formData.getHeaders(),
timeout: 30000,
}
);
if (response.data.success) {
log('✅ 上传成功!', 'green');
console.log('返回数据:', JSON.stringify(response.data, null, 2));
return response.data.data.taskId;
} else {
log('❌ 上传失败', 'red');
console.log('错误信息:', response.data.message);
return null;
}
} catch (error) {
log('❌ 请求失败', 'red');
if (error.response) {
console.log('状态码:', error.response.status);
console.log('错误信息:', error.response.data);
} else {
console.log('错误:', error.message);
}
return null;
}
}
/**
* 测试2: 查询任务状态
*/
async function testGetTaskStatus(taskId) {
log('🔍 测试2: 查询任务状态 (GET /review/tasks/:taskId)', 'cyan');
try {
log(`📋 任务ID: ${taskId}`, 'blue');
const response = await axios.get(`${API_BASE_URL}/review/tasks/${taskId}`, {
timeout: 10000,
});
if (response.data.success) {
log('✅ 查询成功!', 'green');
console.log('任务状态:', JSON.stringify(response.data.data, null, 2));
return response.data.data;
} else {
log('❌ 查询失败', 'red');
console.log('错误信息:', response.data.message);
return null;
}
} catch (error) {
log('❌ 请求失败', 'red');
if (error.response) {
console.log('状态码:', error.response.status);
console.log('错误信息:', error.response.data);
} else {
console.log('错误:', error.message);
}
return null;
}
}
/**
* 测试3: 轮询任务直到完成
*/
async function pollTaskUntilComplete(taskId, maxAttempts = 60) {
log('⏳ 测试3: 轮询任务状态直到完成', 'cyan');
for (let i = 0; i < maxAttempts; i++) {
const task = await testGetTaskStatus(taskId);
if (!task) {
log('❌ 查询任务失败,停止轮询', 'red');
return null;
}
log(`📊 当前状态: ${task.status}`, 'yellow');
if (task.status === 'completed') {
log('✅ 任务已完成!', 'green');
return task;
}
if (task.status === 'failed') {
log('❌ 任务失败', 'red');
console.log('错误信息:', task.errorMessage);
return null;
}
log(`⏱️ 等待5秒后重试... (${i + 1}/${maxAttempts})`, 'blue');
await delay(5000);
}
log('⚠️ 超过最大等待时间,任务仍未完成', 'yellow');
return null;
}
/**
* 测试4: 获取审查报告
*/
async function testGetReport(taskId) {
log('📊 测试4: 获取审查报告 (GET /review/tasks/:taskId/report)', 'cyan');
try {
log(`📋 任务ID: ${taskId}`, 'blue');
const response = await axios.get(`${API_BASE_URL}/review/tasks/${taskId}/report`, {
timeout: 10000,
});
if (response.data.success) {
log('✅ 获取报告成功!', 'green');
console.log('\n📄 完整报告:');
console.log(JSON.stringify(response.data.data, null, 2));
// 显示关键指标
const report = response.data.data;
separator();
log('📈 评估结果摘要:', 'cyan');
log(`总分: ${report.overallScore?.toFixed(1) || 'N/A'}`, 'green');
log(`稿约规范性评分: ${report.editorialReview?.overall_score || 'N/A'}`, 'blue');
log(`方法学评分: ${report.methodologyReview?.overall_score || 'N/A'}`, 'blue');
log(`字数: ${report.wordCount || 'N/A'}`, 'blue');
log(`耗时: ${report.durationSeconds || 'N/A'}`, 'blue');
separator();
return response.data.data;
} else {
log('❌ 获取报告失败', 'red');
console.log('错误信息:', response.data.message);
return null;
}
} catch (error) {
log('❌ 请求失败', 'red');
if (error.response) {
console.log('状态码:', error.response.status);
console.log('错误信息:', error.response.data);
} else {
console.log('错误:', error.message);
}
return null;
}
}
/**
* 测试5: 获取任务列表
*/
async function testGetTaskList() {
log('📋 测试5: 获取任务列表 (GET /review/tasks)', 'cyan');
try {
const response = await axios.get(`${API_BASE_URL}/review/tasks`, {
params: { page: 1, limit: 10 },
timeout: 10000,
});
if (response.data.success) {
log('✅ 获取列表成功!', 'green');
console.log(`找到 ${response.data.data.length} 个任务`);
console.log('任务列表:', JSON.stringify(response.data.data, null, 2));
console.log('分页信息:', JSON.stringify(response.data.pagination, null, 2));
return response.data.data;
} else {
log('❌ 获取列表失败', 'red');
console.log('错误信息:', response.data.message);
return null;
}
} catch (error) {
log('❌ 请求失败', 'red');
if (error.response) {
console.log('状态码:', error.response.status);
console.log('错误信息:', error.response.data);
} else {
console.log('错误:', error.message);
}
return null;
}
}
/**
* 测试6: 删除任务(可选)
*/
async function testDeleteTask(taskId) {
log('🗑️ 测试6: 删除任务 (DELETE /review/tasks/:taskId)', 'cyan');
try {
log(`📋 任务ID: ${taskId}`, 'blue');
const response = await axios.delete(`${API_BASE_URL}/review/tasks/${taskId}`, {
timeout: 10000,
});
if (response.data.success) {
log('✅ 删除成功!', 'green');
console.log('返回数据:', JSON.stringify(response.data, null, 2));
return true;
} else {
log('❌ 删除失败', 'red');
console.log('错误信息:', response.data.message);
return false;
}
} catch (error) {
log('❌ 请求失败', 'red');
if (error.response) {
console.log('状态码:', error.response.status);
console.log('错误信息:', error.response.data);
} else {
console.log('错误:', error.message);
}
return false;
}
}
// ==================== 主测试流程 ====================
async function runAllTests() {
log('🚀 开始稿件审查API测试', 'green');
separator();
// 测试1: 上传稿件
const taskId = await testUploadManuscript();
if (!taskId) {
log('❌ 上传失败,终止测试', 'red');
return;
}
separator();
// 等待2秒
await delay(2000);
// 测试2: 查询任务状态
await testGetTaskStatus(taskId);
separator();
// 测试3: 轮询直到完成
const completedTask = await pollTaskUntilComplete(taskId);
if (!completedTask) {
log('❌ 任务未能完成,跳过报告测试', 'red');
separator();
// 但仍然测试任务列表
await testGetTaskList();
separator();
return;
}
separator();
// 测试4: 获取报告
await testGetReport(taskId);
separator();
// 测试5: 获取任务列表
await testGetTaskList();
separator();
// 测试6: 删除任务(可选,取消注释以启用)
// log('⚠️ 是否删除测试任务取消注释testDeleteTask以启用', 'yellow');
// await testDeleteTask(taskId);
// separator();
log('✅ 所有测试完成!', 'green');
}
// ==================== 健康检查 ====================
async function checkHealth() {
log('🔍 检查后端服务健康状态...', 'cyan');
try {
const response = await axios.get('http://localhost:3001/health', {
timeout: 5000,
});
log('✅ 后端服务正常', 'green');
console.log('健康状态:', response.data);
return true;
} catch (error) {
log('❌ 后端服务不可用', 'red');
console.log('错误:', error.message);
log('💡 请先启动后端服务: npm run dev 或 启动后端.bat', 'yellow');
return false;
}
}
// ==================== 入口 ====================
async function main() {
console.log('\n');
log('═══════════════════════════════════════════════════════════════════════════════', 'cyan');
log(' 稿件审查API自动化测试脚本 ', 'cyan');
log('═══════════════════════════════════════════════════════════════════════════════', 'cyan');
console.log('\n');
// 健康检查
const healthy = await checkHealth();
if (!healthy) {
process.exit(1);
}
separator();
// 运行所有测试
await runAllTests();
console.log('\n');
log('═══════════════════════════════════════════════════════════════════════════════', 'cyan');
log(' 测试完成 ', 'cyan');
log('═══════════════════════════════════════════════════════════════════════════════', 'cyan');
console.log('\n');
}
main().catch(error => {
console.error('测试脚本执行失败:', error);
process.exit(1);
});

View File

@@ -0,0 +1,79 @@
# PowerShell脚本自动添加CloseAI配置到.env文件
# 使用方法:在 backend 目录下运行 .\update-env-closeai.ps1
$envFile = ".env"
# 检查.env文件是否存在
if (-not (Test-Path $envFile)) {
Write-Host "错误:.env文件不存在" -ForegroundColor Red
Write-Host "请先创建.env文件" -ForegroundColor Yellow
exit 1
}
# 读取现有配置
$envContent = Get-Content $envFile -Raw
# 检查是否已经包含CloseAI配置
if ($envContent -match "CLOSEAI_API_KEY") {
Write-Host "检测到.env文件中已包含CloseAI配置" -ForegroundColor Yellow
$overwrite = Read-Host "是否要覆盖现有配置?(y/n)"
if ($overwrite -ne "y") {
Write-Host "取消更新" -ForegroundColor Yellow
exit 0
}
# 删除旧的CloseAI配置
$envContent = $envContent -replace "(?ms)# ={32}.*?# CloseAI.*?# ={32}.*?(?=\r?\n# ={32}|\r?\n\r?\n|\Z)", ""
}
# CloseAI配置内容
$closeaiConfig = @"
# ================================
# CloseAIOpenAIClaude
# ================================
# CloseAIAPIOpenAIClaude访
# https://platform.openai-proxy.org
# API KeyOpenAIClaude
CLOSEAI_API_KEY=sk-cu0iepbXYGGx2jc7BqP6ogtSWmP6fk918qV3RUdtGC3Edlpo
# OpenAI
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
# Claude
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
#
# - OpenAI: gpt-5-pro (), gpt-4-turbo-preview, gpt-3.5-turbo
# - Claude: claude-sonnet-4-5-20250929 (), claude-3-5-sonnet-20241022
"@
# 添加到文件末尾
$envContent = $envContent.TrimEnd() + "`r`n" + $closeaiConfig
# 写回文件
Set-Content -Path $envFile -Value $envContent -NoNewline
Write-Host "✅ CloseAI配置已成功添加到.env文件" -ForegroundColor Green
Write-Host ""
Write-Host "已添加的配置:" -ForegroundColor Cyan
Write-Host "- CLOSEAI_API_KEY: sk-cu0iep...真实API Key" -ForegroundColor White
Write-Host "- CLOSEAI_OPENAI_BASE_URL: https://api.openai-proxy.org/v1" -ForegroundColor White
Write-Host "- CLOSEAI_CLAUDE_BASE_URL: https://api.openai-proxy.org/anthropic" -ForegroundColor White
Write-Host ""
Write-Host "可用模型:" -ForegroundColor Cyan
Write-Host "- GPT-5-Pro: gpt-5-pro" -ForegroundColor White
Write-Host "- Claude-4.5-Sonnet: claude-sonnet-4-5-20250929" -ForegroundColor White
Write-Host ""
Write-Host "下一步:重启后端服务以应用新配置" -ForegroundColor Yellow

View File

@@ -0,0 +1,63 @@
@echo off
chcp 65001 > nul
echo.
echo ========================================
echo 初始化测试用户
echo ========================================
echo.
cd /d "%~dp0"
echo [1/1] 正在初始化测试用户数据...
call npm run prisma:seed
echo.
echo ========================================
echo 初始化完成!
echo ========================================
echo.
echo 测试账号信息:
echo 邮箱: test@example.com
echo 密码: password123
echo 用户ID: user-mock-001
echo.
echo 管理员账号信息:
echo 邮箱: admin@example.com
echo 密码: password123
echo 用户ID: user-admin-001
echo.
echo 现在可以创建知识库了!
echo.
pause

View File

@@ -0,0 +1,96 @@
# 测试用户初始化说明
## 问题
创建知识库时报错 "User not found",因为数据库中没有用户记录。
## 解决方案
### 方法1运行批处理脚本推荐
双击运行:**`初始化测试用户.bat`**
这个脚本会自动创建两个测试用户:
- 普通用户user-mock-001
- 管理员用户user-admin-001
### 方法2手动运行命令
```bash
cd backend
npm run prisma:seed
```
## 测试账号信息
### 普通用户
- **用户ID**: `user-mock-001`
- **邮箱**: `test@example.com`
- **密码**: `password123`
- **知识库配额**: 3个
- **试用期**: 30天
### 管理员用户
- **用户ID**: `user-admin-001`
- **邮箱**: `admin@example.com`
- **密码**: `password123`
- **知识库配额**: 10个
## 说明
1. **当前系统使用硬编码的用户ID**
- 后端控制器使用 `MOCK_USER_ID = 'user-mock-001'`
- 所有API请求都使用这个固定ID
2. **用户认证系统未实现**
- 当前处于开发阶段
- 用户登录/注册功能在里程碑3实现
3. **重新初始化数据库后需要重新运行seed**
- 如果执行了 `prisma migrate reset`
- 或手动清空了数据库
- 需要重新运行 `初始化测试用户.bat`
## 验证
运行seed脚本后可以通过Prisma Studio验证
```bash
npm run prisma:studio
```
在浏览器中打开,查看 `users` 表,应该能看到两个用户记录。
## 下一步
初始化完成后,就可以正常创建知识库了!🎉