feat(ai): AiState + AiModule (ErpModule impl + 权限 + 路由骨架)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-25 13:58:33 +08:00
parent 6d392ae2b5
commit fada33101c
4 changed files with 150 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
use axum::response::IntoResponse;
pub async fn stream_lab_report() -> impl IntoResponse {
"stub"
}
pub async fn stream_trends() -> impl IntoResponse {
"stub"
}
pub async fn stream_checkup_plan() -> impl IntoResponse {
"stub"
}
pub async fn stream_report_summary() -> impl IntoResponse {
"stub"
}
pub async fn list_analysis() -> impl IntoResponse {
"stub"
}
pub async fn get_analysis() -> impl IntoResponse {
"stub"
}

View File

@@ -1,9 +1,14 @@
pub mod dto;
pub mod entity;
pub mod error;
pub mod handler;
pub mod module;
pub mod prompt;
pub mod provider;
pub mod sanitization;
pub mod service;
pub mod state;
pub use error::{AiError, AiResult};
pub use module::AiModule;
pub use state::AiState;

108
crates/erp-ai/src/module.rs Normal file
View File

@@ -0,0 +1,108 @@
use async_trait::async_trait;
use axum::Router;
use erp_core::module::{ErpModule, ModuleType, PermissionDescriptor};
use std::any::Any;
pub struct AiModule;
#[async_trait]
impl ErpModule for AiModule {
fn name(&self) -> &str {
"ai"
}
fn module_type(&self) -> ModuleType {
ModuleType::Builtin
}
fn dependencies(&self) -> Vec<&str> {
vec!["health"]
}
fn permissions(&self) -> Vec<PermissionDescriptor> {
vec![
PermissionDescriptor {
code: "ai.analysis.list".into(),
name: "查看分析历史".into(),
description: "查看 AI 分析结果历史记录".into(),
module: "ai".into(),
},
PermissionDescriptor {
code: "ai.analysis.manage".into(),
name: "请求分析".into(),
description: "发起 AI 分析请求".into(),
module: "ai".into(),
},
PermissionDescriptor {
code: "ai.prompt.list".into(),
name: "查看 Prompt".into(),
description: "查看 AI Prompt 模板列表".into(),
module: "ai".into(),
},
PermissionDescriptor {
code: "ai.prompt.manage".into(),
name: "管理 Prompt".into(),
description: "创建/编辑/激活/回滚 Prompt 模板".into(),
module: "ai".into(),
},
PermissionDescriptor {
code: "ai.usage.list".into(),
name: "查看用量".into(),
description: "查看 AI 用量统计".into(),
module: "ai".into(),
},
PermissionDescriptor {
code: "ai.provider.manage".into(),
name: "管理提供商".into(),
description: "管理 AI 提供商配置".into(),
module: "ai".into(),
},
]
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl AiModule {
pub fn public_routes<S>() -> Router<S>
where
crate::state::AiState: axum::extract::FromRef<S>,
S: Clone + Send + Sync + 'static,
{
Router::new()
}
pub fn protected_routes<S>() -> Router<S>
where
crate::state::AiState: axum::extract::FromRef<S>,
S: Clone + Send + Sync + 'static,
{
Router::new()
.route(
"/ai/analyze/lab-report",
axum::routing::post(crate::handler::stream_lab_report),
)
.route(
"/ai/analyze/trends",
axum::routing::post(crate::handler::stream_trends),
)
.route(
"/ai/analyze/checkup-plan",
axum::routing::post(crate::handler::stream_checkup_plan),
)
.route(
"/ai/analyze/report-summary",
axum::routing::post(crate::handler::stream_report_summary),
)
.route(
"/ai/analysis/history",
axum::routing::get(crate::handler::list_analysis),
)
.route(
"/ai/analysis/{id}",
axum::routing::get(crate::handler::get_analysis),
)
}
}

View File

@@ -0,0 +1,17 @@
use std::sync::Arc;
use erp_core::events::EventBus;
use sea_orm::DatabaseConnection;
use crate::service::analysis::AnalysisService;
use crate::service::prompt::PromptService;
use crate::service::usage::UsageService;
#[derive(Clone)]
pub struct AiState {
pub db: DatabaseConnection,
pub event_bus: EventBus,
pub analysis: Arc<AnalysisService>,
pub prompt: Arc<PromptService>,
pub usage: Arc<UsageService>,
}