Files
erp/crates/erp-server/src/handlers/audit_log.rs
iven 3b41e73f82 fix: resolve E2E audit findings and add Phase C frontend pages
- Fix audit_log handler multi-tenant bug: use Extension<TenantContext>
  instead of hardcoded default_tenant_id
- Fix sendMessage route mismatch: frontend /messages/send → /messages
- Add POST /users/{id}/roles backend route for role assignment
- Add task.completed event payload: started_by + instance_id for
  notification delivery
- Add audit log viewer frontend page (AuditLogViewer.tsx)
- Add language management frontend page (LanguageManager.tsx)
- Add api/auditLogs.ts and api/languages.ts modules
2026-04-12 15:57:33 +08:00

86 lines
2.4 KiB
Rust

use axum::extract::{Extension, FromRef, Query, State};
use axum::response::Json;
use axum::routing::get;
use axum::Router;
use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder};
use serde::{Deserialize, Serialize};
use erp_core::entity::audit_log;
use erp_core::error::AppError;
use erp_core::types::TenantContext;
/// 审计日志查询参数。
#[derive(Debug, Deserialize)]
pub struct AuditLogQuery {
pub resource_type: Option<String>,
pub user_id: Option<uuid::Uuid>,
pub page: Option<u64>,
pub page_size: Option<u64>,
}
/// 审计日志分页响应。
#[derive(Debug, Serialize)]
pub struct AuditLogResponse {
pub items: Vec<audit_log::Model>,
pub total: u64,
pub page: u64,
pub page_size: u64,
}
/// GET /audit-logs
///
/// 分页查询审计日志,支持按 resource_type 和 user_id 过滤。
/// 租户隔离通过 JWT 中间件注入的 TenantContext 实现。
pub async fn list_audit_logs<S>(
State(db): State<sea_orm::DatabaseConnection>,
Extension(ctx): Extension<TenantContext>,
Query(params): Query<AuditLogQuery>,
) -> Result<Json<AuditLogResponse>, AppError>
where
sea_orm::DatabaseConnection: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
let page = params.page.unwrap_or(1).max(1);
let page_size = params.page_size.unwrap_or(20).min(100);
let tenant_id = ctx.tenant_id;
let mut q = audit_log::Entity::find()
.filter(audit_log::Column::TenantId.eq(tenant_id));
if let Some(rt) = &params.resource_type {
q = q.filter(audit_log::Column::ResourceType.eq(rt.clone()));
}
if let Some(uid) = &params.user_id {
q = q.filter(audit_log::Column::UserId.eq(*uid));
}
let paginator = q
.order_by_desc(audit_log::Column::CreatedAt)
.paginate(&db, page_size);
let total = paginator
.num_items()
.await
.map_err(|e| AppError::Internal(format!("查询审计日志失败: {e}")))?;
let items = paginator
.fetch_page(page - 1)
.await
.map_err(|e| AppError::Internal(format!("查询审计日志失败: {e}")))?;
Ok(Json(AuditLogResponse {
items,
total,
page,
page_size,
}))
}
pub fn audit_log_router<S>() -> Router<S>
where
sea_orm::DatabaseConnection: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
Router::new().route("/audit-logs", get(list_audit_logs))
}