fix: V1 测试版本端到端验证修复 — 6 CRITICAL + 3 HIGH 问题全量修复

修复项:
- fix(db): 迁移 149 — 修复 Admin 角色权限绑定被迁移链破坏 (FE-C1)
- fix(health): 4 个 handler 添加空名称验证 — Doctor/Article/AlertRule/Tag (API-C1~C4)
- fix(health): Stats 仪表盘 new_this_week 查询修复 — SeaORM date_trunc bug (FE-C2)
- fix(server): 添加安全响应头 — X-Frame-Options/CSP/XSS-Protection/Referrer-Policy (SEC-H1)
- fix(mp): 预约创建契约修复 — notes/reason 字段映射 + 移除 schedule_id (MP-H1)
- fix(mp): 咨询会话 subject/last_message 字段改为可选 (MP-H3)
- fix(ai): AiConfig Default derive 替代手写 impl (clippy)

测试报告:
- 8 维度端到端测试全部完成 (后端 87 用例 / 前端 30 页面 / 小程序 80+ API / 安全 20 项 / 性能 20 端点)
- 多角色 7 角色 49 检查 100% 通过
- 综合测试报告 + 专家评估报告
This commit is contained in:
iven
2026-05-18 10:24:40 +08:00
parent 38b0d91407
commit d623f8b2ff
36 changed files with 5564 additions and 189 deletions

View File

@@ -4,9 +4,11 @@ use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, TenantContext};
use serde::{Deserialize, Serialize};
use crate::agent::orchestrator::AgentRunParams;
use crate::agent::tool::ToolContext;
use crate::agent::tools::QueryPatientVitalsTool;
use crate::agent::{AgentOrchestrator, ToolRegistry};
use crate::config_resolver;
use crate::dto::{ChatMessage, ChatMessageRole};
use crate::state::AiState;
@@ -33,38 +35,6 @@ pub struct ChatResponse {
pub iterations: usize,
}
const SYSTEM_PROMPT: &str = r#"你是 HMS 健康管理平台的 AI 健康顾问"小华"。
## 核心策略
根据用户表达的内容和情绪,自然地采用以下策略方向:
1. 【情绪安抚】当用户表达焦虑、恐惧、沮丧时:
- 先共情认可感受,不急于给建议
- 用通俗语言解释,避免医学术语
- 分享积极案例,降低恐惧感
2. 【医疗科普】当用户询问指标含义、疾病知识时:
- 调用 search_medical_knowledge 获取准确信息(如可用)
- 用比喻和类比让老年患者也能理解
- 强调"具体请以医生诊断为准"
3. 【服务推荐】当用户表达就医需求或身体不适时:
- 调用 query_appointments 查看已有预约(如可用)
- 主动提出帮用户预约
4. 【风险预警】当用户描述的症状或数据异常时:
- 调用 query_patient_vitals 查看体征数据
- 明确告知风险等级和需要注意的事项
- 高风险时建议尽快就医
5. 【引导到院】当用户有明确就诊意向或高风险预警时:
- 提供科室位置、出诊医生信息
- 建议用户联系前台预约
## 策略不是互斥的,你可以在一轮对话中自然切换。
## 永远不要:推荐具体药物、给出明确诊断、替代医生建议。
## 如果没有可用的工具数据,就基于常识回答,并建议用户咨询医生。"#;
#[utoipa::path(
post,
path = "/ai/chat",
@@ -96,6 +66,9 @@ where
let ai_state = AiState::from_ref(&state);
// 从 settings 表加载 AI 配置(替代硬编码)
let config = config_resolver::load_ai_config(ctx.tenant_id, &ai_state.db).await;
// 构建 Agent 消息历史
let mut messages = vec![];
@@ -152,18 +125,34 @@ where
health_provider: ai_state.health_provider.clone(),
};
let run_params = AgentRunParams {
model: config.agent.model,
temperature: config.agent.temperature,
max_tokens: config.agent.max_tokens,
max_iterations: config.agent.max_iterations,
};
tracing::info!(
tenant_id = %ctx.tenant_id,
user_id = %ctx.user_id,
patient_id = ?body.patient_id,
msg_len = message.len(),
model = %run_params.model,
temperature = run_params.temperature,
max_tokens = run_params.max_tokens,
max_iterations = run_params.max_iterations,
"AI Agent chat request"
);
// 执行 Agent ReAct 循环
let orchestrator = AgentOrchestrator::new(provider_arc, std::sync::Arc::new(registry));
let result = orchestrator
.run(SYSTEM_PROMPT, &mut messages, &tool_ctx)
.run(
&config.agent.system_prompt,
&mut messages,
&tool_ctx,
&run_params,
)
.await
.map_err(|e| {
tracing::error!(error = %e, "AI Agent run failed");