- list 方法新增 cache 参数,使用 resolve_entity_info_cached 替代直接查库 - 查询改用 build_filtered_query_sql_ex,自动路由到 Generated Column - handler 传递 entity_cache 到 list 方法
336 lines
9.9 KiB
Rust
336 lines
9.9 KiB
Rust
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<PaginatedResponse<PluginDataResp>>),
|
|
),
|
|
security(("bearer_auth" = [])),
|
|
tag = "插件数据"
|
|
)]
|
|
/// GET /api/v1/plugins/{plugin_id}/{entity} — 列表
|
|
pub async fn list_plugin_data<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity)): Path<(Uuid, String)>,
|
|
Query(params): Query<PluginDataListParams>,
|
|
) -> Result<Json<ApiResponse<PaginatedResponse<PluginDataResp>>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<serde_json::Value> = 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<PluginDataResp>),
|
|
),
|
|
security(("bearer_auth" = [])),
|
|
tag = "插件数据"
|
|
)]
|
|
/// POST /api/v1/plugins/{plugin_id}/{entity} — 创建
|
|
pub async fn create_plugin_data<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity)): Path<(Uuid, String)>,
|
|
Json(req): Json<CreatePluginDataReq>,
|
|
) -> Result<Json<ApiResponse<PluginDataResp>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<PluginDataResp>),
|
|
),
|
|
security(("bearer_auth" = [])),
|
|
tag = "插件数据"
|
|
)]
|
|
/// GET /api/v1/plugins/{plugin_id}/{entity}/{id} — 详情
|
|
pub async fn get_plugin_data<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity, id)): Path<(Uuid, String, Uuid)>,
|
|
) -> Result<Json<ApiResponse<PluginDataResp>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<PluginDataResp>),
|
|
),
|
|
security(("bearer_auth" = [])),
|
|
tag = "插件数据"
|
|
)]
|
|
/// PUT /api/v1/plugins/{plugin_id}/{entity}/{id} — 更新
|
|
pub async fn update_plugin_data<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity, id)): Path<(Uuid, String, Uuid)>,
|
|
Json(req): Json<UpdatePluginDataReq>,
|
|
) -> Result<Json<ApiResponse<PluginDataResp>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity, id)): Path<(Uuid, String, Uuid)>,
|
|
) -> Result<Json<ApiResponse<()>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<u64>),
|
|
),
|
|
security(("bearer_auth" = [])),
|
|
tag = "插件数据"
|
|
)]
|
|
/// GET /api/v1/plugins/{plugin_id}/{entity}/count — 统计计数
|
|
pub async fn count_plugin_data<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity)): Path<(Uuid, String)>,
|
|
Query(params): Query<CountQueryParams>,
|
|
) -> Result<Json<ApiResponse<u64>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<serde_json::Value> = 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<Vec<AggregateItem>>),
|
|
),
|
|
security(("bearer_auth" = [])),
|
|
tag = "插件数据"
|
|
)]
|
|
/// GET /api/v1/plugins/{plugin_id}/{entity}/aggregate — 聚合查询
|
|
pub async fn aggregate_plugin_data<S>(
|
|
State(state): State<PluginState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
Path((plugin_id, entity)): Path<(Uuid, String)>,
|
|
Query(params): Query<AggregateQueryParams>,
|
|
) -> Result<Json<ApiResponse<Vec<AggregateItem>>>, AppError>
|
|
where
|
|
PluginState: FromRef<S>,
|
|
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<serde_json::Value> = 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)))
|
|
}
|