rust17-部门管理模块

rust17-部门管理模块

1、部门管理模块表的创建

部门管理这部分跟我们的菜单部分有些像,也应该是树级的显示,这里我们处理一下。不过我们数据上直接显示列表,然后折叠部分交给前端页面显示的时候渲染即可。

🍎创建部门表sys-dept

javascript 复制代码
DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept`  (
  `dept_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '部门id',
  `parent_id` bigint(0) NULL DEFAULT 0 COMMENT '父部门id',
  `ancestors` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '祖级列表',
  `dept_name` varchar(30) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '部门名称',
  `order_num` int(0) NULL DEFAULT 0 COMMENT '显示顺序',
  `leader` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT '负责人',
  `phone` varchar(11) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT '联系电话',
  `email` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT '邮箱',
  `status` char(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '0' COMMENT '部门状态(0正常 1停用)',
  `del_flag` char(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  `create_by` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '创建者',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '更新者',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`dept_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 110 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = '部门表' ROW_FORMAT = Dynamic;

2、接口开发

👉路由入口引入申明

接下来我们完成部门模块,在完成功能之前我们需要先添加模块入口,这样rust才能认识到是哪个模块

🍎src\main.rs

先在入口引入我们的模块

javascript 复制代码
  .service(
      web::scope("/api") // 这里加上 /api 前缀
        .configure(modules::dept::routes::config)
  )

🍎模块申明

全局文件之中部门模块申明

我们的文件结构如下所示,mod.rs作为入口文件进行申明文件暴露

javascript 复制代码
// src\modules\mod.rs
pub mod dept;

建立文件结构如下图所示:

javascript 复制代码
// src\modules\dept


📦dept
 ┣ 📜handlers.rs
 ┣ 📜mod.rs
 ┗ 📜routes.rs

基础模块申明

src\modules\dept下面搭建模块

mod.rs模块

javascript 复制代码
// mod.rs
pub mod handlers;
pub mod routes;

routes.rs模块

javascript 复制代码
//routes.rs
use actix_web::web;
pub fn config(cfg: &mut web::ServiceConfig) {
  cfg.route("/system/depts", web::get().to(crate::modules::role::handlers::get_list));
  cfg.route("/system/depts", web::post().to(crate::modules::role::handlers::post_add));
  cfg.route("/system/depts/{id}", web::get().to(crate::modules::role::handlers::get_detail));
  cfg.route("/system/depts", web::put().to(crate::modules::role::handlers::put_update));
  cfg.route("/system/depts/{id}", web::delete().to(crate::modules::role::handlers::del_delete));
}

handlers.rs方法逻辑

javascript 复制代码
// handlers.rs方法逻辑

use actix_web::{HttpResponse};
use crate::common::response::ApiResponse; // 导入 ApiResponse 模型

// 通用查询
pub async fn get_list() -> HttpResponse {
    HttpResponse::Ok().json(ApiResponse {
        code: 200,
        msg: "接口信息",
        data:  None::<()>,
    })
}

// 通用新增
pub async fn post_add() -> HttpResponse {
    HttpResponse::Ok().json(ApiResponse {
        code: 200,
        msg: "接口信息",
        data:  None::<()>,
    })
}
// 通用详情
pub async fn get_detail() -> HttpResponse {
    HttpResponse::Ok().json(ApiResponse {
        code: 200,
        msg: "接口信息",
        data:  None::<()>,
    })
}
// 通用更新
pub async fn put_update() -> HttpResponse {
    HttpResponse::Ok().json(ApiResponse {
        code: 200,
        msg: "接口信息",
        data:  None::<()>,
    })
}
// 通用更新
pub async fn del_delete() -> HttpResponse {
    HttpResponse::Ok().json(ApiResponse {
        code: 200,
        msg: "接口信息",
        data:  None::<()>,
    })
}

3、功能实现

🍎新增功能

接下来我们实现部门模块的新增部分

新增的数据类型我们先写一下

javascript 复制代码
// 部门结构体
#[derive(Debug, Deserialize)]
pub struct AddDept {
    #[serde(rename = "parentId")]
    pub parent_id:String,// 父部门ID

    #[serde(rename = "deptName")]
    pub dept_name: String,      // 部门名称

    #[serde(rename = "orderNum")]
    pub order_num: i32,         // 显示顺序

    #[serde(rename = "leader")]
    pub leader: String,         // 负责人

    #[serde(rename = "phone")]
    pub phone: String,          // 联系电话

    #[serde(rename = "email")]
    pub email: String,          // 邮箱

    #[serde(rename = "status")]
    pub status: String,         // 部门状态(0正常 1停用)
}

主文件引入使用

javascript 复制代码
// 当前模块部分
// 结构体
#[allow(unused_imports)]
use crate::common::response::AddDept; 

// 新增
pub async fn post_add(pool: web::Data<MySqlPool>, form: web::Json<AddDept>) -> HttpResponse {
    let mut data = std::collections::HashMap::new();
    // 插入基础字段
    data.insert("parent_id".to_string(), form.parent_id.to_string());
    data.insert("dept_name".to_string(), form.dept_name.clone());
    data.insert("order_num".to_string(), form.order_num.to_string());
    data.insert("leader".to_string(), form.leader.clone());
    data.insert("phone".to_string(), form.phone.clone());
    data.insert("email".to_string(), form.email.clone());
    data.insert("status".to_string(), form.status.clone());
    // 调用API创建方法
    crate::common::apimethods::create_api(
        pool.get_ref(),
        "sys_dept",
        &data,
        &[
            "dept_name".to_string(),
        ],
    )
    .await
}

测试接口

javascript 复制代码
{
    "parentId": "103",
    "deptName": "测试A",
    "orderNum": 2,
    "leader": "测试A",
    "phone": "18735797977",
    "email": "21545@qq.com",
    "status": "0"
}

返回信息

javascript 复制代码
{
    "code": 200,
    "msg": "新增成功"
}

测试功能ok

🍎查询功能

这里查询我们直接引我们之前的查询部分的参数和方法

javascript 复制代码
// 通过方法
#[allow(unused_imports)]
use crate::common::apimethods::list_api_page; // 引入公共分页查询方法

// 通用查询
pub async fn get_list(
    pool: web::Data<MySqlPool>,
    query: web::Query<QueryParams>,
    filter: Option<web::Query<HashMap<String, String>>>,
) -> impl Responder {
    // 调试:打印查询参数
    if let Some(ref filter_params) = filter {
        println!("查询参数: {:?}", filter_params);
    }
    // 精确查询字段(exact query)
    let exactquery = vec!["status".to_string()];
    // 模糊查询字段(like query) "role_name".to_string()
    let likequery = vec!["dept_name".to_string()];
    // 调用 list_api_page 传入查询条件
    list_api_page(pool, query, filter, "sys_dept", exactquery, likequery).await
}

测试功能ok

🍎详情功能

javascript 复制代码
// 通用详情
pub async fn get_detail(
    pool: web::Data<MySqlPool>,
    path: web::Path<i32>,
) -> HttpResponse {
    crate::common::apimethods::detail_api::<Dept>(pool, "sys_dept", "dept_id", path.into_inner()).await
}

测试功能ok

🍎更新功能

这里需要考虑的更新功能就是注意在数据表之中我们dept_id存储字段的类型为bigint

javascript 复制代码
bignit => i64
int => i32 
javascript 复制代码
// 通用更新
pub async fn put_update(
    pool: web::Data<MySqlPool>,
    form: web::Json<Dept>,
) -> HttpResponse {
    let mut data = HashMap::new();
    // 插入字段
   
   // 插入基础字段
    data.insert("parent_id".to_string(), form.parent_id.to_string());
    data.insert("dept_name".to_string(), form.dept_name.clone());
    data.insert("order_num".to_string(), form.order_num.to_string());
    data.insert("leader".to_string(), form.leader.clone());
    data.insert("phone".to_string(), form.phone.clone());
    data.insert("email".to_string(), form.email.clone());
    data.insert("status".to_string(), form.status.clone());

    crate::common::apimethods::update_api(
        pool.get_ref(),
        "sys_dept",
        "dept_id",
        form.dept_id as i32,
        &data,
    )
    .await
}

测试功能ok

🍎删除功能

javascript 复制代码
// 删除
pub async fn del_delete(pool: web::Data<MySqlPool>, id: web::Path<i32>) -> HttpResponse {
    crate::common::apimethods::delete_api(
        pool.get_ref(),
        "sys_dept",
        "dept_id",
        *id,
        false, // 软删除,isDeleted=1
    )
    .await
}

测试功能ok,到这里我们的功能就ok了,然后我们继续做导出和导出模块,并且进一步进行优化和完善

4、导入导出Excel

👉导出部门数据

接下来我们开发一个路由接口,用于导出Excel部门数据

添加导出数据的入口

javascript 复制代码
src\modules\dept\routes.rs

 cfg.route("/system/depts/export", web::post().to(crate::modules::dept::handlers::post_export));

导出流式输出

javascript 复制代码
// 流式导出csv数据
// CSV 转义
fn escape_csv(s: &str) -> String {
    let need_quote = s.contains(['"', ',', '\n', '\r']);
    if need_quote {
        let escaped = s.replace('"', "\"\"");
        format!("\"{}\"", escaped)
    } else {
        s.to_string()
    }
}
// 流式导出 CSV
pub async fn post_export(pool: web::Data<MySqlPool>) -> HttpResponse {
    use actix_web::web::Bytes;
    use futures_util::stream::{self, StreamExt};

    // BOM + 表头
    let head = {
        let mut v = vec![0xEF, 0xBB, 0xBF];
        v.extend_from_slice("部门ID,父部门ID,部门名称,显示顺序,负责人,联系电话,邮箱,状态\n".as_bytes());
        v
    };
    let head_stream = stream::once(async { Ok::<Bytes, actix_web::Error>(Bytes::from(head)) });

    // 分页流:每次查询一批,转换为 Bytes 后产出
    let pool = pool.get_ref().clone();
    let rows_stream = stream::unfold(Some((pool, 0i64)), |state| async move {
        let (pool, last_id) = state?;

        let rows = match sqlx::query_as::<_, Dept>(
            "SELECT dept_id, parent_id, dept_name, order_num, leader, phone, email, status
             FROM sys_dept
             WHERE del_flag = 0 AND dept_id > ?
             ORDER BY dept_id
             LIMIT 1000"
        )
        .bind(last_id)
        .fetch_all(&pool)
        .await
        {
            Ok(v) => v,
            Err(e) => {
                eprintln!("export_users query error: {e:?}");
                return Some((Err(actix_web::error::ErrorInternalServerError("查询数据失败")), None));
            }
        };

        if rows.is_empty() {
            return None;
        }

        let mut next_last = last_id;
        let mut buf = String::with_capacity(rows.len() * 128);
        for u in rows {
            next_last = u.dept_id.max(next_last);
            let line = format!(
                "{},{},{},{},{},{},{},{}\n",
                u.dept_id,
                u.parent_id,
                escape_csv(&u.dept_name),
                u.order_num,
                escape_csv(&u.leader),
                escape_csv(&u.phone),
                escape_csv(&u.email),
                u.status,
               
            );
            buf.push_str(&line);
        }

        Some((Ok(Bytes::from(buf)), Some((pool, next_last))))
    });

    HttpResponse::Ok()
        .content_type("application/x-www-form-urlencoded; charset=utf-8")
        .append_header(("Content-Disposition", "attachment; filename=\"users.csv\""))
        .append_header(("Access-Control-Expose-Headers", "Content-Disposition, Content-Type"))
        .streaming(head_stream.chain(rows_stream))
}

测试功能,ok

相关推荐
_处女座程序员的日常2 小时前
如何预览常见格式word、excel、ppt、图片等格式的文档
前端·javascript·word·excel·开源软件
明月与玄武2 小时前
前端文件上传终极指南:从原理到架构实践!
前端·前端文件上传终极指南
C++chaofan2 小时前
MyBatis - Plus学习笔记
java·spring boot·笔记·后端·mysql·架构·mybatis
间彧2 小时前
如何设计异常分级策略,对不同级别异常(如业务异常、系统异常)采取不同的告警方式?
后端
间彧2 小时前
Micrometer详解与应用实战
后端
CheungChunChiu3 小时前
从 Rust 到 Flutter:嵌入式图形与构建工具全景指南
开发语言·flutter·rust
间彧3 小时前
SpringBoot/SpringCloud,如何实现监控埋点
后端
一缕南风3 小时前
Spring Boot 响应拦截器(Jackson)实现时间戳自动添加
java·spring boot·后端·拦截器
布列瑟农的星空3 小时前
后台类项目如何挖掘前端技术亮点
前端·面试