248 lines
7.0 KiB
Rust
248 lines
7.0 KiB
Rust
use axum::{
|
|
Json, Router,
|
|
extract::{Path, Query, State},
|
|
routing::{get, post},
|
|
};
|
|
use common_telemetry::{AppError, AppResponse};
|
|
use utoipa::IntoParams;
|
|
use uuid::Uuid;
|
|
|
|
use crate::api::{AppState, handlers::common::extract_bearer_token};
|
|
use auth_kit::middleware::{tenant::TenantId, auth::AuthContext};
|
|
|
|
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
|
pub struct CreateColumnRequest {
|
|
pub name: String,
|
|
pub slug: String,
|
|
pub description: Option<String>,
|
|
pub parent_id: Option<Uuid>,
|
|
pub sort_order: Option<i32>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
|
|
pub struct UpdateColumnRequest {
|
|
pub name: Option<String>,
|
|
pub slug: Option<String>,
|
|
pub description: Option<Option<String>>,
|
|
pub parent_id: Option<Option<Uuid>>,
|
|
pub sort_order: Option<i32>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, IntoParams)]
|
|
pub struct ListColumnsQuery {
|
|
pub page: Option<u32>,
|
|
pub page_size: Option<u32>,
|
|
pub search: Option<String>,
|
|
pub parent_id: Option<Uuid>,
|
|
}
|
|
|
|
pub fn router() -> Router<AppState> {
|
|
Router::new()
|
|
.route("/", post(create_column_handler).get(list_columns_handler))
|
|
.route(
|
|
"/{id}",
|
|
get(get_column_handler)
|
|
.patch(update_column_handler)
|
|
.delete(delete_column_handler),
|
|
)
|
|
}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/v1/columns",
|
|
tag = "Column",
|
|
request_body = CreateColumnRequest,
|
|
security(
|
|
("bearer_auth" = [])
|
|
),
|
|
responses(
|
|
(status = 200, description = "创建栏目", body = crate::domain::models::Column),
|
|
(status = 401, description = "未认证"),
|
|
(status = 403, description = "无权限")
|
|
)
|
|
)]
|
|
pub async fn create_column_handler(
|
|
TenantId(tenant_id): TenantId,
|
|
AuthContext { user_id, .. }: AuthContext,
|
|
State(state): State<AppState>,
|
|
headers: axum::http::HeaderMap,
|
|
Json(body): Json<CreateColumnRequest>,
|
|
) -> Result<AppResponse<crate::domain::models::Column>, AppError> {
|
|
let token = extract_bearer_token(&headers)?;
|
|
state
|
|
.iam_client
|
|
.require_permission(tenant_id, user_id, "cms:column:write", &token)
|
|
.await?;
|
|
|
|
let column = state
|
|
.services
|
|
.create_column(
|
|
tenant_id,
|
|
body.name,
|
|
body.slug,
|
|
body.description,
|
|
body.parent_id,
|
|
body.sort_order.unwrap_or(0),
|
|
)
|
|
.await?;
|
|
Ok(AppResponse::ok(column))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/v1/columns",
|
|
tag = "Column",
|
|
params(ListColumnsQuery),
|
|
security(
|
|
("bearer_auth" = [])
|
|
),
|
|
responses(
|
|
(status = 200, description = "栏目列表", body = crate::infrastructure::repositories::column::Paged<crate::domain::models::Column>),
|
|
(status = 401, description = "未认证"),
|
|
(status = 403, description = "无权限")
|
|
)
|
|
)]
|
|
pub async fn list_columns_handler(
|
|
TenantId(tenant_id): TenantId,
|
|
AuthContext { user_id, .. }: AuthContext,
|
|
State(state): State<AppState>,
|
|
headers: axum::http::HeaderMap,
|
|
Query(query): Query<ListColumnsQuery>,
|
|
) -> Result<AppResponse<crate::infrastructure::repositories::column::Paged<crate::domain::models::Column>>, AppError>
|
|
{
|
|
let token = extract_bearer_token(&headers)?;
|
|
state
|
|
.iam_client
|
|
.require_permission(tenant_id, user_id, "cms:column:read", &token)
|
|
.await?;
|
|
|
|
let result = state
|
|
.services
|
|
.list_columns(
|
|
tenant_id,
|
|
crate::infrastructure::repositories::column::ListColumnsQuery {
|
|
page: query.page.unwrap_or(1),
|
|
page_size: query.page_size.unwrap_or(20),
|
|
search: query.search,
|
|
parent_id: query.parent_id,
|
|
},
|
|
)
|
|
.await?;
|
|
Ok(AppResponse::ok(result))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/v1/columns/{id}",
|
|
tag = "Column",
|
|
params(
|
|
("id" = String, Path, description = "栏目ID")
|
|
),
|
|
security(
|
|
("bearer_auth" = [])
|
|
),
|
|
responses(
|
|
(status = 200, description = "栏目详情", body = crate::domain::models::Column),
|
|
(status = 401, description = "未认证"),
|
|
(status = 403, description = "无权限"),
|
|
(status = 404, description = "不存在")
|
|
)
|
|
)]
|
|
pub async fn get_column_handler(
|
|
TenantId(tenant_id): TenantId,
|
|
AuthContext { user_id, .. }: AuthContext,
|
|
State(state): State<AppState>,
|
|
headers: axum::http::HeaderMap,
|
|
Path(id): Path<Uuid>,
|
|
) -> Result<AppResponse<crate::domain::models::Column>, AppError> {
|
|
let token = extract_bearer_token(&headers)?;
|
|
state
|
|
.iam_client
|
|
.require_permission(tenant_id, user_id, "cms:column:read", &token)
|
|
.await?;
|
|
|
|
let column = state.services.get_column(tenant_id, id).await?;
|
|
Ok(AppResponse::ok(column))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
patch,
|
|
path = "/v1/columns/{id}",
|
|
tag = "Column",
|
|
request_body = UpdateColumnRequest,
|
|
params(
|
|
("id" = String, Path, description = "栏目ID")
|
|
),
|
|
security(
|
|
("bearer_auth" = [])
|
|
),
|
|
responses(
|
|
(status = 200, description = "更新栏目", body = crate::domain::models::Column),
|
|
(status = 401, description = "未认证"),
|
|
(status = 403, description = "无权限"),
|
|
(status = 404, description = "不存在")
|
|
)
|
|
)]
|
|
pub async fn update_column_handler(
|
|
TenantId(tenant_id): TenantId,
|
|
AuthContext { user_id, .. }: AuthContext,
|
|
State(state): State<AppState>,
|
|
headers: axum::http::HeaderMap,
|
|
Path(id): Path<Uuid>,
|
|
Json(body): Json<UpdateColumnRequest>,
|
|
) -> Result<AppResponse<crate::domain::models::Column>, AppError> {
|
|
let token = extract_bearer_token(&headers)?;
|
|
state
|
|
.iam_client
|
|
.require_permission(tenant_id, user_id, "cms:column:write", &token)
|
|
.await?;
|
|
|
|
let column = state
|
|
.services
|
|
.update_column(
|
|
tenant_id,
|
|
id,
|
|
body.name,
|
|
body.slug,
|
|
body.description,
|
|
body.parent_id,
|
|
body.sort_order,
|
|
)
|
|
.await?;
|
|
Ok(AppResponse::ok(column))
|
|
}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/v1/columns/{id}",
|
|
tag = "Column",
|
|
params(
|
|
("id" = String, Path, description = "栏目ID")
|
|
),
|
|
security(
|
|
("bearer_auth" = [])
|
|
),
|
|
responses(
|
|
(status = 200, description = "删除成功"),
|
|
(status = 401, description = "未认证"),
|
|
(status = 403, description = "无权限"),
|
|
(status = 404, description = "不存在")
|
|
)
|
|
)]
|
|
pub async fn delete_column_handler(
|
|
TenantId(tenant_id): TenantId,
|
|
AuthContext { user_id, .. }: AuthContext,
|
|
State(state): State<AppState>,
|
|
headers: axum::http::HeaderMap,
|
|
Path(id): Path<Uuid>,
|
|
) -> Result<AppResponse<serde_json::Value>, AppError> {
|
|
let token = extract_bearer_token(&headers)?;
|
|
state
|
|
.iam_client
|
|
.require_permission(tenant_id, user_id, "cms:column:write", &token)
|
|
.await?;
|
|
|
|
state.services.delete_column(tenant_id, id).await?;
|
|
Ok(AppResponse::ok(serde_json::json!({"deleted": true})))
|
|
}
|