use crate::auth::get_auth_user; use crate::graphql::types::{blog::*, PaginatedResult, PaginationInput}; use crate::services::{blog_service::BlogService, casbin_service::CasbinService}; use async_graphql::{Context, Error as GraphQLError, Object, Result}; use uuid::Uuid; #[derive(Default)] pub struct BlogQuery; #[Object] impl BlogQuery { async fn blogs( &self, ctx: &Context<'_>, filter: Option, sort: Option, pagination: Option, ) -> Result> { let blog_service = ctx.data::()?; // 检查用户权限 let mut updated_filter = filter.unwrap_or(BlogFilterInput { title: None, slug: None, category_id: None, status: None, is_featured: None, is_active: None, tag_ids: None, search: None, date_from: None, date_to: None, }); // 尝试获取用户信息和权限检查 match get_auth_user(ctx).await { Ok(user) => { // 用户已认证,检查是否有读取 blogs 的权限 let casbin_service = ctx.data::()?; let has_permission = casbin_service .can_read(&user.id.to_string(), "blogs") .await .unwrap_or(false); // 如果没有权限,则只返回非 draft 状态的博客 if !has_permission { // 如果过滤器中没有设置状态,或者状态包含 draft,则排除 draft 状态 if updated_filter.status.is_none() || updated_filter.status.as_ref() == Some(&"draft".to_string()) { updated_filter.status = Some("published".to_string()); } } } Err(_) => { // 用户未认证,只返回已发布的博客 if updated_filter.status.is_none() || updated_filter.status.as_ref() == Some(&"draft".to_string()) { updated_filter.status = Some("published".to_string()); } } } let result = blog_service .get_blogs(Some(updated_filter), sort, pagination) .await .map_err(|e| GraphQLError::new(e.to_string()))?; // 转换 model 类型到 GraphQL 类型 Ok(PaginatedResult::new( result.items.into_iter().map(|item| item.into()).collect(), result.total, result.page, result.per_page, )) } /// 根据ID获取博客文章 async fn blog(&self, ctx: &Context<'_>, id: Uuid) -> Result { let blog_service = ctx.data::()?; let blog = blog_service .get_blog_by_id(id) .await .map_err(|e| GraphQLError::new(e.to_string()))?; // 权限检查:如果是 draft 状态,需要验证用户权限 if blog.status == "draft" { match get_auth_user(ctx).await { Ok(user) => { let casbin_service = ctx.data::()?; let has_permission = casbin_service .can_read(&user.id.to_string(), "blogs") .await .unwrap_or(false); if !has_permission { return Err(GraphQLError::new("Insufficient permissions to access draft content")); } } Err(_) => { return Err(GraphQLError::new("Authentication required to access draft content")); } } } Ok(blog.into()) } /// 根据slug获取博客文章 async fn blog_by_slug(&self, ctx: &Context<'_>, slug: String) -> Result { let blog_service = ctx.data::()?; let blog = blog_service .get_blog_by_slug(&slug) .await .map_err(|e| GraphQLError::new(e.to_string()))?; // 权限检查:如果是 draft 状态,需要验证用户权限 if blog.status == "draft" { match get_auth_user(ctx).await { Ok(user) => { let casbin_service = ctx.data::()?; let has_permission = casbin_service .can_read(&user.id.to_string(), "blogs") .await .unwrap_or(false); if !has_permission { return Err(GraphQLError::new("Insufficient permissions to access draft content")); } } Err(_) => { return Err(GraphQLError::new("Authentication required to access draft content")); } } } Ok(blog.into()) } async fn blog_detail(&self, ctx: &Context<'_>, id: Uuid) -> Result { let blog_service = ctx.data::()?; let detail = blog_service .get_blog_detail(id) .await .map_err(|e| GraphQLError::new(e.to_string()))?; // 权限检查:如果是 draft 状态,需要验证用户权限 if detail.blog.status == "draft" { match get_auth_user(ctx).await { Ok(user) => { let casbin_service = ctx.data::()?; let has_permission = casbin_service .can_read(&user.id.to_string(), "blogs") .await .unwrap_or(false); if !has_permission { return Err(GraphQLError::new("Insufficient permissions to access draft content")); } } Err(_) => { return Err(GraphQLError::new("Authentication required to access draft content")); } } } // 手动转换 BlogDetail,因为它包含嵌套结构 Ok(BlogDetail { blog: detail.blog.into(), category: detail.category.map(|c| c.into()), tags: detail.tags.into_iter().map(|t| t.into()).collect(), }) } async fn blog_stats(&self, ctx: &Context<'_>) -> Result { let blog_service = ctx.data::()?; let stats = blog_service .get_blog_stats() .await .map_err(|e| GraphQLError::new(e.to_string()))?; Ok(stats.into()) } async fn blog_categories( &self, ctx: &Context<'_>, filter: Option, ) -> Result> { let blog_service = ctx.data::()?; let categories = blog_service .get_categories(filter) .await .map_err(|e| GraphQLError::new(e.to_string()))?; Ok(categories.into_iter().map(|c| c.into()).collect()) } async fn blog_category(&self, ctx: &Context<'_>, id: Uuid) -> Result { let blog_service = ctx.data::()?; let category = blog_service .get_category_by_id(id) .await .map_err(|e| GraphQLError::new(e.to_string()))?; Ok(category.into()) } async fn blog_tags( &self, ctx: &Context<'_>, filter: Option, ) -> Result> { let blog_service = ctx.data::()?; let tags = blog_service .get_tags(filter) .await .map_err(|e| GraphQLError::new(e.to_string()))?; Ok(tags.into_iter().map(|t| t.into()).collect()) } async fn blog_tag(&self, ctx: &Context<'_>, id: Uuid) -> Result { let blog_service = ctx.data::()?; let tag = blog_service .get_tag_by_id(id) .await .map_err(|e| GraphQLError::new(e.to_string()))?; Ok(tag.into()) } }