use axum::Extension; use axum::extract::{FromRef, Path, Query, State}; use axum::response::Json; use uuid::Uuid; use erp_core::error::AppError; use erp_core::rbac::require_permission; use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext}; use crate::data_dto::{ AggregateItem, AggregateQueryParams, CountQueryParams, CreatePluginDataReq, PluginDataListParams, PluginDataResp, UpdatePluginDataReq, }; use crate::data_service::{PluginDataService, resolve_manifest_id}; use crate::state::PluginState; /// 计算插件数据操作所需的权限码 /// 格式:{manifest_id}.{entity}.{action},如 erp-crm.customer.list fn compute_permission_code(manifest_id: &str, entity_name: &str, action: &str) -> String { let action_suffix = match action { "list" | "get" => "list", _ => "manage", }; format!("{}.{}.{}", manifest_id, entity_name, action_suffix) } #[utoipa::path( get, path = "/api/v1/plugins/{plugin_id}/{entity}", params(PluginDataListParams), responses( (status = 200, description = "成功", body = ApiResponse>), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// GET /api/v1/plugins/{plugin_id}/{entity} — 列表 pub async fn list_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity)): Path<(Uuid, String)>, Query(params): Query, ) -> Result>>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "list"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.list")?; } let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); // 解析 filter JSON let filter: Option = params .filter .as_ref() .and_then(|f| serde_json::from_str(f).ok()); let (items, total) = PluginDataService::list( plugin_id, &entity, ctx.tenant_id, page, page_size, &state.db, filter, params.search, params.sort_by, params.sort_order, &state.entity_cache, ) .await?; Ok(Json(ApiResponse::ok(PaginatedResponse { data: items, total, page, page_size, total_pages: (total as f64 / page_size as f64).ceil() as u64, }))) } #[utoipa::path( post, path = "/api/v1/plugins/{plugin_id}/{entity}", request_body = CreatePluginDataReq, responses( (status = 200, description = "创建成功", body = ApiResponse), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// POST /api/v1/plugins/{plugin_id}/{entity} — 创建 pub async fn create_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity)): Path<(Uuid, String)>, Json(req): Json, ) -> Result>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "create"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.admin")?; } let result = PluginDataService::create( plugin_id, &entity, ctx.tenant_id, ctx.user_id, req.data, &state.db, &state.event_bus, ) .await?; Ok(Json(ApiResponse::ok(result))) } #[utoipa::path( get, path = "/api/v1/plugins/{plugin_id}/{entity}/{id}", responses( (status = 200, description = "成功", body = ApiResponse), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// GET /api/v1/plugins/{plugin_id}/{entity}/{id} — 详情 pub async fn get_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity, id)): Path<(Uuid, String, Uuid)>, ) -> Result>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "get"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.list")?; } let result = PluginDataService::get_by_id(plugin_id, &entity, id, ctx.tenant_id, &state.db).await?; Ok(Json(ApiResponse::ok(result))) } #[utoipa::path( put, path = "/api/v1/plugins/{plugin_id}/{entity}/{id}", request_body = UpdatePluginDataReq, responses( (status = 200, description = "更新成功", body = ApiResponse), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// PUT /api/v1/plugins/{plugin_id}/{entity}/{id} — 更新 pub async fn update_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity, id)): Path<(Uuid, String, Uuid)>, Json(req): Json, ) -> Result>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "update"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.admin")?; } let result = PluginDataService::update( plugin_id, &entity, id, ctx.tenant_id, ctx.user_id, req.data, req.version, &state.db, &state.event_bus, ) .await?; Ok(Json(ApiResponse::ok(result))) } #[utoipa::path( delete, path = "/api/v1/plugins/{plugin_id}/{entity}/{id}", responses( (status = 200, description = "删除成功"), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// DELETE /api/v1/plugins/{plugin_id}/{entity}/{id} — 删除 pub async fn delete_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity, id)): Path<(Uuid, String, Uuid)>, ) -> Result>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "delete"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.admin")?; } PluginDataService::delete( plugin_id, &entity, id, ctx.tenant_id, &state.db, &state.event_bus, ) .await?; Ok(Json(ApiResponse::ok(()))) } #[utoipa::path( get, path = "/api/v1/plugins/{plugin_id}/{entity}/count", params(CountQueryParams), responses( (status = 200, description = "成功", body = ApiResponse), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// GET /api/v1/plugins/{plugin_id}/{entity}/count — 统计计数 pub async fn count_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity)): Path<(Uuid, String)>, Query(params): Query, ) -> Result>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "list"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.list")?; } // 解析 filter JSON let filter: Option = params .filter .as_ref() .and_then(|f| serde_json::from_str(f).ok()); let total = PluginDataService::count( plugin_id, &entity, ctx.tenant_id, &state.db, filter, params.search, ) .await?; Ok(Json(ApiResponse::ok(total))) } #[utoipa::path( get, path = "/api/v1/plugins/{plugin_id}/{entity}/aggregate", params(AggregateQueryParams), responses( (status = 200, description = "成功", body = ApiResponse>), ), security(("bearer_auth" = [])), tag = "插件数据" )] /// GET /api/v1/plugins/{plugin_id}/{entity}/aggregate — 聚合查询 pub async fn aggregate_plugin_data( State(state): State, Extension(ctx): Extension, Path((plugin_id, entity)): Path<(Uuid, String)>, Query(params): Query, ) -> Result>>, AppError> where PluginState: FromRef, S: Clone + Send + Sync + 'static, { let manifest_id = resolve_manifest_id(plugin_id, ctx.tenant_id, &state.db).await?; let fine_perm = compute_permission_code(&manifest_id, &entity, "list"); if require_permission(&ctx, &fine_perm).is_err() { require_permission(&ctx, "plugin.list")?; } // 解析 filter JSON let filter: Option = params .filter .as_ref() .and_then(|f| serde_json::from_str(f).ok()); let rows = PluginDataService::aggregate( plugin_id, &entity, ctx.tenant_id, &state.db, ¶ms.group_by, filter, ) .await?; let items = rows .into_iter() .map(|(key, count)| AggregateItem { key, count }) .collect(); Ok(Json(ApiResponse::ok(items))) }