use axum::Extension; use axum::extract::{FromRef, Json, Path, Query, State}; use erp_core::error::AppError; use erp_core::rbac::{require_any_permission, require_permission}; use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext}; use crate::dto::article_dto::{ArticleListItem, ArticleListParams, ArticleResp, CreateArticleReq, ReviewArticleReq, UpdateArticleReq}; use crate::service::article_service; use crate::state::HealthState; pub async fn list_articles( State(state): State, Extension(ctx): Extension, Query(params): Query, ) -> Result>>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); // 非管理权限用户只能查看已发布文章,防止草稿泄露 let status = if require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok() { params.status } else { Some("published".to_string()) }; let result = article_service::list_articles( &state, ctx.tenant_id, page, page_size, params.category, status, params.category_id, params.tag_id, params.keyword, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn get_article( State(state): State, Extension(ctx): Extension, Path(id): Path, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.list")?; let is_admin = require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok(); let result = article_service::get_article(&state, ctx.tenant_id, id, is_admin).await?; Ok(Json(ApiResponse::ok(result))) } pub async fn create_article( State(state): State, Extension(ctx): Extension, mut req: Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.manage")?; req.sanitize(); let result = article_service::create_article( &state, ctx.tenant_id, Some(ctx.user_id), req.0, ).await?; Ok(Json(ApiResponse::ok(result))) } pub async fn update_article( State(state): State, Extension(ctx): Extension, Path(id): Path, mut req: Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.manage")?; req.sanitize(); let result = article_service::update_article( &state, ctx.tenant_id, id, Some(ctx.user_id), req.0, ).await?; Ok(Json(ApiResponse::ok(result))) } #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct DeleteArticleReq { pub version: i32, } pub async fn delete_article( State(state): State, Extension(ctx): Extension, Path(id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.manage")?; article_service::delete_article(&state, ctx.tenant_id, id, Some(ctx.user_id), req.version).await?; Ok(Json(ApiResponse::ok(()))) } // --------------------------------------------------------------------------- // 审核工作流 // --------------------------------------------------------------------------- #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct VersionReq { pub version: i32, } /// 提交审核 pub async fn submit_article( State(state): State, Extension(ctx): Extension, Path(id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.manage")?; let result = article_service::submit_article( &state, ctx.tenant_id, id, Some(ctx.user_id), req.version, ).await?; Ok(Json(ApiResponse::ok(result))) } /// 审核通过并发布 pub async fn approve_article( State(state): State, Extension(ctx): Extension, Path(id): Path, mut req: Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.review")?; req.sanitize(); let version = req.version.unwrap_or(0); let result = article_service::approve_article( &state, ctx.tenant_id, id, Some(ctx.user_id), req.0, version, ).await?; Ok(Json(ApiResponse::ok(result))) } /// 审核拒绝 pub async fn reject_article( State(state): State, Extension(ctx): Extension, Path(id): Path, mut req: Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.review")?; req.sanitize(); let version = req.version.unwrap_or(0); let result = article_service::reject_article( &state, ctx.tenant_id, id, Some(ctx.user_id), req.0, version, ).await?; Ok(Json(ApiResponse::ok(result))) } /// 撤回发布 pub async fn unpublish_article( State(state): State, Extension(ctx): Extension, Path(id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.manage")?; let result = article_service::unpublish_article( &state, ctx.tenant_id, id, Some(ctx.user_id), req.version, ).await?; Ok(Json(ApiResponse::ok(result))) } /// 浏览计数 pub async fn view_article( State(state): State, Extension(ctx): Extension, Path(id): Path, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { article_service::increment_view_count(&state, ctx.tenant_id, id).await?; Ok(Json(ApiResponse::ok(()))) } #[derive(Debug, serde::Deserialize)] pub struct ListRevisionsQuery { pub page: Option, pub page_size: Option, } /// 查询文章修订历史 pub async fn list_revisions( State(state): State, Extension(ctx): Extension, Path(id): Path, Query(params): Query, ) -> Result>>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); let result = article_service::list_revisions( &state, ctx.tenant_id, id, page, page_size, ).await?; Ok(Json(ApiResponse::ok(result))) }