rust-模块方法封装

举报
林太白 发表于 2025/12/26 15:45:31 2025/12/26
【摘要】 rust-模块方法封装

👉rust-模块方法封装

🍎角色菜单方法封装

之前我们写项目的时候很多接口都是随意调取,这里我们接下来就将我们项目的接口进行封装,达到我们需要的效果

通过角色ID获取对应的菜单ID并且筛选重复的,我们封装一下

先来看看我们旧的写法,每次使用都需要频繁的调用

 // 查询这些角色对应的菜单ID
    let menu_ids: Vec<i32> = if role_ids.is_empty() {
        Vec::new() // 如果没有角色,返回空数组
    } else {
        // 创建 IN 查询的占位符
        let placeholders: String = role_ids.iter().map(|_| "?").collect::<Vec<_>>().join(",");


        // 使用占位符构建查询
        let query = format!(
            "SELECT DISTINCT menu_id FROM sys_role_menu WHERE role_id IN ({})",
            placeholders
        );


        // 创建查询并绑定每个参数
        let mut query_builder = sqlx::query_as::<_, MenuId>(&query);
        for role_id in &role_ids {
            query_builder = query_builder.bind(role_id);
        }


        match query_builder.fetch_all(pool.get_ref()).await {
            Ok(menu_ids) => menu_ids
                .into_iter()
                .map(|menu_id| menu_id.menu_id)
                .collect(),
            Err(_) => {
                return HttpResponse::InternalServerError().json(ApiResponse {
                    code: 500,
                    msg: "获取用户菜单失败",
                    data: None::<()>,
                });
            }
        }
};

将这个方法进行封装以后我们可以放入hook方法逻辑之中

// 定义返回的结构体
/// 根据角色ID列表获取对应的菜单ID列表(已去重)
///
/// # Arguments
/// * `pool` - 数据库连接池
/// * `role_ids` - 角色ID列表
///
/// # Returns
/// * `Result<Vec<i32>, sqlx::Error>` - 返回去重后的菜单ID列表,如果查询失败则返回错误
///
#[allow(unused_imports)]
use sqlx::{ MySqlPool, Pool, Row};


#[allow(unused_imports)]
use crate::types::menu::MenuId;


use crate::types::user; // 添加 Row trait
pub async fn get_menu_ids_by_roles(
    pool: &MySqlPool,
    role_ids: &[i32],
) -> Result<Vec<i32>, sqlx::Error> {
    // 如果没有角色,直接返回空数组
    if role_ids.is_empty() {
        return Ok(Vec::new());
    }


    // 创建 IN 查询的占位符
    let placeholders: String = role_ids.iter().map(|_| "?").collect::<Vec<_>>().join(",");


    // 使用占位符构建查询,使用 DISTINCT 确保去重
    let query = format!(
        "SELECT DISTINCT menu_id FROM sys_role_menu WHERE role_id IN ({})",
        placeholders
    );


    // 创建查询并绑定每个参数
    let mut query_builder = sqlx::query_as::<_, MenuId>(&query);
    for role_id in role_ids {
        query_builder = query_builder.bind(role_id);
    }


    // 执行查询并返回结果
    let menu_ids = query_builder
        .fetch_all(pool)
        .await?
        .into_iter()
        .map(|menu_id| menu_id.menu_id)
        .collect();


    Ok(menu_ids)
}

在需要的地方使用这个逻辑

// 根据角色ID查询菜单ID并去重
let menu_ids: Vec<i32> = match get_menu_ids_by_roles(pool.get_ref(), &role_ids).await {
    Ok(ids) => ids,
    Err(e) => {
        eprintln!("获取菜单ID失败: {}", e);
        return HttpResponse::InternalServerError().json(ApiResponse {
            code: 500,
            msg: "获取用户菜单失败",
            data: None::<()>,
        });
    }
};

🍎根据菜单ID获取菜单信息

封装一个方法,根据菜单的ID可以得到所有的菜单完整的信息

// src/hooks/get_menu_ids_by_roles.rs
use sqlx::MySqlPool;
use crate::modules::auth::handlers::Menu;


/// 根据菜单ID列表获取菜单详细信息
///
/// # Arguments
/// * `pool` - 数据库连接池
/// * `menu_ids` - 菜单ID列表
/// * `exclude_menu_type` - 要排除的菜单类型,如果为 None 则不过滤
///
/// # Returns
/// * `Result<Vec<Menu>, sqlx::Error>` - 返回菜单列表,如果查询失败则返回错误
pub async fn get_menus_by_ids(
    pool: &MySqlPool,
    menu_ids: &[i32],
    exclude_menu_type: Option<&str>, // 新增参数,用于指定要排除的菜单类型
) -> Result<Vec<Menu>, sqlx::Error> {
    // 如果没有菜单ID,直接返回空数组
    if menu_ids.is_empty() {
        return Ok(Vec::new());
    }


    // 创建 IN 查询的占位符
    let placeholders: String = menu_ids
        .iter()
        .map(|_| "?")
        .collect::<Vec<_>>()
        .join(",");


    // 构建基础查询
    let mut query = format!(
        "SELECT * FROM sys_menu WHERE menu_id IN ({})",
        placeholders
    );


    // 如果指定了要排除的菜单类型,添加条件
    if let Some(menu_type) = exclude_menu_type {
        query.push_str(&format!(" AND `menu_type` != '{}'", menu_type));
    }


    // 添加排序
    query.push_str(" ORDER BY parent_id, order_num");


    // 创建查询并绑定每个参数
    let mut query_builder = sqlx::query_as::<_, Menu>(&query);
    for menu_id in menu_ids {
        query_builder = query_builder.bind(menu_id);
    }


    // 执行查询
    let menus = query_builder.fetch_all(pool).await?;
    println!("查询到的菜单: {:?}", menus);
   
    Ok(menus)
}

使用这个接口,排除我们不想要的类型



// 排除类型为 'F' 的菜单
let menus = get_menus_by_ids(pool.get_ref(), &menu_ids, Some("F")).await?;


// 不过滤任何菜单类型
 let all_menus = get_menus_by_ids(pool.get_ref(), &menu_ids, None).await?;


// 排除类型为 'M' 的菜单
let non_directory_menus = get_menus_by_ids(pool.get_ref(), &menu_ids, Some("M")).await?;

项目使用

 // 根据菜单ID查询菜单信息
  let menus = match get_menus_by_ids(pool.get_ref(), &menu_ids, Some("F")).await {
      Ok(menus) => menus,
      Err(e) => {
          eprintln!("获取菜单信息失败: {}", e);
          return HttpResponse::InternalServerError().json(ApiResponse {
              code: 500,
              msg: "获取菜单信息失败",
              data: None::<()>,
          });
      }
  };

🍎根据用户ID获取用户角色id

#[allow(unused_imports)]
use sqlx::{ MySqlPool, Pool, Row};


#[allow(unused_imports)]
use crate::types::menu::MenuId;


#[allow(unused_imports)]
use crate::types::role::Role;


// 添加这些导入
use actix_web::HttpResponse;


// 返回信息
#[allow(unused_imports)]
use crate::common::response::ApiResponse;


/// 根据用户ID获取用户角色id
pub async fn get_user_roles_by_ids(pool: &MySqlPool, user_id: i32) -> Result<Vec<Role>, HttpResponse> {
    match sqlx::query_as::<_, Role>(
        "SELECT * FROM sys_role r
         INNER JOIN sys_user_role ur ON r.role_id = ur.role_id
         WHERE ur.user_id = ?",
    )
    .bind(user_id)
    .fetch_all(pool)
    .await
    {
        Ok(data) => {
            println!("用户角色==== {:?}", data);
            Ok(data)
        }
        Err(_) => Err(HttpResponse::InternalServerError().json(ApiResponse {
            code: 500,
            msg: "获取用户角色失败",
            data: None::<()>,
        })),
    }
}


使用查询用户角色

let roles: Vec<Role> = match get_user_roles_by_ids(pool.get_ref(), user.user_id).await {
    Ok(data) => {
        println!("用户角色==== {:?}", data);
        data
    }
    Err(e) => return e,  // 直接返回错误响应
};

🍎根据角色信息获取角色信息

这里我们将根据角色信息获取角色信息的方法也给封装起来

// src/hooks/get_roles_by_ids.rs
use sqlx::MySqlPool;


/// 根据角色ID列表获取角色信息
///
/// # Arguments
/// * `pool` - 数据库连接池
/// * `role_ids` - 角色ID列表
///
/// # Returns
/// * `Result<Vec<(String, String)>, sqlx::Error>` - 返回角色键值对列表,如果查询失败则返回错误
pub async fn get_roles_by_ids(
    pool: &MySqlPool,
    role_ids: &[i32],
) -> Result<Vec<(String, String)>, sqlx::Error> {
    // 如果没有角色ID,直接返回空数组
    if role_ids.is_empty() {
        return Ok(Vec::new());
    }


    // 构建IN查询的占位符
    let placeholders = role_ids.iter().map(|_| "?").collect::<Vec<_>>().join(",");
   
    // 构建查询
    let query_str = format!("SELECT role_key, role_name FROM sys_role WHERE role_id IN ({}) AND status = 0", placeholders);
   
    // 构建查询
    let mut query = sqlx::query_as::<_, (String, String)>(&query_str);
   
    // 逐个绑定每个角色ID
    for role_id in role_ids {
        query = query.bind(role_id);
    }
   
    // 执行查询
    let roles = query.fetch_all(pool).await?;
    println!("查询到的角色: {:?}", roles);
   
    Ok(roles)
}


使用

// 收集所有角色ID
let role_ids: Vec<i32> = roles.iter().map(|role| role.role_id).collect();


// 使用 get_roles_by_ids 获取角色信息
let role_info = match get_roles_by_ids(pool.get_ref(), &role_ids).await {
    Ok(info) => info,
    Err(e) => {
        eprintln!("获取角色信息失败: {}", e);
        return HttpResponse::InternalServerError().json(ApiResponse {
            code: 500,
            msg: "获取角色信息失败",
            data: None::<()>,
        });
    }
};

🍎验证头部token信息

use actix_web::{HttpRequest, HttpResponse};
use jsonwebtoken::{decode, DecodingKey, Validation};
use crate::common::response::{Claims};


use crate::types::response::{ApiResponse};


use crate::config::JWT_SECRET;  // 引入 JWT_SECRET
/// 从请求中提取并验证 token
pub async fn validate_token(req: &HttpRequest) -> Result<Claims, HttpResponse> {
    // 从 header 获取 token
    let token = match req.headers().get("Authorization") {
        Some(t) => t.to_str().unwrap_or("").replace("Bearer ", ""),
        None => {
            return Err(HttpResponse::Unauthorized().json(ApiResponse {
                code: 401,
                msg: "未提供Token",
                data: None::<()>,
            }));
        }
    };


    // 校验 token
    match decode::<Claims>(
        &token,
        &DecodingKey::from_secret(JWT_SECRET),
        &Validation::default(),
    ) {
        Ok(data) => Ok(data.claims),
        Err(_) => Err(HttpResponse::Unauthorized().json(ApiResponse {
            code: 401,
            msg: "Token无效或已过期",
            data: None::<()>,
        })),
    }
}

使用方法

// 在 get_routers 函数中使用
 let token_data = match validate_token(&req).await {
      Ok(data) => data,
       Err(_) => {
          return HttpResponse::Unauthorized().json(ApiResponse {
              code: 401,
              msg: "Token无效或已过期",
              data: None::<()>,
          });
      }
  };
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。