feat(ssa): Complete V11 UI development and frontend-backend integration - Pixel-perfect V11 UI, multi-task support, Word export, input overlay fix, code cleanup. MVP Phase 1 core 95% complete.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-20 14:46:45 +08:00
parent 49b5c37cb1
commit 8d496d1515
38 changed files with 7255 additions and 1074 deletions

View File

@@ -155,8 +155,9 @@ export default async function analysisRoutes(app: FastifyInstance) {
});
// 返回前端期望的 AnalysisPlan 格式camelCase
const planId = `plan_${Date.now()}`;
const mockPlan = {
id: `plan_${Date.now()}`,
id: planId,
toolCode: 'ST_T_TEST_IND',
toolName: '独立样本 T 检验',
description: `根据您的数据特征和分析需求"${query}",推荐使用独立样本 T 检验,比较 ${groupVar} 分组下 ${valueVar} 的差异。`,
@@ -171,7 +172,27 @@ export default async function analysisRoutes(app: FastifyInstance) {
confidence: 0.85
};
logger.info('[SSA:Analysis] Plan generated', { sessionId: id, query, toolCode: mockPlan.toolCode, params: mockPlan.parameters });
// 保存用户查询消息
await prisma.ssaMessage.create({
data: {
sessionId: id,
role: 'user',
contentType: 'text',
content: { text: query }
}
});
// 保存 plan 消息(支持多任务历史)
await prisma.ssaMessage.create({
data: {
sessionId: id,
role: 'assistant',
contentType: 'plan',
content: { ...mockPlan, query } // 保存原始查询以便后续关联
}
});
logger.info('[SSA:Analysis] Plan generated and saved', { sessionId: id, planId, query, toolCode: mockPlan.toolCode, params: mockPlan.parameters });
return reply.send(mockPlan);
});
@@ -265,7 +286,34 @@ export default async function analysisRoutes(app: FastifyInstance) {
}
});
return reply.send(result);
// 转换 R 服务返回的 snake_case 为前端期望的 camelCase
const transformedResult = {
status: result.status,
message: result.message,
warnings: result.warnings,
results: result.results ? {
method: result.results.method,
statistic: result.results.statistic,
df: result.results.df,
pValue: result.results.p_value,
pValueFmt: result.results.p_value_fmt,
confInt: result.results.conf_int,
estimate: result.results.estimate,
groupStats: result.results.group_stats,
effectSize: result.results.effect_size,
} : null,
plots: result.plots?.map((p: any) =>
typeof p === 'string'
? { type: 'chart', title: '统计图表', imageBase64: p }
: p
),
traceLog: result.trace_log,
reproducibleCode: result.reproducible_code,
guardrailResults: result.guardrail_results,
executionMs: result.executionMs,
};
return reply.send(transformedResult);
} catch (error: any) {
logger.error('[SSA:Analysis] Execute failed', {