feat(ai): 建议查询/审批 API 端点 + 权限注册
- GET /ai/suggestions?analysis_id=xxx — 查看建议列表(ai.suggestion.list)
- POST /ai/suggestions/{id}/approve — 批准/拒绝建议(ai.suggestion.manage)
- 新增 ai.suggestion.list 和 ai.suggestion.manage 权限码
This commit is contained in:
@@ -12,6 +12,8 @@ use crate::dto::{AnalysisSseEvent, AnalysisType};
|
||||
use crate::service::suggestion::SuggestionService;
|
||||
use crate::state::AiState;
|
||||
|
||||
pub mod suggestion_handler;
|
||||
|
||||
// === 分析请求 Body ===
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
89
crates/erp-ai/src/handler/suggestion_handler.rs
Normal file
89
crates/erp-ai/src/handler/suggestion_handler.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use axum::extract::{Extension, FromRef, Path, Query, State};
|
||||
use axum::Json;
|
||||
use erp_core::rbac::require_permission;
|
||||
use erp_core::types::{ApiResponse, TenantContext};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::dto::suggestion::SuggestionStatus;
|
||||
use crate::service::suggestion::SuggestionService;
|
||||
use crate::state::AiState;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ListSuggestionsQuery {
|
||||
pub analysis_id: Option<uuid::Uuid>,
|
||||
pub status: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn list_suggestions<S>(
|
||||
State(state): State<AiState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Query(params): Query<ListSuggestionsQuery>,
|
||||
) -> Result<Json<ApiResponse<serde_json::Value>>, erp_core::error::AppError>
|
||||
where
|
||||
AiState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "ai.suggestion.list")?;
|
||||
|
||||
if let Some(analysis_id) = params.analysis_id {
|
||||
let items = SuggestionService::list_by_analysis(
|
||||
&state.db,
|
||||
ctx.tenant_id,
|
||||
analysis_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(Json(ApiResponse::ok(serde_json::json!({
|
||||
"data": items,
|
||||
"total": items.len(),
|
||||
}))))
|
||||
} else {
|
||||
let items =
|
||||
SuggestionService::list_pending(&state.db, ctx.tenant_id).await?;
|
||||
Ok(Json(ApiResponse::ok(serde_json::json!({
|
||||
"data": items,
|
||||
"total": items.len(),
|
||||
}))))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ApproveBody {
|
||||
pub action: String, // "approve" or "reject"
|
||||
}
|
||||
|
||||
pub async fn approve_suggestion<S>(
|
||||
State(state): State<AiState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(id): Path<uuid::Uuid>,
|
||||
Json(body): Json<ApproveBody>,
|
||||
) -> Result<Json<ApiResponse<serde_json::Value>>, erp_core::error::AppError>
|
||||
where
|
||||
AiState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "ai.suggestion.manage")?;
|
||||
|
||||
let new_status = match body.action.as_str() {
|
||||
"approve" => SuggestionStatus::Approved,
|
||||
"reject" => SuggestionStatus::Rejected,
|
||||
_ => {
|
||||
return Err(erp_core::error::AppError::Validation(
|
||||
"action 必须为 approve 或 reject".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
SuggestionService::update_status(
|
||||
&state.db,
|
||||
id,
|
||||
ctx.tenant_id,
|
||||
new_status,
|
||||
Some(ctx.user_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(serde_json::json!({
|
||||
"id": id,
|
||||
"status": new_status.as_str(),
|
||||
}))))
|
||||
}
|
||||
@@ -57,6 +57,18 @@ impl ErpModule for AiModule {
|
||||
description: "管理 AI 提供商配置".into(),
|
||||
module: "ai".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "ai.suggestion.list".into(),
|
||||
name: "查看 AI 建议".into(),
|
||||
description: "查看 AI 分析生成的建议列表".into(),
|
||||
module: "ai".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "ai.suggestion.manage".into(),
|
||||
name: "审批 AI 建议".into(),
|
||||
description: "批准或拒绝 AI 建议".into(),
|
||||
module: "ai".into(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -128,5 +140,13 @@ impl AiModule {
|
||||
"/ai/usage/by-type",
|
||||
axum::routing::get(crate::handler::usage_by_type),
|
||||
)
|
||||
.route(
|
||||
"/ai/suggestions",
|
||||
axum::routing::get(crate::handler::suggestion_handler::list_suggestions),
|
||||
)
|
||||
.route(
|
||||
"/ai/suggestions/{id}/approve",
|
||||
axum::routing::post(crate::handler::suggestion_handler::approve_suggestion),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user