fix(pkb): fix create KB and upload issues - remove simulated upload, fix department mapping, add upload modal
Fixed issues: - Remove simulateUpload function from DashboardPage Step 3 - Map department to description field when creating KB - Add upload modal in WorkspacePage knowledge assets tab - Fix DocumentUpload import path (../../stores to ../stores) Known issue: Dify API validation error during document upload (file uploaded but DB record failed, needs investigation) Testing: KB creation works, upload dialog opens correctly
This commit is contained in:
@@ -42,3 +42,6 @@ Status: Day 1 complete (11/11 tasks), ready for Day 2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -268,6 +268,9 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -218,3 +218,6 @@ https://iit.xunzhengyixue.com/api/v1/iit/health
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -147,3 +147,6 @@ https://iit.xunzhengyixue.com/api/v1/iit/health
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,3 +48,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -308,3 +308,6 @@ npx tsx src/modules/iit-manager/test-patient-wechat-url-verify.ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -170,3 +170,6 @@ npm run dev
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,3 +47,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -41,3 +41,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,3 +36,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -68,3 +68,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -31,3 +31,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -72,3 +72,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,3 +19,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -107,3 +107,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -78,3 +78,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -64,3 +64,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -106,3 +106,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,3 +17,6 @@ VALUES (
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -49,3 +49,6 @@ VALUES (
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ WHERE table_schema = 'dc_schema'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -101,6 +101,9 @@ ORDER BY ordinal_position;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -114,6 +114,9 @@ runMigration()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ COMMENT ON COLUMN "dc_schema"."dc_tool_c_sessions"."column_mapping" IS '列名
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@ COMMENT ON COLUMN dc_schema.dc_tool_c_sessions.expires_at IS '过期时间(创
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -115,6 +115,9 @@ Write-Host ""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -225,6 +225,9 @@ function extractCodeBlocks(obj, blocks = []) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,3 +26,6 @@ CREATE TABLE IF NOT EXISTS platform_schema.job_common (
|
||||
policy text
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -100,3 +100,6 @@ CREATE OR REPLACE FUNCTION platform_schema.delete_queue(queue_name text) RETURNS
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -244,6 +244,9 @@ checkDCTables();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
-- Create capability_schema for Prompt Management System
|
||||
CREATE SCHEMA IF NOT EXISTS capability_schema;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -196,6 +196,9 @@ createAiHistoryTable()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -183,6 +183,9 @@ createToolCTable()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -180,6 +180,9 @@ createToolCTable()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -111,3 +111,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -331,3 +331,6 @@ runTests().catch(error => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,3 +77,6 @@ async function testAPI() {
|
||||
|
||||
testAPI().catch(console.error);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -296,3 +296,6 @@ verifySchemas()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ export async function logout(
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前用户可访问的模块
|
||||
* <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ǰ<EFBFBD>û<EFBFBD><EFBFBD>ɷ<EFBFBD><EFBFBD>ʵ<EFBFBD>ģ<EFBFBD><EFBFBD>
|
||||
*
|
||||
* GET /api/v1/auth/me/modules
|
||||
*/
|
||||
@@ -248,11 +248,11 @@ export async function getUserModules(
|
||||
return reply.status(401).send({
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
message: '未认证',
|
||||
message: 'δ<EFBFBD><EFBFBD>֤',
|
||||
});
|
||||
}
|
||||
|
||||
// SUPER_ADMIN 和 PROMPT_ENGINEER 可以访问所有模块
|
||||
// SUPER_ADMIN <EFBFBD><EFBFBD> PROMPT_ENGINEER <EFBFBD><EFBFBD><EFBFBD>Է<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD>
|
||||
if (request.user.role === 'SUPER_ADMIN' || request.user.role === 'PROMPT_ENGINEER') {
|
||||
const { moduleService } = await import('./module.service.js');
|
||||
const allModules = await moduleService.getAllModules();
|
||||
@@ -270,8 +270,8 @@ export async function getUserModules(
|
||||
data: result.modules,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '获取用户模块失败';
|
||||
logger.error('获取用户模块失败', { error: message, userId: request.user?.userId });
|
||||
const message = error instanceof Error ? error.message : '<EFBFBD><EFBFBD>ȡ<EFBFBD>û<EFBFBD>ģ<EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>';
|
||||
logger.error('<EFBFBD><EFBFBD>ȡ<EFBFBD>û<EFBFBD>ģ<EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>', { error: message, userId: request.user?.userId });
|
||||
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
|
||||
@@ -184,3 +184,6 @@ export class JWTService {
|
||||
// 导出单例
|
||||
export const jwtService = new JWTService();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -312,6 +312,9 @@ export function getBatchItems<T>(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -100,3 +100,4 @@ export function getAllFallbackCodes(): string[] {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,3 +69,4 @@ export interface VariableValidation {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -75,3 +75,4 @@ export async function moduleRoutes(fastify: FastifyInstance) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -105,3 +105,4 @@ export interface PaginatedResponse<T> {
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,17 @@ import { logger } from '../../../common/logging/index.js';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { startScreeningTask } from '../services/screeningService.js';
|
||||
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入文献(从Excel或JSON)
|
||||
*/
|
||||
@@ -17,7 +28,7 @@ export async function importLiteratures(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId, literatures } = request.body;
|
||||
|
||||
// 验证项目归属
|
||||
@@ -90,7 +101,7 @@ export async function importLiteraturesFromExcel(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
// 获取上传的文件
|
||||
const data = await request.file();
|
||||
@@ -176,7 +187,7 @@ export async function getLiteratures(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
const { page = 1, limit = 50 } = request.query;
|
||||
|
||||
@@ -239,7 +250,7 @@ export async function deleteLiterature(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { literatureId } = request.params;
|
||||
|
||||
// 验证文献归属
|
||||
|
||||
@@ -7,16 +7,26 @@ import { CreateScreeningProjectDto } from '../types/index.js';
|
||||
import { prisma } from '../../../config/database.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建筛选项目
|
||||
*/
|
||||
export async function createProject(
|
||||
request: FastifyRequest<{ Body: CreateScreeningProjectDto & { userId?: string } }>,
|
||||
request: FastifyRequest<{ Body: CreateScreeningProjectDto }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
// 临时测试模式:优先从JWT获取,否则从请求体获取
|
||||
const userId = (request as any).userId || (request.body as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectName, picoCriteria, inclusionCriteria, exclusionCriteria, screeningConfig } = request.body;
|
||||
|
||||
// 验证必填字段
|
||||
@@ -65,7 +75,7 @@ export async function createProject(
|
||||
*/
|
||||
export async function getProjects(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
const projects = await prisma.aslScreeningProject.findMany({
|
||||
where: { userId },
|
||||
@@ -100,7 +110,7 @@ export async function getProjectById(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
|
||||
const project = await prisma.aslScreeningProject.findFirst({
|
||||
@@ -148,7 +158,7 @@ export async function updateProject(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
const updateData = request.body;
|
||||
|
||||
@@ -190,7 +200,7 @@ export async function deleteProject(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
|
||||
// 验证项目归属
|
||||
|
||||
@@ -6,6 +6,17 @@ import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { prisma } from '../../../config/database.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取筛选任务进度
|
||||
* GET /api/v1/asl/projects/:projectId/screening-task
|
||||
@@ -15,7 +26,7 @@ export async function getScreeningTask(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
|
||||
// 验证项目归属
|
||||
@@ -74,7 +85,7 @@ export async function getScreeningResults(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
const page = parseInt(request.query.page || '1', 10);
|
||||
const pageSize = parseInt(request.query.pageSize || '50', 10);
|
||||
@@ -175,7 +186,7 @@ export async function getScreeningResultDetail(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { resultId } = request.params;
|
||||
|
||||
const result = await prisma.aslScreeningResult.findUnique({
|
||||
@@ -241,7 +252,7 @@ export async function reviewScreeningResult(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { resultId } = request.params;
|
||||
const { decision, note } = request.body;
|
||||
|
||||
@@ -327,7 +338,7 @@ export async function getProjectStatistics(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
const { projectId } = request.params;
|
||||
|
||||
// 1. 验证项目归属
|
||||
|
||||
@@ -348,6 +348,9 @@ runTests().catch((error) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -289,6 +289,9 @@ runTest()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -327,6 +327,9 @@ Content-Type: application/json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,17 @@ import { FulltextScreeningService } from '../services/FulltextScreeningService.j
|
||||
// 初始化服务
|
||||
const screeningService = new FulltextScreeningService();
|
||||
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
// ==================== Zod验证Schema ====================
|
||||
|
||||
/**
|
||||
@@ -79,7 +90,7 @@ export async function createTask(
|
||||
const { projectId, literatureIds, modelA, modelB, promptVersion } = validated;
|
||||
|
||||
// 获取当前用户ID(测试模式)
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
logger.info('Creating fulltext screening task', {
|
||||
projectId,
|
||||
@@ -204,7 +215,7 @@ export async function getTaskProgress(
|
||||
) {
|
||||
try {
|
||||
const { taskId } = request.params;
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
// 获取任务详情
|
||||
const task = await prisma.aslFulltextScreeningTask.findFirst({
|
||||
@@ -318,7 +329,7 @@ export async function getTaskResults(
|
||||
) {
|
||||
try {
|
||||
const { taskId } = request.params;
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
// 参数验证
|
||||
const validated = GetResultsQuerySchema.parse(request.query);
|
||||
@@ -507,7 +518,7 @@ export async function updateDecision(
|
||||
) {
|
||||
try {
|
||||
const { resultId } = request.params;
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
// 参数验证
|
||||
const validated = UpdateDecisionSchema.parse(request.body);
|
||||
@@ -589,7 +600,7 @@ export async function exportExcel(
|
||||
) {
|
||||
try {
|
||||
const { taskId } = request.params;
|
||||
const userId = (request as any).userId || 'asl-test-user-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
// 验证任务权限
|
||||
const task = await prisma.aslFulltextScreeningTask.findFirst({
|
||||
|
||||
@@ -267,5 +267,6 @@ export const conflictDetectionService = new ConflictDetectionService();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -217,5 +217,6 @@ curl -X POST http://localhost:3000/api/v1/dc/tool-c/test/execute \
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -271,5 +271,6 @@ export const streamAIController = new StreamAIController();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -180,3 +180,6 @@ logger.info('[SessionMemory] 会话记忆管理器已启动', {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -114,3 +114,6 @@ checkTableStructure();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -101,3 +101,6 @@ checkProjectConfig().catch(console.error);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -83,3 +83,6 @@ main();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -540,3 +540,6 @@ URL: https://iit.xunzhengyixue.com/api/v1/iit/patient-wechat/callback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -175,3 +175,6 @@ console.log('');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -492,3 +492,6 @@ export const patientWechatService = new PatientWechatService();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -137,3 +137,6 @@ testDifyIntegration().catch(error => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -166,3 +166,6 @@ testIitDatabase()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -152,3 +152,6 @@ if (hasError) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -178,3 +178,6 @@ async function testUrlVerification() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -259,3 +259,6 @@ main().catch((error) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -143,3 +143,6 @@ Write-Host ""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -236,3 +236,6 @@ export interface CachedProtocolRules {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,17 @@ import { executeBatchTask, retryFailedDocuments, BatchProgress } from '../servic
|
||||
import { prisma } from '../../../config/database.js';
|
||||
import { ModelType } from '../../../common/llm/adapters/types.js';
|
||||
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
interface ExecuteBatchBody {
|
||||
@@ -40,8 +51,7 @@ export async function executeBatch(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
// TODO: 从JWT获取userId
|
||||
const userId = 'user-mock-001';
|
||||
const userId = getUserId(request);
|
||||
|
||||
const {
|
||||
kb_id,
|
||||
@@ -365,7 +375,7 @@ export async function retryFailed(
|
||||
) {
|
||||
try {
|
||||
const { taskId } = request.params;
|
||||
const userId = 'user-mock-001'; // TODO: 从JWT获取
|
||||
const userId = getUserId(request);
|
||||
|
||||
// 获取WebSocket实例
|
||||
const io = (request.server as any).io;
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import * as documentService from '../services/documentService.js';
|
||||
|
||||
// Mock用户ID(实际应从JWT token中获取)
|
||||
const MOCK_USER_ID = 'user-mock-001';
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文档
|
||||
@@ -69,8 +77,9 @@ export async function uploadDocument(
|
||||
|
||||
// 上传文档(这里fileUrl暂时为空,实际应该上传到对象存储)
|
||||
console.log(`⚙️ 调用文档服务上传文件...`);
|
||||
const userId = getUserId(request);
|
||||
const document = await documentService.uploadDocument(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
kbId,
|
||||
file,
|
||||
filename,
|
||||
@@ -123,7 +132,8 @@ export async function getDocuments(
|
||||
try {
|
||||
const { kbId } = request.params;
|
||||
|
||||
const documents = await documentService.getDocuments(MOCK_USER_ID, kbId);
|
||||
const userId = getUserId(request);
|
||||
const documents = await documentService.getDocuments(userId, kbId);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
@@ -160,7 +170,8 @@ export async function getDocumentById(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
const document = await documentService.getDocumentById(MOCK_USER_ID, id);
|
||||
const userId = getUserId(request);
|
||||
const document = await documentService.getDocumentById(userId, id);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
@@ -197,7 +208,8 @@ export async function deleteDocument(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
await documentService.deleteDocument(MOCK_USER_ID, id);
|
||||
const userId = getUserId(request);
|
||||
await documentService.deleteDocument(userId, id);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
@@ -234,7 +246,8 @@ export async function reprocessDocument(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
await documentService.reprocessDocument(MOCK_USER_ID, id);
|
||||
const userId = getUserId(request);
|
||||
await documentService.reprocessDocument(userId, id);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
@@ -271,7 +284,8 @@ export async function getDocumentFullText(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
const document = await documentService.getDocumentById(MOCK_USER_ID, id);
|
||||
const userId = getUserId(request);
|
||||
const document = await documentService.getDocumentById(userId, id);
|
||||
|
||||
// 返回完整的文档信息
|
||||
return reply.send({
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import * as knowledgeBaseService from '../services/knowledgeBaseService.js';
|
||||
|
||||
// Mock用户ID(实际应从JWT token中获取)
|
||||
const MOCK_USER_ID = 'user-mock-001';
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建知识库
|
||||
@@ -26,8 +34,9 @@ export async function createKnowledgeBase(
|
||||
});
|
||||
}
|
||||
|
||||
const userId = getUserId(request);
|
||||
const knowledgeBase = await knowledgeBaseService.createKnowledgeBase(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
name,
|
||||
description
|
||||
);
|
||||
@@ -49,12 +58,13 @@ export async function createKnowledgeBase(
|
||||
* 获取知识库列表
|
||||
*/
|
||||
export async function getKnowledgeBases(
|
||||
_request: FastifyRequest,
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = getUserId(request);
|
||||
const knowledgeBases = await knowledgeBaseService.getKnowledgeBases(
|
||||
MOCK_USER_ID
|
||||
userId
|
||||
);
|
||||
|
||||
return reply.send({
|
||||
@@ -84,8 +94,9 @@ export async function getKnowledgeBaseById(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
const userId = getUserId(request);
|
||||
const knowledgeBase = await knowledgeBaseService.getKnowledgeBaseById(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
id
|
||||
);
|
||||
|
||||
@@ -129,8 +140,9 @@ export async function updateKnowledgeBase(
|
||||
const { id } = request.params;
|
||||
const updateData = request.body;
|
||||
|
||||
const userId = getUserId(request);
|
||||
const knowledgeBase = await knowledgeBaseService.updateKnowledgeBase(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
id,
|
||||
updateData
|
||||
);
|
||||
@@ -170,7 +182,8 @@ export async function deleteKnowledgeBase(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
await knowledgeBaseService.deleteKnowledgeBase(MOCK_USER_ID, id);
|
||||
const userId = getUserId(request);
|
||||
await knowledgeBaseService.deleteKnowledgeBase(userId, id);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
@@ -221,8 +234,9 @@ export async function searchKnowledgeBase(
|
||||
|
||||
const topK = top_k ? parseInt(top_k, 10) : 15; // Phase 1优化:默认从3增加到15
|
||||
|
||||
const userId = getUserId(request);
|
||||
const results = await knowledgeBaseService.searchKnowledgeBase(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
id,
|
||||
query,
|
||||
topK
|
||||
@@ -263,8 +277,9 @@ export async function getKnowledgeBaseStats(
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
const userId = getUserId(request);
|
||||
const stats = await knowledgeBaseService.getKnowledgeBaseStats(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
id
|
||||
);
|
||||
|
||||
@@ -311,8 +326,9 @@ export async function getDocumentSelection(
|
||||
const maxFiles = max_files ? parseInt(max_files, 10) : undefined;
|
||||
const maxTokens = max_tokens ? parseInt(max_tokens, 10) : undefined;
|
||||
|
||||
const userId = getUserId(request);
|
||||
const selection = await knowledgeBaseService.getDocumentSelection(
|
||||
MOCK_USER_ID,
|
||||
userId,
|
||||
id,
|
||||
maxFiles,
|
||||
maxTokens
|
||||
|
||||
@@ -51,3 +51,4 @@ export default async function healthRoutes(fastify: FastifyInstance) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,14 @@ export async function createKnowledgeBase(
|
||||
}
|
||||
|
||||
// 2. 在Dify中创建Dataset
|
||||
// Dify API name字段限制:避免特殊字符,保持简洁
|
||||
const sanitizedName = name
|
||||
.replace(/[^\u4e00-\u9fa5a-zA-Z0-9_-]/g, '_') // 移除特殊字符
|
||||
.substring(0, 50); // 限制长度
|
||||
|
||||
const difyDataset = await difyClient.createDataset({
|
||||
name: `${userId}_${name}_${Date.now()}`,
|
||||
description: description || `Knowledge base for user ${userId}`,
|
||||
name: `kb_${sanitizedName}_${Date.now()}`, // 简化格式
|
||||
description: description?.substring(0, 200) || '', // 限制描述长度
|
||||
indexing_technique: 'high_quality',
|
||||
});
|
||||
|
||||
|
||||
@@ -127,3 +127,6 @@ Content-Type: application/json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -112,3 +112,6 @@ Write-Host " - 删除任务: DELETE $BaseUrl/api/v2/rvw/tasks/{taskId}" -Foregr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,16 +15,16 @@ import { ModelType } from '../../../common/llm/adapters/types.js';
|
||||
import * as reviewService from '../services/reviewService.js';
|
||||
import { AgentType } from '../types/index.js';
|
||||
|
||||
// TODO: 集成JWT认证后移除
|
||||
const MOCK_USER_ID = 'user-mock-001';
|
||||
|
||||
/**
|
||||
* 获取用户ID(暂时使用Mock,待JWT集成)
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
* 如果未认证则抛出错误
|
||||
*/
|
||||
function getUserId(request: FastifyRequest): string {
|
||||
// TODO: 从JWT token中获取
|
||||
// return request.user?.id;
|
||||
return MOCK_USER_ID;
|
||||
const userId = (request as any).user?.userId;
|
||||
if (!userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
// ==================== 任务创建 ====================
|
||||
@@ -121,12 +121,20 @@ export async function createTask(
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[RVW:Controller] 上传失败', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
logger.error('[RVW:Controller] 上传失败', { error: errorMessage });
|
||||
|
||||
// 区分认证错误和其他错误
|
||||
if (errorMessage === 'User not authenticated') {
|
||||
return reply.status(401).send({
|
||||
success: false,
|
||||
message: '用户未认证,请重新登录',
|
||||
});
|
||||
}
|
||||
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : '上传失败',
|
||||
message: errorMessage || '上传失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,3 +26,6 @@ export * from './services/utils.js';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +1,44 @@
|
||||
/**
|
||||
* RVW稿件审查模块 - 稿约规范性评估服务
|
||||
* @module rvw/services/editorialService
|
||||
*
|
||||
* Phase 3.5.5 改造:使用 PromptService 替代文件读取
|
||||
* - 支持灰度预览(调试者看 DRAFT,普通用户看 ACTIVE)
|
||||
* - 三级容灾(数据库→缓存→兜底)
|
||||
*/
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { LLMFactory } from '../../../common/llm/adapters/LLMFactory.js';
|
||||
import { ModelType } from '../../../common/llm/adapters/types.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
import { prisma } from '../../../config/database.js';
|
||||
import { getPromptService } from '../../../common/prompt/index.js';
|
||||
import { EditorialReview } from '../types/index.js';
|
||||
import { parseJSONFromLLMResponse } from './utils.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Prompt文件路径
|
||||
const PROMPT_PATH = path.join(__dirname, '../../../../prompts/review_editorial_system.txt');
|
||||
|
||||
/**
|
||||
* 稿约规范性评估
|
||||
* @param text 稿件文本
|
||||
* @param modelType 模型类型
|
||||
* @param userId 用户ID(用于灰度预览判断)
|
||||
* @returns 评估结果
|
||||
*/
|
||||
export async function reviewEditorialStandards(
|
||||
text: string,
|
||||
modelType: ModelType = 'deepseek-v3'
|
||||
modelType: ModelType = 'deepseek-v3',
|
||||
userId?: string
|
||||
): Promise<EditorialReview> {
|
||||
try {
|
||||
// 1. 读取系统Prompt
|
||||
const systemPrompt = await fs.readFile(PROMPT_PATH, 'utf-8');
|
||||
// 1. 从 PromptService 获取系统Prompt(支持灰度预览)
|
||||
const promptService = getPromptService(prisma);
|
||||
const { content: systemPrompt, isDraft } = await promptService.get(
|
||||
'RVW_EDITORIAL',
|
||||
{},
|
||||
{ userId }
|
||||
);
|
||||
|
||||
if (isDraft) {
|
||||
logger.info('[RVW:Editorial] 使用 DRAFT 版本 Prompt(调试模式)', { userId });
|
||||
}
|
||||
|
||||
// 2. 构建消息
|
||||
const messages = [
|
||||
@@ -71,3 +79,4 @@ export async function reviewEditorialStandards(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +1,44 @@
|
||||
/**
|
||||
* RVW稿件审查模块 - 方法学评估服务
|
||||
* @module rvw/services/methodologyService
|
||||
*
|
||||
* Phase 3.5.5 改造:使用 PromptService 替代文件读取
|
||||
* - 支持灰度预览(调试者看 DRAFT,普通用户看 ACTIVE)
|
||||
* - 三级容灾(数据库→缓存→兜底)
|
||||
*/
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { LLMFactory } from '../../../common/llm/adapters/LLMFactory.js';
|
||||
import { ModelType } from '../../../common/llm/adapters/types.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
import { prisma } from '../../../config/database.js';
|
||||
import { getPromptService } from '../../../common/prompt/index.js';
|
||||
import { MethodologyReview } from '../types/index.js';
|
||||
import { parseJSONFromLLMResponse } from './utils.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Prompt文件路径
|
||||
const PROMPT_PATH = path.join(__dirname, '../../../../prompts/review_methodology_system.txt');
|
||||
|
||||
/**
|
||||
* 方法学评估
|
||||
* @param text 稿件文本
|
||||
* @param modelType 模型类型
|
||||
* @param userId 用户ID(用于灰度预览判断)
|
||||
* @returns 评估结果
|
||||
*/
|
||||
export async function reviewMethodology(
|
||||
text: string,
|
||||
modelType: ModelType = 'deepseek-v3'
|
||||
modelType: ModelType = 'deepseek-v3',
|
||||
userId?: string
|
||||
): Promise<MethodologyReview> {
|
||||
try {
|
||||
// 1. 读取系统Prompt
|
||||
const systemPrompt = await fs.readFile(PROMPT_PATH, 'utf-8');
|
||||
// 1. 从 PromptService 获取系统Prompt(支持灰度预览)
|
||||
const promptService = getPromptService(prisma);
|
||||
const { content: systemPrompt, isDraft } = await promptService.get(
|
||||
'RVW_METHODOLOGY',
|
||||
{},
|
||||
{ userId }
|
||||
);
|
||||
|
||||
if (isDraft) {
|
||||
logger.info('[RVW:Methodology] 使用 DRAFT 版本 Prompt(调试模式)', { userId });
|
||||
}
|
||||
|
||||
// 2. 构建消息
|
||||
const messages = [
|
||||
@@ -71,3 +79,4 @@ export async function reviewMethodology(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -117,3 +117,6 @@ export function validateAgentSelection(agents: string[]): void {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ export function registerReviewWorker() {
|
||||
logger.info('[reviewWorker] Running editorial review', { taskId });
|
||||
console.log(' 🔍 运行稿约规范性智能体...');
|
||||
|
||||
editorialResult = await reviewEditorialStandards(extractedText, modelType);
|
||||
// ✅ Phase 3.5.5: 传递 userId 支持灰度预览
|
||||
editorialResult = await reviewEditorialStandards(extractedText, modelType, userId);
|
||||
|
||||
logger.info('[reviewWorker] Editorial review completed', {
|
||||
taskId,
|
||||
@@ -97,7 +98,8 @@ export function registerReviewWorker() {
|
||||
logger.info('[reviewWorker] Running methodology review', { taskId });
|
||||
console.log(' 🔬 运行方法学智能体...');
|
||||
|
||||
methodologyResult = await reviewMethodology(extractedText, modelType);
|
||||
// ✅ Phase 3.5.5: 传递 userId 支持灰度预览
|
||||
methodologyResult = await reviewMethodology(extractedText, modelType, userId);
|
||||
|
||||
logger.info('[reviewWorker] Methodology review completed', {
|
||||
taskId,
|
||||
|
||||
@@ -413,6 +413,9 @@ SET session_replication_role = 'origin';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -115,6 +115,9 @@ WHERE key = 'verify_test';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -258,6 +258,9 @@ verifyDatabase()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3
backend/src/types/global.d.ts
vendored
3
backend/src/types/global.d.ts
vendored
@@ -48,6 +48,9 @@ export {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ Write-Host "✅ 完成!" -ForegroundColor Green
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast') ORDER BY schema_name;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -163,3 +163,6 @@ DELETE {{baseUrl}}/api/v2/pkb/knowledge/knowledge-bases/{{testKbId}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -358,6 +358,9 @@ runAdvancedTests().catch(error => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -424,6 +424,9 @@ runAllTests()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -382,6 +382,9 @@ runAllTests()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,3 +20,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,3 +18,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,3 +30,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,3 +19,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -159,3 +159,6 @@ main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
backup_aia_migration_20260111_223208.sql
Normal file
BIN
backup_aia_migration_20260111_223208.sql
Normal file
Binary file not shown.
@@ -166,6 +166,9 @@ Set-Location ..
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -608,6 +608,9 @@ async saveProcessedData(recordId, newData) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user