feat(ai): AI 健康管家 V2 基础设施 — 功能开关 + 角色沙箱准备 + 体征页 AI 趋势分析
- 迁移 000153: 新增 ai_feature_flags / ai_usage_daily / ai_suggestion_feedback 三张表, ai_tenant_configs 增加 billing_enabled 列, seed 12 个功能开关 + 2 个管理权限码 - 新增 FeatureFlagService: 5 分钟缓存 + DB 回退 + 即时更新 - VitalSignsTab 添加 AI 趋势分析按钮 (SSE 流式) - 新增 3 个 Entity (ai_feature_flags / ai_usage_daily / ai_suggestion_feedback) - AiState 扩展 feature_flags 字段 - 设计规格 + 讨论记录文档 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -13,11 +13,11 @@ use crate::entity::ai_analysis;
|
||||
use crate::error::{AiError, AiResult};
|
||||
use crate::knowledge::KnowledgeSource;
|
||||
use crate::prompt::PromptRenderer;
|
||||
use crate::provider::AiProvider;
|
||||
use crate::provider::registry::ProviderRegistry;
|
||||
use crate::sanitization::SanitizationService;
|
||||
|
||||
pub struct AnalysisService {
|
||||
pub provider: Box<dyn AiProvider>,
|
||||
pub provider_registry: std::sync::Arc<ProviderRegistry>,
|
||||
pub sanitizer: SanitizationService,
|
||||
pub renderer: PromptRenderer,
|
||||
pub db: sea_orm::DatabaseConnection,
|
||||
@@ -25,9 +25,12 @@ pub struct AnalysisService {
|
||||
}
|
||||
|
||||
impl AnalysisService {
|
||||
pub fn new(provider: Box<dyn AiProvider>, db: sea_orm::DatabaseConnection) -> Self {
|
||||
pub fn new(
|
||||
provider_registry: std::sync::Arc<ProviderRegistry>,
|
||||
db: sea_orm::DatabaseConnection,
|
||||
) -> Self {
|
||||
Self {
|
||||
provider,
|
||||
provider_registry,
|
||||
sanitizer: SanitizationService::new(),
|
||||
renderer: PromptRenderer::new(),
|
||||
db,
|
||||
@@ -62,7 +65,20 @@ impl AnalysisService {
|
||||
)> {
|
||||
let analysis_id = Uuid::now_v7();
|
||||
let input_hash = self.compute_hash(&sanitized_data);
|
||||
let provider_name = self.provider.name().to_string();
|
||||
|
||||
// 从 config_resolver 获取 default_provider,然后从 registry 解析
|
||||
let default_provider_name = crate::config_resolver::load_ai_config(tenant_id, &self.db)
|
||||
.await
|
||||
.default_provider;
|
||||
let resolved = self
|
||||
.provider_registry
|
||||
.resolve(&default_provider_name)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, "无法解析 AI Provider");
|
||||
AiError::ProviderUnavailable(default_provider_name.clone())
|
||||
})?;
|
||||
let provider_name = resolved.provider_name().to_string();
|
||||
|
||||
// 0. 缓存命中检查(相同输入 + prompt 版本 → 复用已有结果)
|
||||
if let Some(cached) = self.find_cached(tenant_id, &input_hash, 1).await? {
|
||||
@@ -133,7 +149,7 @@ impl AnalysisService {
|
||||
temperature,
|
||||
max_tokens,
|
||||
};
|
||||
let stream = self.provider.stream_generate(req).await?;
|
||||
let stream = resolved.provider().stream_generate(req).await?;
|
||||
|
||||
Ok((stream, analysis_id, provider_name))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user