refactor(backend): incremental architecture evolution (Task 19)

- Add common/ layer for shared capabilities (LLM, RAG, document, middleware)
- Add legacy/ layer for existing business code
- Move files to new structure (controllers, routes, services)
- Update index.ts for new route registration
- System remains fully functional
This commit is contained in:
2025-11-16 15:42:44 +08:00
parent 8a17dc80ae
commit 0c5310fb77
39 changed files with 3904 additions and 353 deletions

View File

@@ -0,0 +1,152 @@
/**
* Phase 3: 批处理模式 - JSON解析工具
*
* AI的输出可能包含额外的文字说明需要提取JSON块并解析
*/
export interface ParseResult<T = any> {
success: boolean;
data?: T;
error?: string;
rawOutput: string;
}
/**
* 从AI输出中提取JSON块
*
* 支持的格式:
* 1. 纯JSON{ "key": "value" }
* 2. 带前言:这是提取结果:\n{ "key": "value" }
* 3. 带后缀:{ "key": "value" }\n\n以上是提取结果
* 4. 代码块:```json\n{ "key": "value" }\n```
*/
export function extractJSON(text: string): string | null {
// 尝试1直接查找 {...} 或 [...]
const jsonPattern = /(\{[\s\S]*\}|\[[\s\S]*\])/;
const match = text.match(jsonPattern);
if (match) {
return match[1];
}
// 尝试2查找代码块中的JSON
const codeBlockPattern = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
const codeMatch = text.match(codeBlockPattern);
if (codeMatch) {
return codeMatch[1].trim();
}
return null;
}
/**
* 解析JSON字符串
*
* @param jsonString JSON字符串
* @param expectedFields 期望的字段列表(可选,用于验证)
* @returns 解析结果
*/
export function parseJSON<T = any>(
jsonString: string,
expectedFields?: string[]
): ParseResult<T> {
const rawOutput = jsonString;
try {
// 提取JSON块
const extracted = extractJSON(jsonString);
if (!extracted) {
return {
success: false,
error: '未找到JSON格式的数据',
rawOutput,
};
}
// 解析JSON
const data = JSON.parse(extracted) as T;
// 验证字段如果提供了expectedFields
if (expectedFields && Array.isArray(expectedFields)) {
const missingFields: string[] = [];
for (const field of expectedFields) {
if (!(field in (data as any))) {
missingFields.push(field);
}
}
if (missingFields.length > 0) {
console.warn(`[JsonParser] 缺少字段: ${missingFields.join(', ')}`);
// 为缺失字段填充默认值
for (const field of missingFields) {
(data as any)[field] = '未提取到';
}
}
}
return {
success: true,
data,
rawOutput,
};
} catch (error: any) {
return {
success: false,
error: error.message,
rawOutput,
};
}
}
/**
* 验证JSON数据是否符合模板要求
*
* @param data 解析后的数据
* @param templateFields 模板字段定义
* @returns 是否有效
*/
export function validateTemplateData(
data: any,
templateFields: Array<{ key: string; type: string }>
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (!data || typeof data !== 'object') {
errors.push('数据不是有效的对象');
return { valid: false, errors };
}
for (const field of templateFields) {
const value = data[field.key];
// 检查字段是否存在
if (value === undefined || value === null || value === '') {
console.warn(`[JsonParser] 字段 ${field.key} 为空`);
// 不算错误,只是警告
}
// 类型检查(宽松)
if (field.type === 'number' && typeof value !== 'number' && value !== '') {
// 尝试转换
const num = Number(value);
if (!isNaN(num)) {
data[field.key] = num;
}
}
}
return { valid: errors.length === 0, errors };
}