feat(ssa): Complete T-test end-to-end testing with 9 bug fixes - Phase 1 core 85% complete. R service: missing value auto-filter. Backend: error handling, variable matching, dynamic filename. Frontend: module activation, session isolation, error propagation. Full flow verified.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-19 20:57:00 +08:00
parent 8137e3cde2
commit 49b5c37cb1
86 changed files with 21207 additions and 252 deletions

View File

@@ -1,8 +1,77 @@
-- Add knowledge_config field to prompt_templates
ALTER TABLE "capability_schema"."prompt_templates" ADD COLUMN "knowledge_config" JSONB;
-- =============================================
-- FIX: Create prompt_templates and prompt_versions tables if they don't exist
-- Background: These tables were originally created via `prisma db push`
-- (bypassing migrations), causing shadow database validation failures.
-- This fix ensures migrations can replay cleanly from scratch.
-- =============================================
-- Create PromptStatus enum if not exists
DO $$ BEGIN
CREATE TYPE "capability_schema"."PromptStatus" AS ENUM ('DRAFT', 'ACTIVE', 'ARCHIVED');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- CreateTable: prompt_templates (if not exists)
CREATE TABLE IF NOT EXISTS "capability_schema"."prompt_templates" (
"id" SERIAL NOT NULL,
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"module" TEXT NOT NULL,
"description" TEXT,
"variables" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "prompt_templates_pkey" PRIMARY KEY ("id")
);
-- CreateIndex (if not exists)
CREATE UNIQUE INDEX IF NOT EXISTS "prompt_templates_code_key" ON "capability_schema"."prompt_templates"("code");
CREATE INDEX IF NOT EXISTS "idx_prompt_templates_module" ON "capability_schema"."prompt_templates"("module");
-- CreateTable: prompt_versions (if not exists)
CREATE TABLE IF NOT EXISTS "capability_schema"."prompt_versions" (
"id" SERIAL NOT NULL,
"template_id" INTEGER NOT NULL,
"version" INTEGER NOT NULL,
"content" TEXT NOT NULL,
"model_config" JSONB,
"status" "capability_schema"."PromptStatus" NOT NULL DEFAULT 'DRAFT',
"changelog" TEXT,
"created_by" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "prompt_versions_pkey" PRIMARY KEY ("id")
);
-- CreateIndex (if not exists)
CREATE INDEX IF NOT EXISTS "idx_prompt_versions_template_status" ON "capability_schema"."prompt_versions"("template_id", "status");
CREATE INDEX IF NOT EXISTS "idx_prompt_versions_status" ON "capability_schema"."prompt_versions"("status");
-- AddForeignKey (if not exists)
DO $$ BEGIN
ALTER TABLE "capability_schema"."prompt_versions"
ADD CONSTRAINT "prompt_versions_template_id_fkey"
FOREIGN KEY ("template_id") REFERENCES "capability_schema"."prompt_templates"("id")
ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- =============================================
-- Original migration content starts here
-- =============================================
-- Add knowledge_config field to prompt_templates (if not exists)
DO $$ BEGIN
ALTER TABLE "capability_schema"."prompt_templates" ADD COLUMN "knowledge_config" JSONB;
EXCEPTION
WHEN duplicate_column THEN null;
END $$;
-- CreateTable: system_knowledge_bases
CREATE TABLE "capability_schema"."system_knowledge_bases" (
CREATE TABLE IF NOT EXISTS "capability_schema"."system_knowledge_bases" (
"id" TEXT NOT NULL,
"code" VARCHAR(50) NOT NULL,
"name" VARCHAR(100) NOT NULL,
@@ -18,7 +87,7 @@ CREATE TABLE "capability_schema"."system_knowledge_bases" (
);
-- CreateTable: system_kb_documents
CREATE TABLE "capability_schema"."system_kb_documents" (
CREATE TABLE IF NOT EXISTS "capability_schema"."system_kb_documents" (
"id" TEXT NOT NULL,
"kb_id" TEXT NOT NULL,
"filename" VARCHAR(255) NOT NULL,
@@ -35,20 +104,19 @@ CREATE TABLE "capability_schema"."system_kb_documents" (
CONSTRAINT "system_kb_documents_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "system_knowledge_bases_code_key" ON "capability_schema"."system_knowledge_bases"("code");
-- CreateIndex (if not exists)
CREATE UNIQUE INDEX IF NOT EXISTS "system_knowledge_bases_code_key" ON "capability_schema"."system_knowledge_bases"("code");
CREATE INDEX IF NOT EXISTS "idx_system_kb_category" ON "capability_schema"."system_knowledge_bases"("category");
CREATE INDEX IF NOT EXISTS "idx_system_kb_status" ON "capability_schema"."system_knowledge_bases"("status");
CREATE INDEX IF NOT EXISTS "idx_system_kb_docs_kb_id" ON "capability_schema"."system_kb_documents"("kb_id");
CREATE INDEX IF NOT EXISTS "idx_system_kb_docs_status" ON "capability_schema"."system_kb_documents"("status");
-- CreateIndex
CREATE INDEX "idx_system_kb_category" ON "capability_schema"."system_knowledge_bases"("category");
-- CreateIndex
CREATE INDEX "idx_system_kb_status" ON "capability_schema"."system_knowledge_bases"("status");
-- CreateIndex
CREATE INDEX "idx_system_kb_docs_kb_id" ON "capability_schema"."system_kb_documents"("kb_id");
-- CreateIndex
CREATE INDEX "idx_system_kb_docs_status" ON "capability_schema"."system_kb_documents"("status");
-- AddForeignKey
ALTER TABLE "capability_schema"."system_kb_documents" ADD CONSTRAINT "system_kb_documents_kb_id_fkey" FOREIGN KEY ("kb_id") REFERENCES "capability_schema"."system_knowledge_bases"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey (if not exists)
DO $$ BEGIN
ALTER TABLE "capability_schema"."system_kb_documents"
ADD CONSTRAINT "system_kb_documents_kb_id_fkey"
FOREIGN KEY ("kb_id") REFERENCES "capability_schema"."system_knowledge_bases"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

View File

@@ -2,6 +2,9 @@
-- 创建 8 张新表用于支持完整的 Agent 功能
-- 日期: 2026-02-07
-- Enable pgvector extension for embedding support
CREATE EXTENSION IF NOT EXISTS vector;
-- CreateTable: iit_skills (Skill 配置存储)
CREATE TABLE "iit_schema"."skills" (
"id" TEXT NOT NULL,

View File

@@ -0,0 +1,164 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "ssa_schema";
-- CreateTable: ssa_sessions
CREATE TABLE "ssa_schema"."ssa_sessions" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"title" TEXT,
"data_schema" JSONB,
"data_payload" JSONB,
"data_oss_key" TEXT,
"status" TEXT NOT NULL DEFAULT 'active',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ssa_sessions_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_messages
CREATE TABLE "ssa_schema"."ssa_messages" (
"id" TEXT NOT NULL,
"session_id" TEXT NOT NULL,
"role" TEXT NOT NULL,
"content_type" TEXT NOT NULL,
"content" JSONB NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ssa_messages_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_tools_library
CREATE TABLE "ssa_schema"."ssa_tools_library" (
"id" TEXT NOT NULL,
"tool_code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"version" TEXT NOT NULL DEFAULT '1.0.0',
"description" TEXT NOT NULL,
"usage_context" TEXT,
"params_schema" JSONB NOT NULL,
"guardrails" JSONB,
"search_text" TEXT NOT NULL,
"embedding" vector(1024),
"is_active" BOOLEAN NOT NULL DEFAULT true,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ssa_tools_library_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_execution_logs
CREATE TABLE "ssa_schema"."ssa_execution_logs" (
"id" TEXT NOT NULL,
"session_id" TEXT NOT NULL,
"message_id" TEXT,
"tool_code" TEXT NOT NULL,
"input_params" JSONB NOT NULL,
"output_status" TEXT NOT NULL,
"output_result" JSONB,
"trace_log" TEXT[],
"execution_ms" INTEGER,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ssa_execution_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_decision_table
CREATE TABLE "ssa_schema"."ssa_decision_table" (
"id" TEXT NOT NULL,
"goal_type" TEXT NOT NULL,
"y_type" TEXT NOT NULL,
"x_type" TEXT,
"design_type" TEXT NOT NULL,
"tool_code" TEXT NOT NULL,
"alt_tool_code" TEXT,
"priority" INTEGER NOT NULL DEFAULT 0,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ssa_decision_table_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_r_code_library
CREATE TABLE "ssa_schema"."ssa_r_code_library" (
"id" TEXT NOT NULL,
"tool_code" TEXT NOT NULL,
"version" TEXT NOT NULL DEFAULT '1.0.0',
"file_name" TEXT NOT NULL,
"code_content" TEXT NOT NULL,
"entry_func" TEXT NOT NULL DEFAULT 'run_analysis',
"description" TEXT,
"dependencies" TEXT[] DEFAULT ARRAY[]::TEXT[],
"is_active" BOOLEAN NOT NULL DEFAULT true,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ssa_r_code_library_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_param_mapping
CREATE TABLE "ssa_schema"."ssa_param_mapping" (
"id" TEXT NOT NULL,
"tool_code" TEXT NOT NULL,
"json_key" TEXT NOT NULL,
"r_param_name" TEXT NOT NULL,
"data_type" TEXT NOT NULL,
"is_required" BOOLEAN NOT NULL DEFAULT false,
"default_value" TEXT,
"validation_rule" TEXT,
"description" TEXT,
CONSTRAINT "ssa_param_mapping_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_guardrail_config
CREATE TABLE "ssa_schema"."ssa_guardrail_config" (
"id" TEXT NOT NULL,
"tool_code" TEXT NOT NULL,
"check_name" TEXT NOT NULL,
"check_order" INTEGER NOT NULL DEFAULT 0,
"check_code" TEXT NOT NULL,
"threshold" TEXT,
"action_type" TEXT NOT NULL,
"action_target" TEXT,
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "ssa_guardrail_config_pkey" PRIMARY KEY ("id")
);
-- CreateTable: ssa_interpretation_templates
CREATE TABLE "ssa_schema"."ssa_interpretation_templates" (
"id" TEXT NOT NULL,
"tool_code" TEXT NOT NULL,
"scenario_key" TEXT NOT NULL,
"template" TEXT NOT NULL,
"placeholders" TEXT[] DEFAULT ARRAY[]::TEXT[],
CONSTRAINT "ssa_interpretation_templates_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "idx_ssa_session_user" ON "ssa_schema"."ssa_sessions"("user_id");
CREATE INDEX "idx_ssa_session_status" ON "ssa_schema"."ssa_sessions"("status");
CREATE INDEX "idx_ssa_message_session" ON "ssa_schema"."ssa_messages"("session_id");
CREATE UNIQUE INDEX "ssa_tools_library_tool_code_key" ON "ssa_schema"."ssa_tools_library"("tool_code");
CREATE INDEX "idx_ssa_exec_log_session" ON "ssa_schema"."ssa_execution_logs"("session_id");
CREATE INDEX "idx_ssa_exec_log_tool" ON "ssa_schema"."ssa_execution_logs"("tool_code");
CREATE UNIQUE INDEX "uq_ssa_decision_table" ON "ssa_schema"."ssa_decision_table"("goal_type", "y_type", "x_type", "design_type");
CREATE INDEX "idx_ssa_rcode_tool" ON "ssa_schema"."ssa_r_code_library"("tool_code");
CREATE UNIQUE INDEX "uq_ssa_param_mapping" ON "ssa_schema"."ssa_param_mapping"("tool_code", "json_key");
CREATE INDEX "idx_ssa_guardrail_tool" ON "ssa_schema"."ssa_guardrail_config"("tool_code");
CREATE UNIQUE INDEX "uq_ssa_interpretation" ON "ssa_schema"."ssa_interpretation_templates"("tool_code", "scenario_key");
-- AddForeignKey
ALTER TABLE "ssa_schema"."ssa_messages" ADD CONSTRAINT "ssa_messages_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "ssa_schema"."ssa_sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ssa_schema"."ssa_execution_logs" ADD CONSTRAINT "ssa_execution_logs_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "ssa_schema"."ssa_sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -283,6 +283,7 @@ model ReviewTask {
methodologyScore Float? @map("methodology_score")
methodologyStatus String? @map("methodology_status")
picoExtract Json? @map("pico_extract")
contextData Json? @map("context_data") /// Skills V2.0 执行上下文数据
isArchived Boolean @default(false) @map("is_archived")
archivedAt DateTime? @map("archived_at")
modelUsed String? @map("model_used")
@@ -2138,3 +2139,170 @@ model ProtocolGeneration {
@@map("protocol_generations")
@@schema("protocol_schema")
}
// ============================================================
// SSA Schema (ssa_schema)
// 智能统计分析模块
// ============================================================
/// SSA 分析会话
model SsaSession {
id String @id @default(uuid())
userId String @map("user_id")
title String?
dataSchema Json? @map("data_schema") /// 数据结构LLM可见
dataPayload Json? @map("data_payload") /// 真实数据仅R可见
dataOssKey String? @map("data_oss_key") /// OSS 存储 key大数据
status String @default("active") /// active | consult | completed | error
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
messages SsaMessage[]
executionLogs SsaExecutionLog[]
@@index([userId], map: "idx_ssa_session_user")
@@index([status], map: "idx_ssa_session_status")
@@map("ssa_sessions")
@@schema("ssa_schema")
}
/// SSA 消息记录
model SsaMessage {
id String @id @default(uuid())
sessionId String @map("session_id")
role String /// user | assistant | system
contentType String @map("content_type") /// text | plan | result
content Json
createdAt DateTime @default(now()) @map("created_at")
session SsaSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
@@index([sessionId], map: "idx_ssa_message_session")
@@map("ssa_messages")
@@schema("ssa_schema")
}
/// SSA 工具库
model SsaTool {
id String @id @default(uuid())
toolCode String @unique @map("tool_code")
name String
version String @default("1.0.0")
description String
usageContext String? @map("usage_context")
paramsSchema Json @map("params_schema")
guardrails Json?
searchText String @map("search_text")
embedding Unsupported("vector(1024)")?
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("ssa_tools_library")
@@schema("ssa_schema")
}
/// SSA 执行日志
model SsaExecutionLog {
id String @id @default(uuid())
sessionId String @map("session_id")
messageId String? @map("message_id")
toolCode String @map("tool_code")
inputParams Json @map("input_params")
outputStatus String @map("output_status")
outputResult Json? @map("output_result")
traceLog String[] @map("trace_log")
executionMs Int? @map("execution_ms")
createdAt DateTime @default(now()) @map("created_at")
session SsaSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
@@index([sessionId], map: "idx_ssa_exec_log_session")
@@index([toolCode], map: "idx_ssa_exec_log_tool")
@@map("ssa_execution_logs")
@@schema("ssa_schema")
}
/// SSA 统计决策表
model SsaDecisionTable {
id String @id @default(uuid())
goalType String @map("goal_type") /// 分析目标
yType String @map("y_type") /// 因变量类型
xType String? @map("x_type") /// 自变量类型
designType String @map("design_type") /// 设计类型
toolCode String @map("tool_code") /// 推荐工具
altToolCode String? @map("alt_tool_code") /// 备选工具(降级)
priority Int @default(0) /// 优先级
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
@@unique([goalType, yType, xType, designType], map: "uq_ssa_decision_table")
@@map("ssa_decision_table")
@@schema("ssa_schema")
}
/// SSA R 代码库
model SsaRCodeLibrary {
id String @id @default(uuid())
toolCode String @map("tool_code") /// 关联工具代码
version String @default("1.0.0")
fileName String @map("file_name") /// R 脚本文件名
codeContent String @map("code_content") @db.Text /// R 代码内容
entryFunc String @default("run_analysis") @map("entry_func")
description String?
dependencies String[] @default([]) /// 依赖包列表
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([toolCode], map: "idx_ssa_rcode_tool")
@@map("ssa_r_code_library")
@@schema("ssa_schema")
}
/// SSA 参数映射配置
model SsaParamMapping {
id String @id @default(uuid())
toolCode String @map("tool_code")
jsonKey String @map("json_key") /// 前端传入的 JSON Key
rParamName String @map("r_param_name") /// R 函数参数名
dataType String @map("data_type") /// string | number | boolean
isRequired Boolean @default(false) @map("is_required")
defaultValue String? @map("default_value")
validationRule String? @map("validation_rule")
description String?
@@unique([toolCode, jsonKey], map: "uq_ssa_param_mapping")
@@map("ssa_param_mapping")
@@schema("ssa_schema")
}
/// SSA 护栏规则配置
model SsaGuardrailConfig {
id String @id @default(uuid())
toolCode String @map("tool_code")
checkName String @map("check_name") /// 检查名称
checkOrder Int @default(0) @map("check_order")
checkCode String @map("check_code") /// R 函数名
threshold String? /// 阈值条件
actionType String @map("action_type") /// Block | Warn | Switch
actionTarget String? @map("action_target") /// Switch 时的目标工具
isEnabled Boolean @default(true) @map("is_enabled")
@@index([toolCode], map: "idx_ssa_guardrail_tool")
@@map("ssa_guardrail_config")
@@schema("ssa_schema")
}
/// SSA 结果解读模板
model SsaInterpretation {
id String @id @default(uuid())
toolCode String @map("tool_code")
scenarioKey String @map("scenario_key") /// significant | not_significant
template String @db.Text /// 解读模板(含占位符)
placeholders String[] @default([]) /// 占位符列表
@@unique([toolCode, scenarioKey], map: "uq_ssa_interpretation")
@@map("ssa_interpretation_templates")
@@schema("ssa_schema")
}

View File

@@ -39,6 +39,22 @@ async function main() {
});
console.log(` ✅ 内部租户创建成功: ${internalTenant.name}`);
// 为内部租户开放所有模块(超级管理员完整权限)
const internalModules = ['AIA', 'ASL', 'PKB', 'DC', 'SSA', 'ST', 'RVW', 'IIT'];
for (const moduleCode of internalModules) {
await prisma.tenant_modules.upsert({
where: { tenant_id_module_code: { tenant_id: internalTenant.id, module_code: moduleCode } },
update: {},
create: {
id: uuidv4(),
tenant_id: internalTenant.id,
module_code: moduleCode,
is_enabled: true,
},
});
}
console.log(` ✅ 内部租户模块订阅: ${internalModules.join(', ')}`);
// ============================================
// 1.5 创建公共租户(个人用户池)
// ============================================