├── docs ├── sql │ ├── other │ │ └── test.sql │ └── system │ │ ├── sys_role_dept.sql │ │ ├── sys_user_post.sql │ │ ├── sys_role_menu.sql │ │ ├── sys_user_role.sql │ │ ├── sys_dict_type.sql │ │ ├── sys_notice.sql │ │ ├── sys_post.sql │ │ ├── sys_oper_log.sql │ │ ├── sys_role.sql │ │ ├── sys_login_log.sql │ │ ├── sys_dict_data.sql │ │ ├── sys_user.sql │ │ └── sys_dept.sql ├── images │ ├── dept.jpg │ ├── dict.jpg │ ├── menu.jpg │ ├── post.jpg │ ├── role.jpg │ ├── user.jpg │ ├── notice.jpg │ ├── dict_data.jpg │ ├── login_log.jpg │ ├── role_menu.jpg │ ├── role_user.jpg │ └── user_role.jpg └── test │ ├── other │ └── test.http │ ├── http-client.env.json │ └── system │ ├── sys_login_log.http │ ├── sys_operate_log.http │ ├── sys_post.http │ ├── sys_dict_type.http │ ├── sys_notice.http │ ├── sys_dict_data.http │ ├── sys_menu.http │ ├── sys_dept.http │ ├── sys_user.http │ └── sys_role.http ├── src ├── model │ ├── other │ │ └── mod.rs │ ├── mod.rs │ └── system │ │ ├── mod.rs │ │ ├── sys_role_menu_model.rs │ │ ├── sys_role_dept_model.rs │ │ ├── sys_user_role_model.rs │ │ ├── sys_user_post_model.rs │ │ ├── sys_notice_model.rs │ │ ├── sys_dict_type_model.rs │ │ ├── sys_post_model.rs │ │ ├── sys_login_log_model.rs │ │ ├── sys_role_model.rs │ │ ├── sys_operate_log_model.rs │ │ ├── sys_dict_data_model.rs │ │ ├── sys_dept_model.rs │ │ ├── sys_menu_model.rs │ │ └── sys_user_model.rs ├── vo │ ├── other │ │ └── mod.rs │ ├── system │ │ ├── mod.rs │ │ ├── sys_login_log_vo.rs │ │ ├── sys_dict_type_vo.rs │ │ ├── sys_post_vo.rs │ │ ├── sys_notice_vo.rs │ │ ├── sys_operate_log_vo.rs │ │ ├── sys_dict_data_vo.rs │ │ ├── sys_dept_vo.rs │ │ ├── sys_menu_vo.rs │ │ ├── sys_role_vo.rs │ │ └── sys_user_vo.rs │ └── mod.rs ├── handler │ ├── other │ │ └── mod.rs │ ├── mod.rs │ └── system │ │ ├── mod.rs │ │ ├── sys_login_log_handler.rs │ │ ├── sys_operate_log_handler.rs │ │ ├── sys_notice_handler.rs │ │ ├── sys_dict_data_handler.rs │ │ ├── sys_post_handler.rs │ │ ├── sys_dict_type_handler.rs │ │ ├── sys_menu_handler.rs │ │ └── sys_dept_handler.rs ├── route │ ├── mod.rs │ └── system │ │ ├── mod.rs │ │ ├── sys_login_log_route.rs │ │ ├── sys_operate_log_route.rs │ │ ├── sys_dept_route.rs │ │ ├── sys_post_route.rs │ │ ├── sys_notice_route.rs │ │ ├── sys_dict_data_route.rs │ │ ├── sys_dict_type_route.rs │ │ ├── sys_menu_route.rs │ │ ├── sys_user_route.rs │ │ └── sys_role_route.rs ├── middleware │ ├── mod.rs │ └── auth.rs ├── common │ ├── mod.rs │ ├── result.rs │ └── error.rs ├── utils │ ├── mod.rs │ ├── redis_util.rs │ ├── time_util.rs │ ├── db.rs │ ├── user_agent_util.rs │ └── jwt_util.rs ├── config │ └── log4rs.yaml └── main.rs ├── .gitignore ├── .rustfmt.toml ├── Dockerfile ├── config.toml ├── .zed └── settings.json ├── deploy.sh ├── Cargo.toml ├── README.md └── LICENSE /docs/sql/other/test.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/model/other/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/vo/other/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/handler/other/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/route/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod system; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | log 4 | -------------------------------------------------------------------------------- /src/middleware/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod auth; 2 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # 控制宏调用的格式化方式 2 | max_width = 200 3 | 4 | -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod result; 3 | -------------------------------------------------------------------------------- /src/handler/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod other; 2 | pub mod system; 3 | -------------------------------------------------------------------------------- /src/model/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod other; 2 | 3 | pub mod system; 4 | -------------------------------------------------------------------------------- /docs/images/dept.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/dept.jpg -------------------------------------------------------------------------------- /docs/images/dict.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/dict.jpg -------------------------------------------------------------------------------- /docs/images/menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/menu.jpg -------------------------------------------------------------------------------- /docs/images/post.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/post.jpg -------------------------------------------------------------------------------- /docs/images/role.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/role.jpg -------------------------------------------------------------------------------- /docs/images/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/user.jpg -------------------------------------------------------------------------------- /docs/images/notice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/notice.jpg -------------------------------------------------------------------------------- /docs/images/dict_data.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/dict_data.jpg -------------------------------------------------------------------------------- /docs/images/login_log.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/login_log.jpg -------------------------------------------------------------------------------- /docs/images/role_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/role_menu.jpg -------------------------------------------------------------------------------- /docs/images/role_user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/role_user.jpg -------------------------------------------------------------------------------- /docs/images/user_role.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/axum-admin/HEAD/docs/images/user_role.jpg -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod db; 2 | pub mod jwt_util; 3 | pub mod redis_util; 4 | pub mod time_util; 5 | pub mod user_agent_util; 6 | -------------------------------------------------------------------------------- /docs/test/other/test.http: -------------------------------------------------------------------------------- 1 | ### GET request to example server 2 | GET https://examples.http-client.intellij.net/get 3 | ?generated-in=RustRover 4 | 5 | ### -------------------------------------------------------------------------------- /docs/test/http-client.env.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "host": "http://localhost:3000" 4 | }, 5 | "pro": { 6 | "host": "http://175.178.110.17:8088" 7 | } 8 | } -------------------------------------------------------------------------------- /src/utils/redis_util.rs: -------------------------------------------------------------------------------- 1 | use redis::Client; 2 | 3 | pub async fn init_redis(url: &str) -> Client { 4 | redis::Client::open(url).expect("Invalid redis URL") 5 | } 6 | -------------------------------------------------------------------------------- /docs/sql/system/sys_role_dept.sql: -------------------------------------------------------------------------------- 1 | create table sys_role_dept 2 | ( 3 | role_id bigint not null comment '角色id', 4 | dept_id bigint not null comment '部门id', 5 | primary key (role_id, dept_id) 6 | ) comment = '角色和部门关联表'; 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster-slim 2 | 3 | ENV TZ Asia/Shanghai 4 | 5 | WORKDIR /app 6 | 7 | COPY ./src/config/log4rs.yaml /app/src/config/log4rs.yaml 8 | COPY ./config.toml /app/config.toml 9 | COPY ./target/release/axum-admin /app/ 10 | 11 | CMD ["./axum-admin"] -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | # 配置服务器的基本信息 2 | [server] 3 | # 定义服务器监听的端口和主机地址 4 | addr = "0.0.0.0:3000" 5 | 6 | # 配置数据库的连接信息 7 | [db] 8 | # 定义连接数据库的URL,包含用户名、密码、主机地址、端口和数据库名 9 | url = "mysql://root:123456@127.0.0.1:3306/rustdb" 10 | [redis] 11 | url = "redis://:123456@127.0.0.1:6379" -------------------------------------------------------------------------------- /src/vo/system/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sys_dept_vo; 2 | pub mod sys_dict_data_vo; 3 | pub mod sys_dict_type_vo; 4 | pub mod sys_login_log_vo; 5 | pub mod sys_menu_vo; 6 | pub mod sys_notice_vo; 7 | pub mod sys_operate_log_vo; 8 | pub mod sys_post_vo; 9 | pub mod sys_role_vo; 10 | pub mod sys_user_vo; 11 | -------------------------------------------------------------------------------- /src/route/system/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sys_dept_route; 2 | pub mod sys_dict_data_route; 3 | pub mod sys_dict_type_route; 4 | pub mod sys_login_log_route; 5 | pub mod sys_menu_route; 6 | pub mod sys_notice_route; 7 | pub mod sys_operate_log_route; 8 | pub mod sys_post_route; 9 | pub mod sys_role_route; 10 | pub mod sys_user_route; 11 | -------------------------------------------------------------------------------- /src/utils/time_util.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbdc::DateTime; 2 | 3 | /* 4 | *时间转字符串 5 | *author:刘飞华 6 | *date:2025/01/02 14:38:11 7 | */ 8 | pub fn time_to_string(t: Option) -> String { 9 | match t { 10 | None => "".to_string(), 11 | Some(x) => x.format("YYYY-MM-DD hh:mm:ss"), 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/sql/system/sys_user_post.sql: -------------------------------------------------------------------------------- 1 | create table sys_user_post 2 | ( 3 | user_id bigint not null comment '用户id', 4 | post_id bigint not null comment '岗位id', 5 | primary key (user_id, post_id) 6 | ) comment = '用户与岗位关联表'; 7 | 8 | 9 | insert into sys_user_post values ('1', '1'); 10 | insert into sys_user_post values ('2', '2'); 11 | -------------------------------------------------------------------------------- /src/handler/system/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sys_dept_handler; 2 | pub mod sys_dict_data_handler; 3 | pub mod sys_dict_type_handler; 4 | pub mod sys_login_log_handler; 5 | pub mod sys_menu_handler; 6 | pub mod sys_notice_handler; 7 | pub mod sys_operate_log_handler; 8 | pub mod sys_post_handler; 9 | pub mod sys_role_handler; 10 | pub mod sys_user_handler; 11 | -------------------------------------------------------------------------------- /docs/sql/system/sys_role_menu.sql: -------------------------------------------------------------------------------- 1 | create table sys_role_menu 2 | ( 3 | id bigint auto_increment comment '主键' 4 | primary key, 5 | role_id bigint not null comment '角色ID', 6 | menu_id bigint not null comment '菜单ID', 7 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间' 8 | ) 9 | comment '菜单角色关联表'; 10 | -------------------------------------------------------------------------------- /src/model/system/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sys_dept_model; 2 | pub mod sys_dict_data_model; 3 | pub mod sys_dict_type_model; 4 | pub mod sys_login_log_model; 5 | pub mod sys_menu_model; 6 | pub mod sys_notice_model; 7 | pub mod sys_operate_log_model; 8 | pub mod sys_post_model; 9 | pub mod sys_role_dept_model; 10 | pub mod sys_role_menu_model; 11 | pub mod sys_role_model; 12 | pub mod sys_user_model; 13 | pub mod sys_user_post_model; 14 | pub mod sys_user_role_model; 15 | -------------------------------------------------------------------------------- /docs/sql/system/sys_user_role.sql: -------------------------------------------------------------------------------- 1 | create table sys_user_role 2 | ( 3 | id bigint auto_increment comment '主键' 4 | primary key, 5 | user_id bigint default 0 not null comment '用户ID', 6 | role_id bigint not null comment '角色ID', 7 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间' 8 | ) 9 | comment '角色用户关联表'; 10 | INSERT INTO sys_user_role (user_id, role_id) VALUES (1, 1) -------------------------------------------------------------------------------- /.zed/settings.json: -------------------------------------------------------------------------------- 1 | // Folder-specific settings 2 | // 3 | // For a full list of overridable settings, and general information on folder-specific settings, 4 | // see the documentation: https://zed.dev/docs/configuring-zed#settings-files 5 | { 6 | "autosave": { 7 | "after_delay": { 8 | "milliseconds": 1000 9 | } 10 | }, 11 | "inlay_hints": { 12 | "enabled": true, 13 | "show_type_hints": true, 14 | "show_parameter_hints": true, 15 | "show_other_hints": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/db.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbatis::RBatis; 2 | use rbatis::rbdc::pool::{ConnectionManager, Pool}; 3 | use rbdc_mysql::MysqlDriver; 4 | use rbdc_pool_fast::FastPool; 5 | 6 | pub async fn init_db(url: &str) -> RBatis { 7 | let rb = RBatis::new(); 8 | 9 | let manager = ConnectionManager::new(MysqlDriver {}, url).expect("create connection manager error"); 10 | let pool = FastPool::new(manager).expect("create db pool error"); 11 | 12 | rb.init_pool(pool).expect("init db pool error"); 13 | rb 14 | } 15 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script 4 | # 编译构建项目,-r 表示进行发布构建 5 | cargo build -r 6 | 7 | # 停止名为 axum-admin 的容器 8 | docker stop axum-admin 9 | 10 | # 强制移除名为 axum-admin 的容器 11 | docker rm -f axum-admin 12 | 13 | # 强制移除标签为 axum-admin:v1 的镜像 14 | docker rmi -f axum-admin:v1 15 | 16 | # 移除所有未被标记的镜像 17 | docker rmi -f $(docker images | grep "none" | awk '{print $3}') 18 | 19 | # 使用 Dockerfile 构建新的镜像并打上标签 axum-admin:v1 20 | docker build -t axum-admin:v1 -f Dockerfile . 21 | 22 | # 以交互模式和守护进程方式运行容器,使用主机网络并命名为 axum-admin 23 | docker run -itd --net=host --name=axum-admin axum-admin:v1 24 | 25 | -------------------------------------------------------------------------------- /src/config/log4rs.yaml: -------------------------------------------------------------------------------- 1 | refresh_rate: 30 seconds 2 | appenders: 3 | stdout: 4 | kind: console 5 | encoder: 6 | pattern: "{d(%Y-%m-%d %H:%M:%S)} [{f}:{L}] {h({l})} [{M}] - {m}{n}" 7 | file: 8 | kind: rolling_file 9 | path: "log/axum-admin.log" 10 | encoder: 11 | pattern: "{d(%Y-%m-%d %H:%M:%S)} [{f}:{L}] {h({l})} [{M}] - {m}{n}" 12 | # kind: json 13 | policy: 14 | #trigger: 15 | # kind: size 16 | # limit: 10 mb #测试日志轮转,可以改成1 kb 17 | trigger: 18 | kind: time 19 | interval: 1 day 20 | roller: 21 | kind: fixed_window 22 | pattern: "log/old-axum-admin-{}.log" 23 | base: 1 24 | count: 30 25 | root: 26 | level: debug 27 | appenders: 28 | - stdout 29 | - file -------------------------------------------------------------------------------- /docs/test/system/sys_login_log.http: -------------------------------------------------------------------------------- 1 | 2 | 3 | ###删除系统访问记录 deleteLoginLog 4 | POST {{host}}/api/system/loginLog/deleteLoginLog 5 | Content-Type: application/json 6 | Authorization: Bearer {{token}} 7 | 8 | { 9 | "ids": [13] 10 | } 11 | 12 | ###清空系统登录日志 cleanLoginLog 13 | GET {{host}}/api/system/loginLog/cleanLoginLog 14 | Authorization: Bearer {{token}} 15 | 16 | ###查询系统访问记录详情 queryLoginLogDetail 17 | POST {{host}}/api/system/loginLog/queryLoginLogDetail 18 | Content-Type: application/json 19 | Authorization: Bearer {{token}} 20 | 21 | { 22 | "id": 1 23 | } 24 | 25 | 26 | ###查询系统访问记录列表 queryLoginLogList 27 | POST {{host}}/api/system/loginLog/queryLoginLogList 28 | Content-Type: application/json 29 | Authorization: Bearer {{token}} 30 | 31 | { 32 | "pageNo": 1, 33 | "pageSize": 10 34 | } 35 | 36 | -------------------------------------------------------------------------------- /docs/test/system/sys_operate_log.http: -------------------------------------------------------------------------------- 1 | 2 | ###删除操作日志记录 deleteOperateLog 3 | POST {{host}}/api/system/operateLog/deleteOperateLog 4 | Content-Type: application/json 5 | Authorization: Bearer {{token}} 6 | 7 | { 8 | "ids": [13] 9 | } 10 | ###清空系统登录日志 cleanLoginLog 11 | GET {{host}}/api/system/operateLog/cleanOperateLog 12 | Authorization: Bearer {{token}} 13 | 14 | ###查询操作日志记录详情 queryOperateLogDetail 15 | POST {{host}}/api/system/operateLog/queryOperateLogDetail 16 | Content-Type: application/json 17 | Authorization: Bearer {{token}} 18 | 19 | { 20 | "id": 1 21 | } 22 | 23 | 24 | ###查询操作日志记录列表 queryOperateLogList 25 | POST {{host}}/api/system/operateLog/queryOperateLogList 26 | Content-Type: application/json 27 | Authorization: Bearer {{token}} 28 | 29 | { 30 | "pageNo": 1, 31 | "pageSize": 10 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "axum-admin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum ={ version = "0.8.4" } 10 | tokio ={ version = "1",features = ["full"]} 11 | axum-valid = "0.24.0" 12 | validator = { version = "0.20.0", features = ["derive"] } 13 | garde = "0.22.0" 14 | serde = { version = "1.0", features = ["derive"] } 15 | 16 | rbs = { version = "4.6"} 17 | rbatis = { version = "4.6"} 18 | #rbatis = { version = "4.0",features = ["debug_mode"]} 19 | rbdc-mysql={version="4.6"} 20 | rbdc-pool-fast={version="4.6"} 21 | 22 | log = "0.4" 23 | log4rs = "1.0" 24 | 25 | jsonwebtoken = "9.3.0" 26 | 27 | redis = "0.32.3" 28 | 29 | thiserror = "2.0.3" 30 | regex = "1.11.1" 31 | config = "0.15.9" 32 | chrono = "0.4.38" 33 | -------------------------------------------------------------------------------- /src/route/system/sys_login_log_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_login_log_handler; 2 | use crate::AppState; 3 | use axum::routing::{get, post}; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建系统访问记录路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_login_log_route() -> Router> { 12 | Router::new() 13 | .route("/system/loginLog/deleteLoginLog", post(sys_login_log_handler::delete_sys_login_log)) 14 | .route("/system/loginLog/cleanLoginLog", get(sys_login_log_handler::clean_sys_login_log)) 15 | .route("/system/loginLog/queryLoginLogDetail", post(sys_login_log_handler::query_sys_login_log_detail)) 16 | .route("/system/loginLog/queryLoginLogList", post(sys_login_log_handler::query_sys_login_log_list)) 17 | //记得在main.rs中添加路由build_sys_login_log_route() 18 | } 19 | -------------------------------------------------------------------------------- /src/route/system/sys_operate_log_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_operate_log_handler; 2 | use crate::AppState; 3 | use axum::routing::{get, post}; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建操作日志记录路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_operate_log_route() -> Router> { 12 | Router::new() 13 | .route("/system/operateLog/deleteOperateLog", post(sys_operate_log_handler::delete_sys_operate_log)) 14 | .route("/system/operateLog/cleanOperateLog", get(sys_operate_log_handler::clean_sys_operate_log)) 15 | .route("/system/operateLog/queryOperateLogDetail", post(sys_operate_log_handler::query_sys_operate_log_detail)) 16 | .route("/system/operateLog/queryOperateLogList", post(sys_operate_log_handler::query_sys_operate_log_list)) 17 | //记得在main.rs中添加路由build_sys_operate_log_route() 18 | } 19 | -------------------------------------------------------------------------------- /src/route/system/sys_dept_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_dept_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建部门表路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_dept_route() -> Router> { 12 | Router::new() 13 | .route("/system/dept/addDept", post(sys_dept_handler::add_sys_dept)) 14 | .route("/system/dept/deleteDept", post(sys_dept_handler::delete_sys_dept)) 15 | .route("/system/dept/updateDept", post(sys_dept_handler::update_sys_dept)) 16 | .route("/system/dept/updateDeptStatus", post(sys_dept_handler::update_sys_dept_status)) 17 | .route("/system/dept/queryDeptDetail", post(sys_dept_handler::query_sys_dept_detail)) 18 | .route("/system/dept/queryDeptList", post(sys_dept_handler::query_sys_dept_list)) 19 | //记得在main.rs中添加路由build_sys_dept_route() 20 | } 21 | -------------------------------------------------------------------------------- /src/route/system/sys_post_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_post_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建岗位信息表路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_post_route() -> Router> { 12 | Router::new() 13 | .route("/system/post/addPost", post(sys_post_handler::add_sys_post)) 14 | .route("/system/post/deletePost", post(sys_post_handler::delete_sys_post)) 15 | .route("/system/post/updatePost", post(sys_post_handler::update_sys_post)) 16 | .route("/system/post/updatePostStatus", post(sys_post_handler::update_sys_post_status)) 17 | .route("/system/post/queryPostDetail", post(sys_post_handler::query_sys_post_detail)) 18 | .route("/system/post/queryPostList", post(sys_post_handler::query_sys_post_list)) 19 | //记得在main.rs中添加路由build_sys_post_route() 20 | } 21 | -------------------------------------------------------------------------------- /docs/sql/system/sys_dict_type.sql: -------------------------------------------------------------------------------- 1 | create table sys_dict_type 2 | ( 3 | id bigint not null auto_increment comment '字典主键', 4 | dict_name varchar(100) default '' not null comment '字典名称', 5 | dict_type varchar(100) default '' not null comment '字典类型', 6 | status tinyint default 0 not null comment '状态(0:停用,1:正常)', 7 | remark varchar(500) default '' not null comment '备注', 8 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 9 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间', 10 | primary key (id), 11 | unique (dict_type) 12 | ) comment = '字典类型表'; 13 | 14 | INSERT INTO sys_dict_type (dict_name, dict_type, status, remark) VALUES ('用户性别', 'sys_user_sex', 1, '用户性别列表'); 15 | INSERT INTO sys_dict_type (dict_name, dict_type, status, remark) VALUES ('通知类型', 'sys_notice_type', 1, '通知类型列表'); 16 | -------------------------------------------------------------------------------- /src/route/system/sys_notice_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_notice_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建通知公告表路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_notice_route() -> Router> { 12 | Router::new() 13 | .route("/system/notice/addNotice", post(sys_notice_handler::add_sys_notice)) 14 | .route("/system/notice/deleteNotice", post(sys_notice_handler::delete_sys_notice)) 15 | .route("/system/notice/updateNotice", post(sys_notice_handler::update_sys_notice)) 16 | .route("/system/notice/updateNoticeStatus", post(sys_notice_handler::update_sys_notice_status)) 17 | .route("/system/notice/queryNoticeDetail", post(sys_notice_handler::query_sys_notice_detail)) 18 | .route("/system/notice/queryNoticeList", post(sys_notice_handler::query_sys_notice_list)) 19 | //记得在main.rs中添加路由build_sys_notice_route() 20 | } 21 | -------------------------------------------------------------------------------- /docs/sql/system/sys_notice.sql: -------------------------------------------------------------------------------- 1 | create table sys_notice 2 | ( 3 | id bigint auto_increment comment '公告ID' 4 | primary key, 5 | notice_title varchar(50) not null comment '公告标题', 6 | notice_type tinyint default 1 not null comment '公告类型(1:通知,2:公告)', 7 | notice_content varchar(255) default '' not null comment '公告内容', 8 | status tinyint default 0 not null comment '公告状态(0:关闭,1:正常 )', 9 | remark varchar(255) default '' not null comment '备注', 10 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 11 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间' 12 | ) comment '通知公告表'; 13 | 14 | 15 | INSERT INTO sys_notice (notice_title, notice_type, notice_content, status) VALUES ('测试通知1', 1, '这是一条测试通知内容', 1); 16 | INSERT INTO sys_notice (notice_title, notice_type, notice_content, status) VALUES ('测试公告2', 2, '这是一条测试公告内容', 1); 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/route/system/sys_dict_data_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_dict_data_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建字典数据表路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_dict_data_route() -> Router> { 12 | Router::new() 13 | .route("/system/dictData/addDictData", post(sys_dict_data_handler::add_sys_dict_data)) 14 | .route("/system/dictData/deleteDictData", post(sys_dict_data_handler::delete_sys_dict_data)) 15 | .route("/system/dictData/updateDictData", post(sys_dict_data_handler::update_sys_dict_data)) 16 | .route("/system/dictData/updateDictDataStatus", post(sys_dict_data_handler::update_sys_dict_data_status)) 17 | .route("/system/dictData/queryDictDataDetail", post(sys_dict_data_handler::query_sys_dict_data_detail)) 18 | .route("/system/dictData/queryDictDataList", post(sys_dict_data_handler::query_sys_dict_data_list)) 19 | //记得在main.rs中添加路由build_sys_dict_data_route() 20 | } 21 | -------------------------------------------------------------------------------- /src/route/system/sys_dict_type_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_dict_type_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | /* 7 | *构建字典类型表路由 8 | *author:刘飞华 9 | *date:2024/12/25 14:07:37 10 | */ 11 | pub fn build_sys_dict_type_route() -> Router> { 12 | Router::new() 13 | .route("/system/dictType/addDictType", post(sys_dict_type_handler::add_sys_dict_type)) 14 | .route("/system/dictType/deleteDictType", post(sys_dict_type_handler::delete_sys_dict_type)) 15 | .route("/system/dictType/updateDictType", post(sys_dict_type_handler::update_sys_dict_type)) 16 | .route("/system/dictType/updateDictTypeStatus", post(sys_dict_type_handler::update_sys_dict_type_status)) 17 | .route("/system/dictType/queryDictTypeDetail", post(sys_dict_type_handler::query_sys_dict_type_detail)) 18 | .route("/system/dictType/queryDictTypeList", post(sys_dict_type_handler::query_sys_dict_type_list)) 19 | //记得在main.rs中添加路由build_sys_dict_type_route() 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # axum-admin 3 | 4 | 是基于axum和rbatis的rbac权限管理系统 5 | 6 | # 预览地址 7 | 8 | 9 | 10 | # 前端项目 11 | 12 | ``` 13 | https://github.com/feihua/antd-admin.git 14 | ``` 15 | 16 | 17 | # 本地启动 18 | 19 | ``` 20 | 1.创建数据库并导入sql脚本 21 | 2.修改comfig.toml中的数据库连接地址 22 | 3.启动 cargo run .\src\main.rs 23 | ``` 24 | 25 | # 系统截图 26 | 27 | ## 用户界面 28 | 29 | ![user](docs/images/user.jpg) 30 | 31 | ## 角色分配界面 32 | 33 | ![user-role](docs/images/user_role.jpg) 34 | 35 | ## 角色界面 36 | 37 | ![role](docs/images/role.jpg) 38 | 39 | ## 角色用户界面 40 | 41 | ![role](docs/images/role_user.jpg) 42 | 43 | ## 菜单分配界面 44 | 45 | ![role-menu](docs/images/role_menu.jpg) 46 | 47 | ## 菜单界面 48 | 49 | ![menu](docs/images/menu.jpg) 50 | 51 | ## 部门界面 52 | 53 | ![menu](docs/images/dept.jpg) 54 | 55 | ## 岗位界面 56 | 57 | ![menu](docs/images/post.jpg) 58 | 59 | ## 字典界面 60 | 61 | ![menu](docs/images/dict.jpg) 62 | 63 | ## 字典数字界面 64 | 65 | ![menu](docs/images/dict_data.jpg) 66 | 67 | ## 通知界面 68 | 69 | ![menu](docs/images/notice.jpg) 70 | 71 | ## 登录日志 72 | 73 | ![menu](docs/images/login_log.jpg) -------------------------------------------------------------------------------- /src/route/system/sys_menu_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_menu_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | 7 | /* 8 | *构建菜单信息路由 9 | *author:刘飞华 10 | *date:2024/12/12 17:04:49 11 | */ 12 | pub fn build_sys_menu_route() -> Router> { 13 | Router::new() 14 | .route("/system/menu/addMenu", post(sys_menu_handler::add_sys_menu)) 15 | .route("/system/menu/deleteMenu", post(sys_menu_handler::delete_sys_menu)) 16 | .route("/system/menu/updateMenu", post(sys_menu_handler::update_sys_menu)) 17 | .route("/system/menu/updateMenuStatus", post(sys_menu_handler::update_sys_menu_status)) 18 | .route("/system/menu/queryMenuDetail", post(sys_menu_handler::query_sys_menu_detail)) 19 | .route("/system/menu/queryMenuList", post(sys_menu_handler::query_sys_menu_list)) 20 | .route("/system/menu/queryMenuListSimple", post(sys_menu_handler::query_sys_menu_list_simple)) 21 | .route("/system/menu/queryMenuResourceList", post(sys_menu_handler::query_sys_menu_resource_list)) 22 | //记得在main.rs中添加路由build_sys_menu_route() 23 | } 24 | -------------------------------------------------------------------------------- /docs/sql/system/sys_post.sql: -------------------------------------------------------------------------------- 1 | drop table if exists sys_post; 2 | create table sys_post 3 | ( 4 | id bigint auto_increment comment '岗位id' 5 | primary key, 6 | post_code varchar(64) not null comment '岗位编码', 7 | post_name varchar(50) not null comment '岗位名称', 8 | sort int default 0 not null comment '显示顺序', 9 | status tinyint default 0 not null comment '岗位状态(0:停用,1:正常)', 10 | remark varchar(500) default '' not null comment '备注', 11 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 12 | update_time datetime null on update CURRENT_TIMESTAMP comment '更新时间' 13 | ) comment = '岗位信息表'; 14 | 15 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('ceo', '董事长', 1, 1, ''); 16 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('se', '项目经理', 2, 1, ''); 17 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('hr', '人力资源', 3, 1, ''); 18 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('user', '普通员工', 1, 1, ''); 19 | -------------------------------------------------------------------------------- /src/model/system/sys_role_menu_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use rbatis::rbdc::datetime::DateTime; 5 | use rbatis::RBatis; 6 | use serde::{Deserialize, Serialize}; 7 | use std::collections::HashMap; 8 | 9 | /* 10 | *菜单角色关联表 11 | *author:刘飞华 12 | *date:2024/12/12 14:41:44 13 | */ 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct RoleMenu { 16 | pub id: Option, //主键 17 | pub role_id: i64, //角色ID 18 | pub menu_id: i64, //菜单ID 19 | pub create_time: Option, //创建时间 20 | } 21 | 22 | /* 23 | *菜单角色关联表基本操作 24 | *author:刘飞华 25 | *date:2024/12/12 14:41:44 26 | */ 27 | rbatis::crud!(RoleMenu {}, "sys_role_menu"); 28 | 29 | /* 30 | *根据角色id查询菜单ids 31 | *author:刘飞华 32 | *date:2024/12/12 14:41:44 33 | */ 34 | #[sql("select menu_id from sys_role_menu where role_id = ?")] 35 | pub async fn query_menu_by_role(rb: &RBatis, role_id: i64) -> rbatis::Result>> { 36 | impled!() 37 | } 38 | 39 | /* 40 | *查询菜单使用数量 41 | *author:刘飞华 42 | *date:2024/12/25 10:01:11 43 | */ 44 | #[sql("select count(1) from sys_role_menu where menu_id= ?")] 45 | pub async fn select_count_menu_by_menu_id(rb: &RBatis, menu_id: &i64) -> rbatis::Result { 46 | impled!() 47 | } 48 | -------------------------------------------------------------------------------- /src/model/system/sys_role_dept_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /* 8 | *角色和部门关联表 9 | *author:刘飞华 10 | *date:2024/12/25 10:01:11 11 | */ 12 | #[derive(Clone, Debug, Serialize, Deserialize)] 13 | pub struct RoleDept { 14 | pub role_id: i64, //角色id 15 | pub dept_id: i64, //部门id 16 | } 17 | 18 | /* 19 | *角色和部门关联表基本操作 20 | *author:刘飞华 21 | *date:2024/12/25 10:01:11 22 | */ 23 | rbatis::crud!(RoleDept {}, "sys_role_dept"); 24 | 25 | /* 26 | *根据id查询角色和部门关联表 27 | *author:刘飞华 28 | *date:2024/12/25 10:01:11 29 | */ 30 | impl_select!(RoleDept{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_role_dept"); 31 | 32 | /* 33 | *分页查询角色和部门关联表 34 | *author:刘飞华 35 | *date:2024/12/25 10:01:11 36 | */ 37 | impl_select_page!(RoleDept{select_page() =>" 38 | if !sql.contains('count'): 39 | order by create_time desc" 40 | },"sys_role_dept"); 41 | 42 | /* 43 | *根据条件分页查询角色和部门关联表 44 | *author:刘飞华 45 | *date:2024/12/25 10:01:11 46 | */ 47 | impl_select_page!(RoleDept{select_page_by_name(name:&str) =>" 48 | if name != null && name != '': 49 | where real_name != #{name} 50 | if name == '': 51 | where real_name != ''" 52 | },"sys_role_dept"); 53 | -------------------------------------------------------------------------------- /docs/sql/system/sys_oper_log.sql: -------------------------------------------------------------------------------- 1 | drop table if exists sys_operate_log; 2 | create table sys_operate_log 3 | ( 4 | id bigint auto_increment comment '日志主键' 5 | primary key, 6 | title varchar(50) default '' comment '模块标题', 7 | business_type tinyint default 0 comment '业务类型(0其它 1新增 2修改 3删除)', 8 | method varchar(200) default '' comment '方法名称', 9 | request_method varchar(10) default '' comment '请求方式', 10 | operator_type tinyint default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', 11 | operate_name varchar(50) default '' comment '操作人员', 12 | dept_name varchar(50) default '' comment '部门名称', 13 | operate_url varchar(255) default '' comment '请求URL', 14 | operate_ip varchar(128) default '' comment '主机地址', 15 | operate_location varchar(255) default '' comment '操作地点', 16 | operate_param varchar(2000) default '' comment '请求参数', 17 | json_result varchar(2000) default '' comment '返回参数', 18 | status tinyint default 0 comment '操作状态(0:异常,正常)', 19 | error_msg varchar(2000) default '' comment '错误消息', 20 | operate_time datetime default CURRENT_TIMESTAMP not null comment '操作时间', 21 | cost_time bigint(20) default 0 comment '消耗时间' 22 | 23 | ) comment = '操作日志记录'; 24 | 25 | -------------------------------------------------------------------------------- /docs/sql/system/sys_role.sql: -------------------------------------------------------------------------------- 1 | create table sys_role 2 | ( 3 | id bigint auto_increment comment '主键' 4 | primary key, 5 | role_name varchar(50) not null comment '名称', 6 | role_key varchar(100) default '' not null comment '角色权限字符串', 7 | data_scope tinyint default 1 not null comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', 8 | status tinyint default 1 not null comment '状态(1:正常,0:禁用)', 9 | remark varchar(255) not null comment '备注', 10 | del_flag tinyint default 1 not null comment '删除标志(0代表删除 1代表存在)', 11 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 12 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间', 13 | constraint role_name 14 | unique (role_name) 15 | ) comment '角色信息'; 16 | 17 | create index name_status_index 18 | on sys_role (role_name, status); 19 | 20 | INSERT INTO sys_role (id, role_name, role_key, status, remark) VALUES (1, '超级管理员', 'admin',1, '全部权限'); 21 | INSERT INTO sys_role (id, role_name, role_key, status, remark) VALUES (2, '演示角色', 'query',1, '仅有查看功能'); 22 | INSERT INTO sys_role (id, role_name, role_key, status, remark) VALUES (3, '121', 'dev',0, '121211'); 23 | -------------------------------------------------------------------------------- /docs/test/system/sys_post.http: -------------------------------------------------------------------------------- 1 | ###添加岗位信息表 addPost 2 | POST {{host}}/api/system/post/addPost 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "postCode": "abc12", 8 | "postName": "测试2", 9 | "sort": 0, 10 | "status": 1, 11 | "remark": "123" 12 | 13 | } 14 | 15 | ###删除岗位信息表 deletePost 16 | POST {{host}}/api/system/post/deletePost 17 | Content-Type: application/json 18 | Authorization: Bearer {{token}} 19 | 20 | { 21 | "ids": [1] 22 | } 23 | 24 | ###更新岗位信息表 updatePost 25 | POST {{host}}/api/system/post/updatePost 26 | Content-Type: application/json 27 | Authorization: Bearer {{token}} 28 | 29 | { 30 | "id": 5, 31 | "postCode": "abc12", 32 | "postName": "测试2", 33 | "sort": 0, 34 | "status": 1, 35 | "remark": "123" 36 | } 37 | 38 | ###更新岗位信息表 updatePostStatus状态 39 | POST {{host}}/api/system/post/updatePostStatus 40 | Content-Type: application/json 41 | Authorization: Bearer {{token}} 42 | 43 | { 44 | "ids": [13], 45 | "status": 1 46 | } 47 | ###查询岗位信息表详情 queryPostDetail 48 | POST {{host}}/api/system/post/queryPostDetail 49 | Content-Type: application/json 50 | Authorization: Bearer {{token}} 51 | 52 | { 53 | "id": 5 54 | } 55 | 56 | 57 | ###查询岗位信息表列表 queryPostList 58 | POST {{host}}/api/system/post/queryPostList 59 | Content-Type: application/json 60 | Authorization: Bearer {{token}} 61 | 62 | { 63 | "pageNo": 1, 64 | "pageSize": 10 65 | } 66 | 67 | -------------------------------------------------------------------------------- /docs/sql/system/sys_login_log.sql: -------------------------------------------------------------------------------- 1 | drop table if exists sys_login_log; 2 | create table sys_login_log 3 | ( 4 | id bigint auto_increment comment '访问ID' 5 | primary key, 6 | login_name varchar(50) default '' not null comment '登录账号', 7 | ipaddr varchar(128) default '' not null comment '登录IP地址', 8 | login_location varchar(255) default '' not null comment '登录地点', 9 | platform varchar(50) default '' not null comment '平台信息', 10 | browser varchar(50) default '' not null comment '浏览器类型', 11 | version varchar(50) default '' not null comment '浏览器版本', 12 | os varchar(50) default '' not null comment '操作系统', 13 | arch varchar(50) default '' not null comment '体系结构信息', 14 | engine varchar(50) default '' not null comment '渲染引擎信息', 15 | engine_details varchar(50) default '' not null comment '渲染引擎详细信息', 16 | extra varchar(50) default '' not null comment '其他信息(可选)', 17 | status tinyint default 0 not null comment '登录状态(0:失败,1:成功)', 18 | msg varchar(255) default '' not null comment '提示消息', 19 | login_time datetime default CURRENT_TIMESTAMP not null comment '访问时间' 20 | ) comment = '系统访问记录'; 21 | -------------------------------------------------------------------------------- /docs/test/system/sys_dict_type.http: -------------------------------------------------------------------------------- 1 | ###添加字典类型表 addDictType 2 | POST {{host}}/api/system/dictType/addDictType 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "dictName": "1", 8 | "dictType": "1", 9 | "status": 0, 10 | "remark": "1" 11 | 12 | } 13 | 14 | ###删除字典类型表 deleteDictType 15 | POST {{host}}/api/system/dictType/deleteDictType 16 | Content-Type: application/json 17 | Authorization: Bearer {{token}} 18 | 19 | { 20 | "ids": [13] 21 | } 22 | 23 | ###更新字典类型表 updateDictType 24 | POST {{host}}/api/system/dictType/updateDictType 25 | Content-Type: application/json 26 | Authorization: Bearer {{token}} 27 | 28 | { 29 | "dictId": 0, 30 | "dictName": "", 31 | "dictType": "", 32 | "status": 0, 33 | "remark": "" 34 | } 35 | 36 | ###更新字典类型表 updateDictTypeStatus状态 37 | POST {{host}}/api/system/dictType/updateDictTypeStatus 38 | Content-Type: application/json 39 | Authorization: Bearer {{token}} 40 | 41 | { 42 | "ids": [13], 43 | "status": 1 44 | } 45 | ###查询字典类型表详情 queryDictTypeDetail 46 | POST {{host}}/api/system/dictType/queryDictTypeDetail 47 | Content-Type: application/json 48 | Authorization: Bearer {{token}} 49 | 50 | { 51 | "id": 1 52 | } 53 | 54 | 55 | ###查询字典类型表列表 queryDictTypeList 56 | POST {{host}}/api/system/dictType/queryDictTypeList 57 | Content-Type: application/json 58 | Authorization: Bearer {{token}} 59 | 60 | { 61 | "pageNo": 1, 62 | "pageSize": 10 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/route/system/sys_user_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_user_handler; 2 | use crate::AppState; 3 | use axum::routing::{get, post}; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | 7 | /* 8 | *构建用户信息路由 9 | *author:刘飞华 10 | *date:2024/12/12 17:04:49 11 | */ 12 | pub fn build_sys_user_route() -> Router> { 13 | Router::new() 14 | .route("/system/user/addUser", post(sys_user_handler::add_sys_user)) 15 | .route("/system/user/deleteUser", post(sys_user_handler::delete_sys_user)) 16 | .route("/system/user/updateUser", post(sys_user_handler::update_sys_user)) 17 | .route("/system/user/updateUserStatus", post(sys_user_handler::update_sys_user_status)) 18 | .route("/system/user/reset_sys_user_password", post(sys_user_handler::reset_sys_user_password)) 19 | .route("/system/user/queryUserDetail", post(sys_user_handler::query_sys_user_detail)) 20 | .route("/system/user/queryUserList", post(sys_user_handler::query_sys_user_list)) 21 | .route("/system/user/login", post(sys_user_handler::login)) 22 | .route("/system/user/queryUserMenu", get(sys_user_handler::query_user_menu)) 23 | .route("/system/user/queryUserRole", post(sys_user_handler::query_user_role)) 24 | .route("/system/user/updateUserRole", post(sys_user_handler::update_user_role)) 25 | .route("/system/user/updateUserPassword", post(sys_user_handler::update_sys_user_password)) 26 | //记得在main.rs中添加路由build_sys_user_route() 27 | } 28 | -------------------------------------------------------------------------------- /src/model/system/sys_user_role_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use rbatis::rbdc::datetime::DateTime; 5 | use rbatis::RBatis; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | *角色用户关联表 10 | *author:刘飞华 11 | *date:2024/12/12 14:41:44 12 | */ 13 | #[derive(Clone, Debug, Serialize, Deserialize)] 14 | pub struct UserRole { 15 | pub id: Option, //主键 16 | pub user_id: i64, //用户ID 17 | pub role_id: i64, //角色ID 18 | pub create_time: Option, //创建时间 19 | } 20 | 21 | /* 22 | *角色用户关联表基本操作 23 | *author:刘飞华 24 | *date:2024/12/12 14:41:44 25 | */ 26 | rbatis::crud!(UserRole {}, "sys_user_role"); 27 | 28 | /* 29 | *查询是否为超级管理员(role_id=1是预设超级管理的id) 30 | *author:刘飞华 31 | *date:2024/12/12 14:41:44 32 | */ 33 | #[sql("select count(1) from sys_user_role where role_id = 1 and user_id = ?")] 34 | pub async fn is_admin(rb: &RBatis, user_id: &i64) -> rbatis::Result { 35 | impled!() 36 | } 37 | 38 | /* 39 | *通过角色id查询角色使用数量 40 | *author:刘飞华 41 | *date:2024/12/12 14:41:44 42 | */ 43 | #[sql("select count(1) from sys_user_role where role_id = ?")] 44 | pub async fn count_user_role_by_role_id(rb: &RBatis, role_id: i64) -> rbatis::Result { 45 | impled!() 46 | } 47 | 48 | /* 49 | *通过角色id和用户id删除 50 | *author:刘飞华 51 | *date:2024/12/12 14:41:44 52 | */ 53 | #[sql("delete from sys_user_role where role_id = ? and user_id = ?")] 54 | pub async fn delete_user_role_by_role_id_user_id(rb: &RBatis, role_id: i64, user_id: i64) -> Option { 55 | impled!() 56 | } 57 | -------------------------------------------------------------------------------- /src/route/system/sys_role_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_role_handler; 2 | use crate::AppState; 3 | use axum::routing::post; 4 | use axum::Router; 5 | use std::sync::Arc; 6 | 7 | /* 8 | *构建角色信息路由 9 | *author:刘飞华 10 | *date:2024/12/12 17:04:49 11 | */ 12 | pub fn build_sys_role_route() -> Router> { 13 | Router::new() 14 | .route("/system/role/addRole", post(sys_role_handler::add_sys_role)) 15 | .route("/system/role/deleteRole", post(sys_role_handler::delete_sys_role)) 16 | .route("/system/role/updateRole", post(sys_role_handler::update_sys_role)) 17 | .route("/system/role/updateRoleStatus", post(sys_role_handler::update_sys_role_status)) 18 | .route("/system/role/queryRoleDetail", post(sys_role_handler::query_sys_role_detail)) 19 | .route("/system/role/queryRoleList", post(sys_role_handler::query_sys_role_list)) 20 | .route("/system/role/queryRoleMenu", post(sys_role_handler::query_role_menu)) 21 | .route("/system/role/updateRoleMenu", post(sys_role_handler::update_role_menu)) 22 | .route("/system/role/queryAllocatedList", post(sys_role_handler::query_allocated_list)) 23 | .route("/system/role/queryUnallocatedList", post(sys_role_handler::query_unallocated_list)) 24 | .route("/system/role/cancelAuthUser", post(sys_role_handler::cancel_auth_user)) 25 | .route("/system/role/batchCancelAuthUser", post(sys_role_handler::batch_cancel_auth_user)) 26 | .route("/system/role/batchAuthUser", post(sys_role_handler::batch_auth_user)) 27 | //记得在main.rs中添加路由build_sys_role_route() 28 | } 29 | -------------------------------------------------------------------------------- /src/model/system/sys_user_post_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use rbatis::RBatis; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | *用户与岗位关联 10 | *author:刘飞华 11 | *date:2024/12/25 10:01:11 12 | */ 13 | #[derive(Clone, Debug, Serialize, Deserialize)] 14 | pub struct UserPost { 15 | pub user_id: i64, //用户id 16 | pub post_id: i64, //岗位id 17 | } 18 | 19 | /* 20 | *用户与岗位关联基本操作 21 | *author:刘飞华 22 | *date:2024/12/25 10:01:11 23 | */ 24 | rbatis::crud!(UserPost {}, "sys_user_post"); 25 | 26 | /* 27 | *根据id查询用户与岗位关联 28 | *author:刘飞华 29 | *date:2024/12/25 10:01:11 30 | */ 31 | impl_select!(UserPost{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_user_post"); 32 | 33 | /* 34 | *分页查询用户与岗位关联 35 | *author:刘飞华 36 | *date:2024/12/25 10:01:11 37 | */ 38 | impl_select_page!(UserPost{select_page() =>" 39 | if !sql.contains('count'): 40 | order by create_time desc" 41 | },"sys_user_post"); 42 | 43 | /* 44 | *根据条件分页查询用户与岗位关联 45 | *author:刘飞华 46 | *date:2024/12/25 10:01:11 47 | */ 48 | impl_select_page!(UserPost{select_page_by_name(name:&str) =>" 49 | if name != null && name != '': 50 | where real_name != #{name} 51 | if name == '': 52 | where real_name != ''" 53 | },"sys_user_post"); 54 | 55 | /* 56 | *通过岗位id查询岗位使用数量 57 | *author:刘飞华 58 | *date:2024/12/12 14:41:44 59 | */ 60 | #[sql("select count(1) from sys_user_post where post_id = ? ")] 61 | pub async fn count_user_post_by_id(rb: &RBatis, post_id: i64) -> rbatis::Result { 62 | impled!() 63 | } 64 | -------------------------------------------------------------------------------- /docs/test/system/sys_notice.http: -------------------------------------------------------------------------------- 1 | ###添加通知公告表 addNotice 2 | POST {{host}}/api/system/notice/addNotice 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "noticeTitle": "123ggg", 8 | "noticeType": 1, 9 | "noticeContent": "sdfs", 10 | "status": 1, 11 | "remark": "sfdsdf" 12 | 13 | } 14 | 15 | ###删除通知公告表 deleteNotice 16 | POST {{host}}/api/system/notice/deleteNotice 17 | Content-Type: application/json 18 | Authorization: Bearer {{token}} 19 | 20 | { 21 | "ids": [13] 22 | } 23 | 24 | ###更新通知公告表 updateNotice 25 | POST {{host}}/api/system/notice/updateNotice 26 | Content-Type: application/json 27 | Authorization: Bearer {{token}} 28 | 29 | { 30 | "id": 1, 31 | "noticeTitle": "123ggg", 32 | "noticeType": 0, 33 | "noticeContent": "sdfs", 34 | "status": 0, 35 | "remark": "sfdsdf" 36 | } 37 | 38 | ###更新通知公告表 updateNoticeStatus状态 39 | POST {{host}}/api/system/notice/updateNoticeStatus 40 | Content-Type: application/json 41 | Authorization: Bearer {{token}} 42 | 43 | { 44 | "ids": [13], 45 | "status": 1 46 | } 47 | ###查询通知公告表详情 queryNoticeDetail 48 | POST {{host}}/api/system/notice/queryNoticeDetail 49 | Content-Type: application/json 50 | Authorization: Bearer {{token}} 51 | 52 | { 53 | "id": 1 54 | } 55 | 56 | 57 | ###查询通知公告表列表 queryNoticeList 58 | POST {{host}}/api/system/notice/queryNoticeList 59 | Content-Type: application/json 60 | Authorization: Bearer {{token}} 61 | 62 | #{ 63 | # "pageNo": 1, 64 | # "pageSize": 10, 65 | # "noticeTitle": "", 66 | # "noticeType": 0, 67 | # "status": 0 68 | #} 69 | 70 | { 71 | "pageNo": 1, 72 | "pageSize": 10, 73 | "noticeTitle": "123" 74 | } 75 | -------------------------------------------------------------------------------- /docs/test/system/sys_dict_data.http: -------------------------------------------------------------------------------- 1 | ###添加字典数据表 addDictData 2 | POST {{host}}/api/system/dictData/addDictData 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "dictSort": 1, 8 | "dictLabel": "1", 9 | "dictValue": "1", 10 | "dictType": "1", 11 | "cssClass": "1", 12 | "listClass": "1", 13 | "isDefault": "1", 14 | "status": 1, 15 | "remark": "" 16 | 17 | } 18 | 19 | ###删除字典数据表 deleteDictData 20 | POST {{host}}/api/system/dictData/deleteDictData 21 | Content-Type: application/json 22 | Authorization: Bearer {{token}} 23 | 24 | { 25 | "ids": [13] 26 | } 27 | 28 | ###更新字典数据表 updateDictData 29 | POST {{host}}/api/system/dictData/updateDictData 30 | Content-Type: application/json 31 | Authorization: Bearer {{token}} 32 | 33 | { 34 | "dictCode": 0, 35 | "dictSort": 0, 36 | "dictLabel": "", 37 | "dictValue": "", 38 | "dictType": "", 39 | "cssClass": "", 40 | "listClass": "", 41 | "isDefault": "", 42 | "status": 0, 43 | "remark": "", 44 | } 45 | 46 | ###更新字典数据表 updateDictDataStatus状态 47 | POST {{host}}/api/system/dictData/updateDictDataStatus 48 | Content-Type: application/json 49 | Authorization: Bearer {{token}} 50 | 51 | { 52 | "ids": [13], 53 | "status": 1 54 | } 55 | ###查询字典数据表详情 queryDictDataDetail 56 | POST {{host}}/api/system/dictData/queryDictDataDetail 57 | Content-Type: application/json 58 | Authorization: Bearer {{token}} 59 | 60 | { 61 | "id": 1 62 | } 63 | 64 | 65 | ###查询字典数据表列表 queryDictDataList 66 | POST {{host}}/api/system/dictData/queryDictDataList 67 | Content-Type: application/json 68 | Authorization: Bearer {{token}} 69 | 70 | { 71 | "pageNo": 1, 72 | "pageSize": 10 73 | } 74 | 75 | -------------------------------------------------------------------------------- /docs/test/system/sys_menu.http: -------------------------------------------------------------------------------- 1 | ###添加菜单信息 addMenu 2 | POST {{host}}/api/system/menu/addMenu 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "menuName": "首页1", 8 | "menuType": 1, 9 | "visible": 1, 10 | "status": 1, 11 | "sort": 1, 12 | "parentId": 0, 13 | "menuUrl": "/home", 14 | "apiUrl": "", 15 | "menuIcon": "DashboardOutlined", 16 | "remark": "首页" 17 | } 18 | ###删除菜单信息 deleteMenu 19 | POST {{host}}/api/system/menu/deleteMenu 20 | Content-Type: application/json 21 | Authorization: Bearer {{token}} 22 | 23 | { 24 | "id": 31 25 | } 26 | 27 | ###更新菜单信息 updateMenu 28 | POST {{host}}/api/system/menu/updateMenu 29 | Content-Type: application/json 30 | Authorization: Bearer {{token}} 31 | 32 | { 33 | "id": 1, 34 | "menuName": "首页", 35 | "menuType": 1, 36 | "visible": 1, 37 | "status": 1, 38 | "sort": 1, 39 | "parentId": 0, 40 | "menuUrl": "/home", 41 | "apiUrl": "", 42 | "menuIcon": "DashboardOutlined", 43 | "remark": "首页" 44 | } 45 | 46 | ###更新菜单信息 updateMenuStatus状态 47 | POST {{host}}/api/system/menu/updateMenuStatus 48 | Content-Type: application/json 49 | Authorization: Bearer {{token}} 50 | 51 | { 52 | "ids": [31], 53 | "status": 0 54 | } 55 | ###查询菜单信息详情 queryMenu 56 | POST {{host}}/api/system/menu/queryMenuDetail 57 | Content-Type: application/json 58 | Authorization: Bearer {{token}} 59 | 60 | { 61 | "id": 1 62 | } 63 | 64 | 65 | ###查询菜单信息列表 queryMenuList 66 | POST {{host}}/api/system/menu/queryMenuList 67 | Content-Type: application/json 68 | Authorization: Bearer {{token}} 69 | 70 | { 71 | 72 | } 73 | 74 | ###查询菜单信息列表 queryMenuListSimple 75 | GET {{host}}/api/system/menu/queryMenuListSimple 76 | Authorization: Bearer {{token}} 77 | 78 | -------------------------------------------------------------------------------- /docs/test/system/sys_dept.http: -------------------------------------------------------------------------------- 1 | ###添加部门表 addDept 2 | POST {{host}}/api/system/dept/addDept 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "parentId": 1, 8 | "ancestors": "1,2,3", 9 | "deptName": "测试", 10 | "sort": 1, 11 | "leader": "测试1", 12 | "phone": "123", 13 | "email": "123", 14 | "status": 0 15 | } 16 | 17 | ###删除部门表 deleteDept 18 | POST {{host}}/api/system/dept/deleteDept 19 | Content-Type: application/json 20 | Authorization: Bearer {{token}} 21 | 22 | { 23 | "ids": [13] 24 | } 25 | 26 | ###更新部门表 updateDept 27 | POST {{host}}/api/system/dept/updateDept 28 | Content-Type: application/json 29 | Authorization: Bearer {{token}} 30 | 31 | { 32 | "id": 1, 33 | "parentId": 1, 34 | "ancestors": "1,2,3", 35 | "deptName": "测试123", 36 | "sort": 1, 37 | "leader": "测试1231", 38 | "phone": "123", 39 | "email": "123", 40 | "status": 0, 41 | "del_flag": 1 42 | } 43 | 44 | ###更新部门表 updateDeptStatus状态 45 | POST {{host}}/api/system/dept/updateDeptStatus 46 | Content-Type: application/json 47 | Authorization: Bearer {{token}} 48 | 49 | { 50 | "ids": [13], 51 | "status": 1 52 | } 53 | ###查询部门表详情 queryDeptDetail 54 | POST {{host}}/api/system/dept/queryDeptDetail 55 | Content-Type: application/json 56 | Authorization: Bearer {{token}} 57 | 58 | { 59 | "id": 1 60 | } 61 | 62 | 63 | ###查询部门表列表 queryDeptList 64 | POST {{host}}/api/system/dept/queryDeptList 65 | Content-Type: application/json 66 | Authorization: Bearer {{token}} 67 | 68 | 69 | //{ 70 | // "pageNo": 1, 71 | // "pageSize": 10, 72 | // "parentId": 0, 73 | // "ancestors": "", 74 | // "deptName": "", 75 | // "leader": "", 76 | // "phone": "", 77 | // "email": "", 78 | // "status": 0 79 | // 80 | //} 81 | 82 | { 83 | "pageNo": 1, 84 | "pageSize": 10 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/vo/system/sys_login_log_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | use crate::common::result::serialize_datetime; 4 | use rbatis::rbdc::DateTime; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /* 8 | 删除系统访问记录请求参数 9 | */ 10 | #[derive(Debug, Serialize, Deserialize)] 11 | pub struct DeleteLoginLogReq { 12 | pub ids: Vec, 13 | } 14 | 15 | /* 16 | 查询系统访问记录详情请求参数 17 | */ 18 | #[derive(Debug, Serialize, Deserialize)] 19 | pub struct QueryLoginLogDetailReq { 20 | pub id: i64, 21 | } 22 | 23 | /* 24 | 查询系统访问记录列表请求参数 25 | */ 26 | #[derive(Debug, Serialize, Deserialize)] 27 | #[serde(rename_all = "camelCase")] 28 | pub struct QueryLoginLogListReq { 29 | pub page_no: u64, 30 | pub page_size: u64, 31 | pub login_name: Option, //登录账号 32 | pub ipaddr: Option, //登录IP地址 33 | pub login_location: Option, //登录地点 34 | pub browser: Option, //浏览器类型 35 | pub os: Option, //操作系统 36 | #[serde(default = "default_status")] 37 | pub status: Option, //登录状态(0:失败,1:成功) 38 | } 39 | fn default_status() -> Option { 40 | Some(2) 41 | } 42 | /* 43 | 查询系统访问记录列表响应参数 44 | */ 45 | #[derive(Debug, Serialize, Deserialize)] 46 | #[serde(rename_all = "camelCase")] 47 | pub struct LoginLogResp { 48 | pub id: Option, //访问ID 49 | pub login_name: String, //登录账号 50 | pub ipaddr: String, //登录IP地址 51 | pub login_location: String, //登录地点 52 | pub platform: String, //平台信息 53 | pub browser: String, //浏览器类型 54 | pub version: String, //浏览器版本 55 | pub os: String, //操作系统 56 | pub arch: String, //体系结构信息 57 | pub engine: String, //渲染引擎信息 58 | pub engine_details: String, //渲染引擎详细信息 59 | pub extra: String, //其他信息(可选) 60 | pub status: i8, //登录状态(0:失败,1:成功) 61 | pub msg: String, //提示消息 62 | #[serde(serialize_with = "serialize_datetime")] 63 | pub login_time: Option, //访问时间 64 | } 65 | -------------------------------------------------------------------------------- /docs/sql/system/sys_dict_data.sql: -------------------------------------------------------------------------------- 1 | create table sys_dict_data 2 | ( 3 | id bigint not null auto_increment comment '字典编码', 4 | dict_sort int default 0 not null comment '字典排序', 5 | dict_label varchar(100) default '' not null comment '字典标签', 6 | dict_value varchar(100) default '' not null comment '字典键值', 7 | dict_type varchar(100) default '' not null comment '字典类型', 8 | css_class varchar(100) default '' not null comment '样式属性(其他样式扩展)', 9 | list_class varchar(100) default '' not null comment '表格回显样式', 10 | is_default char(1) default 'N' not null comment '是否默认(Y是 N否)', 11 | status tinyint default 0 not null comment '状态(0:停用,1:正常)', 12 | remark varchar(500) default '' not null comment '备注', 13 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 14 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间', 15 | primary key (id) 16 | )comment = '字典数据表'; 17 | 18 | 19 | INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, remark) VALUES (1, '男', '0', 'sys_user_sex', '1', '1', 'N', 1, '性别男'); 20 | INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, remark) VALUES (2, '女', '1', 'sys_user_sex', '1', '1', 'N', 1, '性别女'); 21 | INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, remark) VALUES (3, '未知', '2', 'sys_user_sex', '1', '1', 'N', 1, '性别未知'); 22 | INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, remark) VALUES (1, '通知', '1', 'sys_notice_type', '1', '1', 'N', 1, '通知'); 23 | INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, remark) VALUES (2, '公告', '2', 'sys_notice_type', '1', '1', 'N', 1, '公告'); 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/vo/system/sys_dict_type_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除字典类型表请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeleteDictTypeReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 更新字典类型表请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct DictTypeReq { 22 | pub id: Option, //字典主键 23 | pub dict_name: String, //字典名称 24 | pub dict_type: String, //字典类型 25 | pub status: i8, //状态(0:停用,1:正常) 26 | pub remark: Option, //备注 27 | } 28 | 29 | /* 30 | 更新字典类型表状态请求参数 31 | */ 32 | #[derive(Debug, Serialize, Deserialize)] 33 | pub struct UpdateDictTypeStatusReq { 34 | pub ids: Vec, 35 | pub status: i8, 36 | } 37 | 38 | /* 39 | 查询字典类型表详情请求参数 40 | */ 41 | #[derive(Debug, Serialize, Deserialize)] 42 | pub struct QueryDictTypeDetailReq { 43 | pub id: i64, 44 | } 45 | 46 | /* 47 | 查询字典类型表列表请求参数 48 | */ 49 | #[derive(Debug, Serialize, Deserialize)] 50 | #[serde(rename_all = "camelCase")] 51 | pub struct QueryDictTypeListReq { 52 | pub page_no: u64, 53 | pub page_size: u64, 54 | pub dict_name: Option, //字典名称 55 | pub dict_type: Option, //字典类型 56 | #[serde(default = "default_status")] 57 | pub status: Option, //状态(0:停用,1:正常) 58 | } 59 | fn default_status() -> Option { 60 | Some(2) 61 | } 62 | /* 63 | 查询字典类型表列表响应参数 64 | */ 65 | #[derive(Debug, Serialize, Deserialize)] 66 | #[serde(rename_all = "camelCase")] 67 | pub struct DictTypeResp { 68 | pub id: Option, //字典主键 69 | pub dict_name: String, //字典名称 70 | pub dict_type: String, //字典类型 71 | pub status: i8, //状态(0:停用,1:正常) 72 | pub remark: Option, //备注 73 | #[serde(serialize_with = "serialize_datetime")] 74 | pub create_time: Option, //创建时间 75 | #[serde(serialize_with = "serialize_datetime")] 76 | pub update_time: Option, //修改时间 77 | } 78 | -------------------------------------------------------------------------------- /src/vo/system/sys_post_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除岗位信息表请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeletePostReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 更新岗位信息表请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct PostReq { 22 | pub id: Option, //岗位id 23 | pub post_code: String, //岗位编码 24 | pub post_name: String, //岗位名称 25 | pub sort: i32, //显示顺序 26 | pub status: i8, //部状态(0:停用,1:正常) 27 | pub remark: Option, //备注 28 | } 29 | 30 | /* 31 | 更新岗位信息表状态请求参数 32 | */ 33 | #[derive(Debug, Serialize, Deserialize)] 34 | pub struct UpdatePostStatusReq { 35 | pub ids: Vec, 36 | pub status: i8, 37 | } 38 | 39 | /* 40 | 查询岗位信息表详情请求参数 41 | */ 42 | #[derive(Debug, Serialize, Deserialize)] 43 | pub struct QueryPostDetailReq { 44 | pub id: i64, 45 | } 46 | 47 | /* 48 | 查询岗位信息表列表请求参数 49 | */ 50 | #[derive(Debug, Serialize, Deserialize)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct QueryPostListReq { 53 | pub page_no: u64, 54 | pub page_size: u64, 55 | pub post_code: Option, //岗位编码 56 | pub post_name: Option, //岗位名称 57 | #[serde(default = "default_status")] 58 | pub status: Option, //部状态(0:停用,1:正常) 59 | } 60 | fn default_status() -> Option { 61 | Some(2) 62 | } 63 | /* 64 | 查询岗位信息表列表响应参数 65 | */ 66 | #[derive(Debug, Serialize, Deserialize)] 67 | #[serde(rename_all = "camelCase")] 68 | pub struct PostResp { 69 | pub id: Option, //岗位id 70 | pub post_code: String, //岗位编码 71 | pub post_name: String, //岗位名称 72 | pub sort: i32, //显示顺序 73 | pub status: i8, //部状态(0:停用,1:正常) 74 | pub remark: Option, //备注 75 | #[serde(serialize_with = "serialize_datetime")] 76 | pub create_time: Option, //创建时间 77 | #[serde(serialize_with = "serialize_datetime")] 78 | pub update_time: Option, //修改时间 79 | } 80 | -------------------------------------------------------------------------------- /src/common/result.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppResult; 2 | use axum::Json; 3 | use rbatis::rbdc::DateTime; 4 | use serde::Serialize; 5 | use std::fmt::Debug; 6 | 7 | // 统一返回vo 8 | #[derive(Serialize, Debug, Clone)] 9 | pub struct BaseResponse { 10 | pub code: i32, 11 | pub msg: String, 12 | pub data: Option, 13 | } 14 | 15 | #[derive(Serialize, Debug, Clone)] 16 | pub struct ResponsePage { 17 | pub code: i32, 18 | pub msg: String, 19 | pub total: u64, 20 | pub success: bool, 21 | pub data: Option, 22 | } 23 | 24 | pub fn ok() -> AppResult>> { 25 | ok_result_data(()) 26 | } 27 | 28 | pub fn ok_result() -> AppResult>> { 29 | ok_result_msg("操作成功") 30 | } 31 | 32 | pub fn ok_result_msg(msg: &str) -> AppResult>> { 33 | Ok(Json(BaseResponse { 34 | msg: msg.to_string(), 35 | code: 0, 36 | data: Some("None".to_string()), 37 | })) 38 | } 39 | 40 | pub fn ok_result_data(data: T) -> AppResult>> { 41 | Ok(Json(BaseResponse { 42 | msg: "操作成功".to_string(), 43 | code: 0, 44 | data: Some(data), 45 | })) 46 | } 47 | 48 | pub fn ok_result_page(data: T, total: u64) -> AppResult>> { 49 | Ok(Json(ResponsePage { 50 | msg: "操作成功".to_string(), 51 | code: 0, 52 | success: true, 53 | data: Some(data), 54 | total, 55 | })) 56 | } 57 | 58 | pub fn err_result_msg(msg: &str) -> AppResult>> { 59 | Ok(Json(BaseResponse { 60 | msg: msg.to_string(), 61 | code: 1, 62 | data: Some("None".to_string()), 63 | })) 64 | } 65 | 66 | pub fn serialize_datetime(dt: &Option, serializer: S) -> Result 67 | where 68 | S: serde::Serializer, 69 | { 70 | match dt { 71 | Some(datetime) => { 72 | let formatted = datetime.format("YYYY-MM-DD hh:mm:ss"); 73 | serializer.serialize_str(&formatted) 74 | } 75 | None => serializer.serialize_str(""), 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /docs/sql/system/sys_user.sql: -------------------------------------------------------------------------------- 1 | create table sys_user 2 | ( 3 | id bigint auto_increment comment '主键' 4 | primary key, 5 | mobile char(11) default '' not null comment '手机号码', 6 | user_name varchar(50) not null comment '用户账号', 7 | nick_name varchar(30) not null comment '用户昵称', 8 | user_type varchar(2) default '00' not null comment '用户类型(00系统用户)', 9 | avatar varchar(100) default '' not null comment '头像路径', 10 | email varchar(50) default '' not null comment '用户邮箱', 11 | password varchar(64) not null comment '密码', 12 | status tinyint default 1 not null comment '状态(1:正常,0:禁用)', 13 | dept_id bigint default 1 not null comment '部门ID', 14 | login_ip varchar(128) default '' not null comment '最后登录IP', 15 | login_date datetime comment '最后登录时间', 16 | login_browser varchar(50) default '' not null comment '浏览器类型', 17 | login_os varchar(50) default '' not null comment '操作系统', 18 | pwd_update_date datetime comment '密码最后更新时间', 19 | remark varchar(255) null comment '备注', 20 | del_flag tinyint default 1 not null comment '删除标志(0代表删除 1代表存在)', 21 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 22 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间', 23 | constraint AK_phone 24 | unique (mobile) 25 | ) comment '用户信息'; 26 | 27 | 28 | INSERT INTO sys_user (id, mobile, user_name, nick_name, avatar, email, password, status, remark) VALUES (1, '18613030111', 'admin','admin', 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png','xx@qq.com','123456', 1, '超级管理员'); 29 | INSERT INTO sys_user (id, mobile, user_name, nick_name, avatar, email, password, status, remark) VALUES (2, '18613030222', 'test', 'test', 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png','123@qq.com','123456', 1, '演示权限'); 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/vo/system/sys_notice_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除通知公告表请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeleteNoticeReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 更新通知公告表请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct NoticeReq { 22 | pub id: Option, //公告ID 23 | pub notice_title: String, //公告标题 24 | pub notice_type: i8, //公告类型(1:通知,2:公告) 25 | pub notice_content: String, //公告内容 26 | pub status: i8, //公告状态(0:关闭,1:正常 ) 27 | pub remark: Option, //备注 28 | } 29 | 30 | /* 31 | 更新通知公告表状态请求参数 32 | */ 33 | #[derive(Debug, Serialize, Deserialize)] 34 | pub struct UpdateNoticeStatusReq { 35 | pub ids: Vec, 36 | pub status: i8, 37 | } 38 | 39 | /* 40 | 查询通知公告表详情请求参数 41 | */ 42 | #[derive(Debug, Serialize, Deserialize)] 43 | pub struct QueryNoticeDetailReq { 44 | pub id: i64, 45 | } 46 | 47 | /* 48 | 查询通知公告表列表请求参数 49 | */ 50 | #[derive(Debug, Serialize, Deserialize)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct QueryNoticeListReq { 53 | pub page_no: u64, 54 | pub page_size: u64, 55 | pub notice_title: Option, //公告标题 56 | pub notice_type: Option, //公告类型(1:通知,2:公告) 57 | #[serde(default = "default_status")] 58 | pub status: Option, //公告状态(0:关闭,1:正常 ) 59 | } 60 | fn default_status() -> Option { 61 | Some(2) 62 | } 63 | /* 64 | 查询通知公告表列表响应参数 65 | */ 66 | #[derive(Debug, Serialize, Deserialize)] 67 | #[serde(rename_all = "camelCase")] 68 | pub struct NoticeResp { 69 | pub id: Option, //公告ID 70 | pub notice_title: String, //公告标题 71 | pub notice_type: i8, //公告类型(1:通知,2:公告) 72 | pub notice_content: String, //公告内容 73 | pub status: i8, //公告状态(0:关闭,1:正常 ) 74 | pub remark: Option, //备注 75 | #[serde(serialize_with = "serialize_datetime")] 76 | pub create_time: Option, //创建时间 77 | #[serde(serialize_with = "serialize_datetime")] 78 | pub update_time: Option, //修改时间 79 | } 80 | -------------------------------------------------------------------------------- /docs/test/system/sys_user.http: -------------------------------------------------------------------------------- 1 | ###登录 login 2 | POST {{host}}/api/system/user/login 3 | Content-Type: application/json 4 | 5 | { 6 | "account": "18613030111", 7 | "password": "123456" 8 | } 9 | > {% client.global.set("token", response.body.data); %} 10 | 11 | ###查询用户菜单 query_user_menu 12 | GET {{host}}/api/system/user/queryUserMenu 13 | Authorization: Bearer {{token}} 14 | 15 | ###添加用户信息 addUser 16 | POST {{host}}/api/system/user/addUser 17 | Content-Type: application/json 18 | Authorization: Bearer {{token}} 19 | 20 | { 21 | "mobile": "123123123", 22 | "userName": "22334", 23 | "status": 1, 24 | "sort": 11, 25 | "remark": "22334" 26 | } 27 | 28 | ###删除用户信息 deleteUser 29 | POST {{host}}/api/system/user/deleteUser 30 | Content-Type: application/json 31 | Authorization: Bearer {{token}} 32 | 33 | { 34 | "ids": [15] 35 | } 36 | 37 | ###更新用户信息 updateUser 38 | POST {{host}}/api/system/user/updateUser 39 | Content-Type: application/json 40 | Authorization: Bearer {{token}} 41 | 42 | { 43 | "id": 13, 44 | "mobile": "", 45 | "userName": "", 46 | "password": "", 47 | "status": 0, 48 | "sort": 0, 49 | "remark": "" 50 | } 51 | 52 | ###更新用户信息 updateUserStatus状态 53 | POST {{host}}/api/system/user/updateUserStatus 54 | Content-Type: application/json 55 | Authorization: Bearer {{token}} 56 | 57 | { 58 | "ids": [12,13], 59 | "status": 0 60 | } 61 | ###查询用户信息详情 queryUserDetail 62 | POST {{host}}/api/system/user/queryUserDetail 63 | Content-Type: application/json 64 | Authorization: Bearer {{token}} 65 | 66 | { 67 | "id": 1 68 | } 69 | 70 | 71 | ###查询用户信息列表 queryUserList 72 | POST {{host}}/api/system/user/queryUserList 73 | Content-Type: application/json 74 | Authorization: Bearer {{token}} 75 | 76 | { 77 | "pageNo1": 1, 78 | "pageSize": 10, 79 | "mobile": "", 80 | "userName": "", 81 | "status": 1 82 | } 83 | 84 | ###查询用户角色列表 queryUserRole 85 | POST {{host}}/api/system/user/queryUserRole 86 | Content-Type: application/json 87 | Authorization: Bearer {{token}} 88 | 89 | { 90 | "userId": 1 91 | } 92 | 93 | ###更新用户角色列表 updateUserRole 94 | POST {{host}}/api/system/user/updateUserRole 95 | Content-Type: application/json 96 | Authorization: Bearer {{token}} 97 | 98 | { 99 | "userId": 2, 100 | "roleIds": [1,2] 101 | } 102 | -------------------------------------------------------------------------------- /src/handler/system/sys_login_log_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_login_log_model::{clean_login_log, LoginLog}; 4 | use crate::vo::system::sys_login_log_vo::*; 5 | use crate::AppState; 6 | use axum::extract::State; 7 | use axum::response::IntoResponse; 8 | use axum::Json; 9 | use log::info; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbs::value; 12 | use std::sync::Arc; 13 | /* 14 | *删除系统访问记录 15 | *author:刘飞华 16 | *date:2024/12/25 11:36:48 17 | */ 18 | pub async fn delete_sys_login_log(State(state): State>, Json(item): Json) -> impl IntoResponse { 19 | info!("delete sys_login_log params: {:?}", &item); 20 | let rb = &state.batis; 21 | 22 | LoginLog::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result())? 23 | } 24 | 25 | /* 26 | *清空系统登录日志 27 | *author:刘飞华 28 | *date:2024/12/25 11:36:48 29 | */ 30 | pub async fn clean_sys_login_log(State(state): State>) -> impl IntoResponse { 31 | info!("clean sys_login_log "); 32 | let rb = &state.batis; 33 | 34 | clean_login_log(rb).await.map(|_| ok_result())? 35 | } 36 | 37 | /* 38 | *查询系统访问记录详情 39 | *author:刘飞华 40 | *date:2024/12/25 11:36:48 41 | */ 42 | pub async fn query_sys_login_log_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 43 | info!("query sys_login_log_detail params: {:?}", &item); 44 | let rb = &state.batis; 45 | 46 | LoginLog::select_by_id(rb, &item.id).await?.map_or_else( 47 | || Err(AppError::BusinessError("系统访问记录不存在")), 48 | |x| { 49 | let data: LoginLogResp = x.into(); 50 | ok_result_data(data) 51 | }, 52 | ) 53 | } 54 | 55 | /* 56 | *查询系统访问记录列表 57 | *author:刘飞华 58 | *date:2024/12/25 11:36:48 59 | */ 60 | pub async fn query_sys_login_log_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 61 | info!("query sys_login_log_list params: {:?}", &item); 62 | let rb = &state.batis; 63 | 64 | let page = &PageRequest::new(item.page_no, item.page_size); 65 | 66 | LoginLog::select_login_log_list(rb, page, &item) 67 | .await 68 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 69 | } 70 | -------------------------------------------------------------------------------- /src/handler/system/sys_operate_log_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_operate_log_model::{clean_operate_log, OperateLog}; 4 | use crate::vo::system::sys_operate_log_vo::*; 5 | use crate::AppState; 6 | use axum::extract::State; 7 | use axum::response::IntoResponse; 8 | use axum::Json; 9 | use log::info; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbs::value; 12 | use std::sync::Arc; 13 | /* 14 | *删除操作日志记录 15 | *author:刘飞华 16 | *date:2024/12/25 11:36:48 17 | */ 18 | pub async fn delete_sys_operate_log(State(state): State>, Json(item): Json) -> impl IntoResponse { 19 | info!("delete sys_operate_log params: {:?}", &item); 20 | let rb = &state.batis; 21 | 22 | OperateLog::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result())? 23 | } 24 | 25 | /* 26 | *清空操作日志记录 27 | *author:刘飞华 28 | *date:2024/12/25 11:36:48 29 | */ 30 | pub async fn clean_sys_operate_log(State(state): State>) -> impl IntoResponse { 31 | info!("clean sys_operate_log"); 32 | let rb = &state.batis; 33 | 34 | clean_operate_log(rb).await.map(|_| ok_result())? 35 | } 36 | 37 | /* 38 | *查询操作日志记录详情 39 | *author:刘飞华 40 | *date:2024/12/25 11:36:48 41 | */ 42 | pub async fn query_sys_operate_log_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 43 | info!("query sys_operate_log_detail params: {:?}", &item); 44 | let rb = &state.batis; 45 | 46 | OperateLog::select_by_id(rb, &item.id).await?.map_or_else( 47 | || Err(AppError::BusinessError("操作日志不存在")), 48 | |x| { 49 | let data: OperateLogResp = x.into(); 50 | ok_result_data(data) 51 | }, 52 | ) 53 | } 54 | /* 55 | *查询操作日志记录列表 56 | *author:刘飞华 57 | *date:2024/12/25 11:36:48 58 | */ 59 | pub async fn query_sys_operate_log_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 60 | info!("query sys_operate_log_list params: {:?}", &item); 61 | let rb = &state.batis; 62 | 63 | let page = &PageRequest::new(item.page_no, item.page_size); 64 | OperateLog::select_page_by_name(rb, page, &item) 65 | .await 66 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 67 | } 68 | -------------------------------------------------------------------------------- /docs/test/system/sys_role.http: -------------------------------------------------------------------------------- 1 | ###添加角色信息 addRole 2 | POST {{host}}/api/system/role/addRole 3 | Content-Type: application/json 4 | Authorization: Bearer {{token}} 5 | 6 | { 7 | "roleName": "aa", 8 | "status": 1, 9 | "sort": 1, 10 | "remark": "aa" 11 | } 12 | 13 | ###删除角色信息 deleteRole 14 | POST {{host}}/api/system/role/deleteRole 15 | Content-Type: application/json 16 | Authorization: Bearer {{token}} 17 | 18 | { 19 | "ids": [ 20 | 5 21 | ] 22 | } 23 | 24 | ###更新角色信息 updateRole 25 | POST {{host}}/api/system/role/updateRole 26 | Content-Type: application/json 27 | Authorization: Bearer {{token}} 28 | 29 | { 30 | "id": 5, 31 | "roleName": "aa11", 32 | "status": 1, 33 | "sort": 1, 34 | "remark": "aa11" 35 | } 36 | 37 | ###更新角色信息 updateRoleStatus状态 38 | POST {{host}}/api/system/role/updateRoleStatus 39 | Content-Type: application/json 40 | Authorization: Bearer {{token}} 41 | 42 | { 43 | "ids": [ 44 | 4 45 | ], 46 | "status": 1 47 | } 48 | 49 | ###查询角色信息详情 queryRole 50 | POST {{host}}/api/system/role/queryRoleDetail 51 | Content-Type: application/json 52 | Authorization: Bearer {{token}} 53 | 54 | { 55 | "id": 1 56 | } 57 | 58 | 59 | ###查询角色信息列表 queryRoleList 60 | POST {{host}}/api/system/role/queryRoleList 61 | Content-Type: application/json 62 | Authorization: Bearer {{token}} 63 | 64 | { 65 | "pageNo": 1, 66 | "pageSize": 10, 67 | "roleName": "", 68 | "status": 1 69 | } 70 | 71 | ###查询角色菜单 queryRoleMenu 72 | POST {{host}}/api/system/role/queryRoleMenu 73 | Content-Type: application/json 74 | Authorization: Bearer {{token}} 75 | 76 | { 77 | "roleId": 3 78 | } 79 | 80 | ###更新角色菜单 updateRoleMenu 81 | POST {{host}}/api/system/role/updateRoleMenu 82 | Content-Type: application/json 83 | Authorization: Bearer {{token}} 84 | 85 | { 86 | "roleId": 3, 87 | "menuIds": [ 88 | 1, 89 | 2, 90 | 3 91 | ] 92 | } 93 | 94 | ###查询已分配用户角色列表 queryAllocatedList 95 | POST {{host}}/api/system/role/queryAllocatedList 96 | Content-Type: application/json 97 | Authorization: Bearer {{token}} 98 | 99 | { 100 | "pageNo": 1, 101 | "pageSize": 10, 102 | "roleId": 3 103 | } 104 | 105 | ###查询未分配用户角色列表 queryUnallocatedList 106 | POST {{host}}/api/system/role/queryUnallocatedList 107 | Content-Type: application/json 108 | Authorization: Bearer {{token}} 109 | 110 | { 111 | "pageNo": 1, 112 | "pageSize": 10, 113 | "roleId": 3 114 | } -------------------------------------------------------------------------------- /src/vo/system/sys_operate_log_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | /* 8 | 删除操作日志记录请求参数 9 | */ 10 | #[derive(Debug, Serialize, Deserialize)] 11 | pub struct DeleteOperateLogReq { 12 | pub ids: Vec, 13 | } 14 | 15 | /* 16 | 查询操作日志记录详情请求参数 17 | */ 18 | #[derive(Debug, Serialize, Deserialize)] 19 | pub struct QueryOperateLogDetailReq { 20 | pub id: i64, 21 | } 22 | 23 | /* 24 | 查询操作日志记录列表请求参数 25 | */ 26 | #[derive(Debug, Serialize, Deserialize)] 27 | #[serde(rename_all = "camelCase")] 28 | pub struct QueryOperateLogListReq { 29 | pub page_no: u64, 30 | pub page_size: u64, 31 | pub title: Option, //模块标题 32 | pub business_type: Option, //业务类型(0其它 1新增 2修改 3删除) 33 | pub method: Option, //方法名称 34 | pub request_method: Option, //请求方式 35 | pub operator_type: Option, //操作类别(0其它 1后台用户 2手机端用户) 36 | pub operate_name: Option, //操作人员 37 | pub dept_name: Option, //部门名称 38 | pub operate_url: Option, //请求URL 39 | pub operate_ip: Option, //主机地址 40 | pub operate_location: Option, //操作地点 41 | #[serde(default = "default_status")] 42 | pub status: Option, //操作状态(0:异常,正常) 43 | } 44 | fn default_status() -> Option { 45 | Some(2) 46 | } 47 | /* 48 | 查询操作日志记录列表响应参数 49 | */ 50 | #[derive(Debug, Serialize, Deserialize)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct OperateLogResp { 53 | pub id: Option, //日志主键 54 | pub title: Option, //模块标题 55 | pub business_type: Option, //业务类型(0其它 1新增 2修改 3删除) 56 | pub method: Option, //方法名称 57 | pub request_method: Option, //请求方式 58 | pub operator_type: Option, //操作类别(0其它 1后台用户 2手机端用户) 59 | pub operate_name: Option, //操作人员 60 | pub dept_name: Option, //部门名称 61 | pub operate_url: Option, //请求URL 62 | pub operate_ip: Option, //主机地址 63 | pub operate_location: Option, //操作地点 64 | pub operate_param: Option, //请求参数 65 | pub json_result: Option, //返回参数 66 | pub status: Option, //操作状态(0:异常,正常) 67 | pub error_msg: Option, //错误消息 68 | #[serde(serialize_with = "serialize_datetime")] 69 | pub operate_time: Option, //操作时间 70 | pub cost_time: Option, //消耗时间 71 | } 72 | -------------------------------------------------------------------------------- /src/vo/system/sys_dict_data_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除字典数据表请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeleteDictDataReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 更新字典数据表请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct DictDataReq { 22 | pub id: Option, //字典编码 23 | pub dict_sort: i32, //字典排序 24 | pub dict_label: String, //字典标签 25 | pub dict_value: String, //字典键值 26 | pub dict_type: String, //字典类型 27 | pub css_class: String, //样式属性(其他样式扩展) 28 | pub list_class: String, //表格回显样式 29 | pub is_default: String, //是否默认(Y是 N否) 30 | pub status: i8, //状态(0:停用,1:正常) 31 | pub remark: Option, //备注 32 | } 33 | 34 | /* 35 | 更新字典数据表状态请求参数 36 | */ 37 | #[derive(Debug, Serialize, Deserialize)] 38 | pub struct UpdateDictDataStatusReq { 39 | pub ids: Vec, 40 | pub status: i8, 41 | } 42 | 43 | /* 44 | 查询字典数据表详情请求参数 45 | */ 46 | #[derive(Debug, Serialize, Deserialize)] 47 | pub struct QueryDictDataDetailReq { 48 | pub id: i64, 49 | } 50 | 51 | /* 52 | 查询字典数据表列表请求参数 53 | */ 54 | #[derive(Debug, Serialize, Deserialize)] 55 | #[serde(rename_all = "camelCase")] 56 | pub struct QueryDictDataListReq { 57 | pub page_no: u64, 58 | pub page_size: u64, 59 | pub dict_label: Option, //字典标签 60 | pub dict_value: Option, //字典键值 61 | pub dict_type: Option, //字典类型 62 | #[serde(default = "default_status")] 63 | pub status: Option, //状态(0:停用,1:正常) 64 | } 65 | fn default_status() -> Option { 66 | Some(2) 67 | } 68 | /* 69 | 查询字典数据表列表响应参数 70 | */ 71 | #[derive(Debug, Serialize, Deserialize)] 72 | #[serde(rename_all = "camelCase")] 73 | pub struct DictDataResp { 74 | pub id: Option, //字典编码 75 | pub dict_sort: i32, //字典排序 76 | pub dict_label: String, //字典标签 77 | pub dict_value: String, //字典键值 78 | pub dict_type: String, //字典类型 79 | pub css_class: String, //样式属性(其他样式扩展) 80 | pub list_class: String, //表格回显样式 81 | pub is_default: String, //是否默认(Y是 N否) 82 | pub status: i8, //状态(0:停用,1:正常) 83 | pub remark: Option, //备注 84 | #[serde(serialize_with = "serialize_datetime")] 85 | pub create_time: Option, //创建时间 86 | #[serde(serialize_with = "serialize_datetime")] 87 | pub update_time: Option, //修改时间 88 | } 89 | -------------------------------------------------------------------------------- /docs/sql/system/sys_dept.sql: -------------------------------------------------------------------------------- 1 | create table sys_dept 2 | ( 3 | id bigint auto_increment comment '部门id' 4 | primary key, 5 | parent_id bigint(20) default 0 not null comment '父部门id', 6 | ancestors varchar(50) default '' not null comment '祖级列表', 7 | dept_name varchar(30) default '' not null comment '部门名称', 8 | sort int(4) default 0 not null comment '显示顺序', 9 | leader varchar(20) default '' not null comment '负责人', 10 | phone varchar(11) default '' not null comment '联系电话', 11 | email varchar(50) default '' not null comment '邮箱', 12 | status tinyint default 0 not null comment '部门状态(0:停用,1:正常)', 13 | del_flag tinyint default 1 not null comment '删除标志(0代表删除 1代表存在)', 14 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 15 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间' 16 | ) comment = '部门表'; 17 | 18 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (0, '0', '测试科技', 1, 'admin', '18613030352', '1002219331@qq.com', 1, 1); 19 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (1, '0,1', '深圳总公司', 1, '1', '1', 'xx@qq.com', 1, 1); 20 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (1, '0,1', '长沙分公司', 2, '1', '1', 'xx@qq.com', 1, 1); 21 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (2, '0,1,2', '研发部门', 1, '1', '1', 'xx@qq.com', 1, 1); 22 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (2, '0,1,2', '市场部门', 2, '1', '1', 'xx@qq.com', 1, 1); 23 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (2, '0,1,2', '测试部门', 3, '1', '1', 'xx@qq.com', 1, 1); 24 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (2, '0,1,2', '财务部门', 4, '1', '1', 'xx@qq.com', 1, 1); 25 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (2, '0,1,2', '运维部门', 5, '1', '1', 'xx@qq.com', 1, 1); 26 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (3, '0,1,3', '市场部门1', 6, '1', '1', 'xx@qq.com', 1, 1); 27 | INSERT INTO sys_dept (parent_id, ancestors, dept_name, sort, leader, phone, email, status, del_flag) VALUES (3, '0,1,3', '财务部门1', 1, '1', '1', 'xx@qq.com', 1, 1); 28 | -------------------------------------------------------------------------------- /src/utils/user_agent_util.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct UserAgentUtil { 5 | pub platform: String, //平台信息 6 | pub os: String, //操作系统信息 7 | pub arch: String, //体系结构信息 8 | pub engine: String, //渲染引擎信息 9 | pub engine_details: String, //渲染引擎详细信息 10 | pub browser: String, //浏览器名称 11 | pub version: String, //浏览器版本 12 | pub extra: String, //其他信息(可选) 13 | } 14 | 15 | impl UserAgentUtil { 16 | pub fn new(user_agent: &str) -> Self { 17 | let mut parse = UserAgentUtil { 18 | platform: "".to_string(), 19 | os: "".to_string(), 20 | arch: "".to_string(), 21 | engine: "".to_string(), 22 | engine_details: "".to_string(), 23 | browser: "".to_string(), 24 | version: "".to_string(), 25 | extra: "".to_string(), 26 | }; 27 | // 定义正则表达式来匹配并提取信息 28 | let re = Regex::new( 29 | r"(?x) 30 | Mozilla/5.0 \s* # Mozilla标识 31 | \((?P[^;]+); \s* # 平台信息 32 | (?P[^;]+); \s* # 操作系统信息 33 | (?P[^\)]+)\) \s* # 体系结构信息 34 | (?P[^\s]+) \s* # 渲染引擎信息 35 | \((?P[^\)]+)\) \s* # 渲染引擎详细信息 36 | (?P[^\s/]+)/ # 浏览器名称 37 | (?P[^\s]+) \s* # 浏览器版本 38 | (?P[^\s]+)? # 其他信息(可选) 39 | ", 40 | ) 41 | .unwrap(); 42 | 43 | // 使用正则表达式进行匹配 44 | if let Some(captures) = re.captures(user_agent) { 45 | let platform = captures.name("platform").unwrap().as_str(); 46 | let os = captures.name("os").unwrap().as_str(); 47 | let arch = captures.name("arch").unwrap().as_str(); 48 | let engine = captures.name("engine").unwrap().as_str(); 49 | let engine_details = captures.name("engine_details").unwrap().as_str(); 50 | let browser = captures.name("browser").unwrap().as_str(); 51 | let version = captures.name("version").unwrap().as_str(); 52 | let extra = captures.name("extra").map_or("", |m| m.as_str()); 53 | 54 | parse.platform = platform.to_string(); 55 | parse.os = os.to_string(); 56 | parse.arch = arch.to_string(); 57 | parse.engine = engine.to_string(); 58 | parse.engine_details = engine_details.to_string(); 59 | parse.browser = browser.to_string(); 60 | parse.version = version.to_string(); 61 | parse.extra = extra.to_string(); 62 | } 63 | 64 | parse 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/vo/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod other; 2 | pub mod system; 3 | 4 | // // 统一返回vo 5 | // #[derive(Serialize, Debug, Clone)] 6 | // pub struct BaseResponse 7 | // where T: Serialize + Debug 8 | // { 9 | // pub code: i32, 10 | // pub msg: String, 11 | // pub data: Option, 12 | // } 13 | // 14 | // // 处理统一返回 15 | // pub fn handle_result(result: Result) -> BaseResponse { 16 | // match result { 17 | // Ok(_u) => { 18 | // ok_result() 19 | // } 20 | // Err(err) => { 21 | // err_result_msg(err.to_string()) 22 | // } 23 | // } 24 | // } 25 | // 26 | // 27 | // pub fn ok_result() -> BaseResponse { 28 | // BaseResponse { 29 | // msg: "操作成功".to_string(), 30 | // code: 0, 31 | // data: Some("None".to_string()), 32 | // } 33 | // } 34 | // 35 | // pub fn ok_result_msg(msg: String) -> BaseResponse { 36 | // BaseResponse { 37 | // msg: msg.to_string(), 38 | // code: 0, 39 | // data: Some("None".to_string()), 40 | // } 41 | // } 42 | // 43 | // pub fn ok_result_code(code: i32, msg: String) -> BaseResponse { 44 | // BaseResponse { 45 | // msg: msg.to_string(), 46 | // code, 47 | // data: Some("None".to_string()), 48 | // } 49 | // } 50 | // 51 | // pub fn ok_result_data(data: T) -> BaseResponse { 52 | // BaseResponse { 53 | // msg: "操作成功".to_string(), 54 | // code: 0, 55 | // data: Some(data), 56 | // } 57 | // } 58 | // 59 | // pub fn err_result_msg(msg: String) -> BaseResponse { 60 | // BaseResponse { 61 | // msg: msg.to_string(), 62 | // code: 1, 63 | // data: Some("None".to_string()), 64 | // } 65 | // } 66 | // 67 | // pub fn err_result_code(code: i32, msg: String) -> BaseResponse { 68 | // BaseResponse { 69 | // msg: msg.to_string(), 70 | // code, 71 | // data: Some("None".to_string()), 72 | // } 73 | // } 74 | // 75 | // // 统一返回分页 76 | // #[derive(Serialize, Debug, Clone)] 77 | // pub struct ResponsePage 78 | // where T: Serialize + Debug 79 | // { 80 | // pub code: i32, 81 | // pub msg: String, 82 | // pub total: u64, 83 | // pub success: bool, 84 | // pub data: Option, 85 | // } 86 | // 87 | // pub fn ok_result_page(data: T, total: u64) -> ResponsePage { 88 | // ResponsePage { 89 | // msg: "操作成功".to_string(), 90 | // code: 0, 91 | // success: true, 92 | // data: Some(data), 93 | // total, 94 | // } 95 | // } 96 | // 97 | // pub fn err_result_page(data: T, msg: String) -> ResponsePage { 98 | // ResponsePage { 99 | // msg: msg.to_string(), 100 | // code: 1, 101 | // success: false, 102 | // data: Some(data), 103 | // total: 0, 104 | // } 105 | // } 106 | -------------------------------------------------------------------------------- /src/vo/system/sys_dept_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/25 10:01:11 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use regex::Regex; 7 | use serde::{Deserialize, Serialize}; 8 | use std::sync::LazyLock; 9 | use validator::Validate; 10 | /* 11 | 删除部门表请求参数 12 | */ 13 | #[derive(Debug, Serialize, Deserialize)] 14 | pub struct DeleteDeptReq { 15 | pub id: i64, 16 | } 17 | 18 | static PHONE_NUMBER: LazyLock = LazyLock::new(|| Regex::new(r"\d{11}$").unwrap()); 19 | 20 | /* 21 | 更新部门表请求参数 22 | */ 23 | #[derive(Debug, Serialize, Deserialize, Validate)] 24 | #[serde(rename_all = "camelCase")] 25 | pub struct DeptReq { 26 | pub id: Option, //部门id 27 | pub parent_id: i64, //父部门id 28 | #[validate(length(min = 2, max = 30, message = "部门名称不能为空且长度为2-30"))] 29 | pub dept_name: String, //部门名称 30 | pub ancestors: Option, //祖级列表 31 | pub sort: i32, //显示顺序 32 | #[validate(length(min = 2, max = 20, message = "负责人必填且长度为2-20"))] 33 | pub leader: String, //负责人 34 | #[validate(regex(path = *PHONE_NUMBER, message = "联系电话格式不正确,必须为11位数字"))] 35 | pub phone: String, //联系电话 36 | #[validate(email, length(max = 50, message = "邮箱格式不正确且长度不能超过50"))] 37 | pub email: String, //邮箱 38 | pub status: i8, //部状态(0:停用,1:正常) 39 | } 40 | 41 | /* 42 | 更新部门表状态请求参数 43 | */ 44 | #[derive(Debug, Serialize, Deserialize)] 45 | pub struct UpdateDeptStatusReq { 46 | pub id: i64, 47 | pub status: i8, 48 | } 49 | 50 | /* 51 | 查询部门表详情请求参数 52 | */ 53 | #[derive(Debug, Serialize, Deserialize)] 54 | pub struct QueryDeptDetailReq { 55 | pub id: i64, 56 | } 57 | 58 | /* 59 | 查询部门表列表请求参数 60 | */ 61 | #[derive(Debug, Serialize, Deserialize)] 62 | #[serde(rename_all = "camelCase")] 63 | pub struct QueryDeptListReq { 64 | pub dept_name: Option, //部门名称 65 | pub leader: Option, //负责人 66 | pub phone: Option, //联系电话 67 | pub email: Option, //邮箱 68 | #[serde(default = "default_status")] 69 | pub status: Option, //部状态(0:停用,1:正常) 70 | } 71 | fn default_status() -> Option { 72 | Some(2) 73 | } 74 | /* 75 | 查询部门表列表响应参数 76 | */ 77 | #[derive(Debug, Serialize, Deserialize)] 78 | #[serde(rename_all = "camelCase")] 79 | pub struct DeptResp { 80 | pub id: Option, //部门id 81 | pub key: String, //部门id(angular tree使用) 82 | pub parent_id: i64, //父部门id 83 | pub ancestors: Option, //祖级列表 84 | pub dept_name: String, //部门名称 85 | pub title: String, //部门名称(angular treeSelect使用) 86 | pub sort: i32, //显示顺序 87 | pub leader: String, //负责人 88 | pub phone: String, //联系电话 89 | pub email: String, //邮箱 90 | pub status: i8, //部状态(0:停用,1:正常) 91 | #[serde(serialize_with = "serialize_datetime")] 92 | pub create_time: Option, //创建时间 93 | #[serde(serialize_with = "serialize_datetime")] 94 | pub update_time: Option, //修改时间 95 | } 96 | -------------------------------------------------------------------------------- /src/common/error.rs: -------------------------------------------------------------------------------- 1 | use crate::common::result::BaseResponse; 2 | use axum::http::StatusCode; 3 | use axum::response::{IntoResponse, Response}; 4 | use axum::Json; 5 | use redis::RedisError; 6 | use thiserror::Error; 7 | 8 | #[derive(Error, Debug)] 9 | pub enum AppError { 10 | // #[error("Failed to complete an HTTP request")] 11 | // Http { #[from] source: reqwest::Error }, 12 | // 13 | #[error("Failed to read the cache file")] 14 | DiskCacheRead { source: std::io::Error }, 15 | // 16 | // #[error("Failed to update the cache file")] 17 | // DiskCacheWrite { source: std::io::Error }, 18 | #[error("jwt:{0}")] 19 | JwtTokenError(String), 20 | 21 | #[error("数据库错误: {0}")] 22 | DbError(#[from] rbatis::Error), 23 | 24 | #[error("redis错误: {0}")] 25 | RedisError(#[from] RedisError), 26 | 27 | #[error("业务异常: {0}")] 28 | BusinessError(&'static str), 29 | 30 | #[error("验证异常: {0}")] 31 | ValidationError(String), 32 | 33 | #[error("内部异常: {0}")] 34 | InternalError(&'static str), 35 | } 36 | pub type AppResult = Result; 37 | 38 | #[async_trait] 39 | impl IntoResponse for AppError { 40 | fn into_response(self) -> Response { 41 | let response = BaseResponse { 42 | msg: self.to_string(), 43 | code: 1, 44 | data: Some("None".to_string()), 45 | }; 46 | 47 | match self { 48 | AppError::DbError(_) | AppError::RedisError(_) => { 49 | (StatusCode::INTERNAL_SERVER_ERROR, Json(response)).into_response() 50 | }, 51 | AppError::DiskCacheRead { source:_ } => { 52 | (StatusCode::INTERNAL_SERVER_ERROR, Json(response)).into_response() 53 | }, 54 | AppError::JwtTokenError (_msg) => { 55 | (StatusCode::UNAUTHORIZED, Json(response)).into_response() 56 | }, 57 | AppError::BusinessError(_msg) => { 58 | (StatusCode::INTERNAL_SERVER_ERROR, Json(response)).into_response() 59 | }, 60 | AppError::ValidationError(_msg) => { 61 | (StatusCode::INTERNAL_SERVER_ERROR, Json(response)).into_response() 62 | }, 63 | AppError::InternalError(_msg) => { 64 | (StatusCode::FORBIDDEN, Json(response)).into_response() 65 | }, 66 | } 67 | } 68 | } 69 | 70 | 71 | impl AppError { 72 | pub fn default() -> Result<(), AppError> { 73 | Err(AppError::InternalError("服务器发生内部异常,请稍后再试")) 74 | } 75 | 76 | pub fn build_validation_error_message(e: &validator::ValidationErrors) -> String { 77 | e.field_errors().iter().map(|(field, errors)| { 78 | let messages: Vec = errors.iter().map(|error| { 79 | if let Some(message) = &error.message { 80 | message.to_string() 81 | } else { 82 | format!("字段 '{}' 验证失败", field) 83 | } 84 | }).collect(); 85 | messages.join(", ") 86 | }).collect::>().join("; ") 87 | } 88 | 89 | pub fn validation_error(e: &validator::ValidationErrors) -> AppError { 90 | AppError::ValidationError(Self::build_validation_error_message(e)) 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /src/model/system/sys_notice_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_notice_vo::{NoticeReq, NoticeResp, QueryNoticeListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use serde::{Deserialize, Serialize}; 8 | /* 9 | *通知公告表 10 | *author:刘飞华 11 | *date:2024/12/25 10:01:11 12 | */ 13 | #[derive(Clone, Debug, Serialize, Deserialize)] 14 | pub struct Notice { 15 | pub id: Option, //公告ID 16 | pub notice_title: String, //公告标题 17 | pub notice_type: i8, //公告类型(1:通知,2:公告) 18 | pub notice_content: String, //公告内容 19 | pub status: i8, //公告状态(0:关闭,1:正常 ) 20 | pub remark: Option, //备注 21 | pub create_time: Option, //创建时间 22 | pub update_time: Option, //修改时间 23 | } 24 | 25 | /* 26 | *通知公告表基本操作 27 | *author:刘飞华 28 | *date:2024/12/25 10:01:11 29 | */ 30 | rbatis::crud!(Notice {}, "sys_notice"); 31 | 32 | impl From for Notice { 33 | fn from(item: NoticeReq) -> Self { 34 | let mut model = Notice { 35 | id: item.id, //公告ID 36 | notice_title: item.notice_title, //公告标题 37 | notice_type: item.notice_type, //公告类型(1:通知,2:公告) 38 | notice_content: item.notice_content, //公告内容 39 | status: item.status, //公告状态(0:关闭,1:正常 ) 40 | remark: item.remark, //备注 41 | create_time: None, //创建时间 42 | update_time: None, //修改时间 43 | }; 44 | if let None = item.id { 45 | model.create_time = Some(DateTime::now()); 46 | } else { 47 | model.update_time = Some(DateTime::now()); 48 | } 49 | model 50 | } 51 | } 52 | 53 | impl Into for Notice { 54 | fn into(self) -> NoticeResp { 55 | NoticeResp { 56 | id: self.id, //公告ID 57 | notice_title: self.notice_title, //公告标题 58 | notice_type: self.notice_type, //公告类型(1:通知,2:公告) 59 | notice_content: self.notice_content, //公告内容 60 | status: self.status, //公告状态(0:关闭,1:正常 ) 61 | remark: self.remark, //备注 62 | create_time: self.create_time, //创建时间 63 | update_time: self.update_time, //修改时间 64 | } 65 | } 66 | } 67 | /* 68 | *根据id查询通知公告表 69 | *author:刘飞华 70 | *date:2024/12/25 10:01:11 71 | */ 72 | impl_select!(Notice{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_notice"); 73 | 74 | /* 75 | *根据公告标题查询通知公告表 76 | *author:刘飞华 77 | *date:2024/12/25 10:01:11 78 | */ 79 | impl_select!(Notice{select_by_title(title:&str) -> Option => "`where notice_title = #{title} limit 1`"}, "sys_notice"); 80 | 81 | /* 82 | *根据条件分页查询通知公告表 83 | *author:刘飞华 84 | *date:2024/12/25 10:01:11 85 | */ 86 | impl_select_page!(Notice{select_sys_notice_list(req:&QueryNoticeListReq) =>" 87 | where 1=1 88 | if req.noticeTitle != '' && req.noticeTitle != null: 89 | ` and notice_title like concat('%', #{req.noticeTitle}, '%') ` 90 | if req.noticeType != 0: 91 | ` and notice_type = #{req.noticeType} ` 92 | if req.status != 2: 93 | ` and status = #{req.status} ` 94 | if !sql.contains('count'): 95 | ` order by create_time desc `" 96 | },"sys_notice"); 97 | -------------------------------------------------------------------------------- /src/vo/system/sys_menu_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use crate::common::result::serialize_datetime; 5 | use crate::model::system::sys_menu_model::Menu; 6 | use rbatis::rbdc::DateTime; 7 | use serde::{Deserialize, Serialize}; 8 | /* 9 | 删除菜单信息请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize, Clone)] 12 | pub struct DeleteMenuReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 更新菜单信息请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct MenuReq { 22 | pub id: Option, //主键 23 | pub menu_name: String, //菜单名称 24 | pub menu_type: i8, //菜单类型(1:目录 2:菜单 3:按钮) 25 | pub visible: i8, //菜单状态(0:隐藏, 显示:1) 26 | pub status: i8, //状态(1:正常,0:禁用) 27 | pub sort: i32, //排序 28 | pub parent_id: Option, //父ID 29 | pub menu_url: Option, //路由路径 30 | pub api_url: Option, //接口URL 31 | pub menu_icon: Option, //菜单图标 32 | pub remark: Option, //备注 33 | } 34 | 35 | /* 36 | 更新菜单信息状态请求参数 37 | */ 38 | #[derive(Debug, Serialize, Deserialize)] 39 | pub struct UpdateMenuStatusReq { 40 | pub ids: Vec, 41 | pub status: i8, 42 | } 43 | 44 | /* 45 | 查询菜单信息详情请求参数 46 | */ 47 | #[derive(Debug, Serialize, Deserialize)] 48 | pub struct QueryMenuDetailReq { 49 | pub id: i64, 50 | } 51 | 52 | /* 53 | 查询菜单信息列表请求参数 54 | */ 55 | #[derive(Debug, Serialize, Deserialize)] 56 | #[serde(rename_all = "camelCase")] 57 | pub struct QueryMenuListReq { 58 | pub page_no: u64, 59 | pub page_size: u64, 60 | pub menu_name: Option, //菜单名称 61 | pub menu_type: Option, //菜单类型(1:目录 2:菜单 3:按钮) 62 | pub visible: Option, //显示状态(0:隐藏, 显示:1) 63 | pub status: Option, //菜单状态(1:正常,0:禁用) 64 | pub parent_id: Option, //父ID 65 | pub menu_url: Option, //路由路径 66 | pub api_url: Option, //接口URL 67 | } 68 | 69 | /* 70 | 查询菜单信息列表响应参数 71 | */ 72 | #[derive(Debug, Serialize, Deserialize)] 73 | #[serde(rename_all = "camelCase")] 74 | pub struct MenuResp { 75 | pub id: Option, //主键 76 | pub menu_name: String, //菜单名称 77 | pub menu_type: i8, //菜单类型(1:目录 2:菜单 3:按钮) 78 | pub visible: i8, //菜单状态(0:隐藏, 显示:1) 79 | pub status: i8, //状态(1:正常,0:禁用) 80 | pub sort: i32, //排序 81 | pub parent_id: Option, //父ID 82 | pub menu_url: Option, //路由路径 83 | pub api_url: Option, //接口URL 84 | pub menu_icon: Option, //菜单图标 85 | pub remark: Option, //备注 86 | #[serde(serialize_with = "serialize_datetime")] 87 | pub create_time: Option, //创建时间 88 | #[serde(serialize_with = "serialize_datetime")] 89 | pub update_time: Option, //修改时间 90 | } 91 | 92 | /* 93 | 查询菜单信息列表响应参数 94 | */ 95 | #[derive(Debug, Serialize, Deserialize)] 96 | #[serde(rename_all = "camelCase")] 97 | pub struct MenuSimpleResp { 98 | pub id: Option, //主键 99 | pub menu_name: String, //菜单名称 100 | pub parent_id: Option, //父ID 101 | } 102 | impl From for MenuSimpleResp { 103 | fn from(item: Menu) -> Self { 104 | MenuSimpleResp { 105 | id: item.id, //主键 106 | menu_name: item.menu_name, //菜单名称 107 | parent_id: item.parent_id, //父ID 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/model/system/sys_dict_type_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_dict_type_vo::{DictTypeReq, DictTypeResp, QueryDictTypeListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use serde::{Deserialize, Serialize}; 8 | /* 9 | *字典类型 10 | *author:刘飞华 11 | *date:2024/12/25 10:01:11 12 | */ 13 | #[derive(Clone, Debug, Serialize, Deserialize)] 14 | pub struct DictType { 15 | pub id: Option, //字典主键 16 | pub dict_name: String, //字典名称 17 | pub dict_type: String, //字典类型 18 | pub status: i8, //状态(0:停用,1:正常) 19 | pub remark: Option, //备注 20 | pub create_time: Option, //创建时间 21 | pub update_time: Option, //修改时间 22 | } 23 | 24 | /* 25 | *字典类型基本操作 26 | *author:刘飞华 27 | *date:2024/12/25 10:01:11 28 | */ 29 | rbatis::crud!(DictType {}, "sys_dict_type"); 30 | 31 | impl From for DictType { 32 | fn from(item: DictTypeReq) -> Self { 33 | let mut model = DictType { 34 | id: item.id, //字典主键 35 | dict_name: item.dict_name, //字典名称 36 | dict_type: item.dict_type, //字典类型 37 | status: item.status, //状态(0:停用,1:正常) 38 | remark: item.remark, //备注 39 | create_time: None, //创建时间 40 | update_time: None, //修改时间 41 | }; 42 | if let None = item.id { 43 | model.create_time = Some(DateTime::now()); 44 | } else { 45 | model.update_time = Some(DateTime::now()); 46 | } 47 | model 48 | } 49 | } 50 | 51 | impl Into for DictType { 52 | fn into(self) -> DictTypeResp { 53 | DictTypeResp { 54 | id: self.id, //字典主键 55 | dict_name: self.dict_name, //字典名称 56 | dict_type: self.dict_type, //字典类型 57 | status: self.status, //状态(0:停用,1:正常) 58 | remark: self.remark, //备注 59 | create_time: self.create_time, //创建时间 60 | update_time: self.update_time, //修改时间 61 | } 62 | } 63 | } 64 | 65 | /* 66 | *根据id查询字典类型 67 | *author:刘飞华 68 | *date:2024/12/25 10:01:11 69 | */ 70 | impl_select!(DictType{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_dict_type"); 71 | 72 | /* 73 | *根据dict_type查询字典类型 74 | *author:刘飞华 75 | *date:2024/12/25 10:01:11 76 | */ 77 | impl_select!(DictType{select_by_dict_type(dict_type:&str) -> Option => "`where dict_type = #{dict_type} limit 1`"}, "sys_dict_type"); 78 | 79 | /* 80 | *分页查询字典类型 81 | *author:刘飞华 82 | *date:2024/12/25 10:01:11 83 | */ 84 | impl_select_page!(DictType{select_page() =>" 85 | if !sql.contains('count'): 86 | order by create_time desc" 87 | },"sys_dict_type"); 88 | 89 | /* 90 | *根据条件分页查询字典类型 91 | *author:刘飞华 92 | *date:2024/12/25 10:01:11 93 | */ 94 | impl_select_page!(DictType{select_dict_type_list(req:&QueryDictTypeListReq) =>" 95 | where 1=1 96 | if req.dictName != null && req.dictName != '': 97 | ` and dict_name like concat('%', #{req.dictName}, '%') ` 98 | if req.dictType != null && req.dictType != '': 99 | ` and dict_type like concat('%', #{req.dictType}, '%') ` 100 | if req.status != 2: 101 | ` and status = #{req.status} ` 102 | if !sql.contains('count'): 103 | ` order by create_time desc" 104 | },"sys_dict_type"); 105 | -------------------------------------------------------------------------------- /src/utils/jwt_util.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::error::AppError::JwtTokenError; 3 | use jsonwebtoken::{decode, encode, errors::ErrorKind, Algorithm, DecodingKey, EncodingKey, Header, Validation}; 4 | use serde::{Deserialize, Serialize}; 5 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 6 | 7 | #[derive(Debug, Serialize, Deserialize, Clone)] 8 | pub struct JwtToken { 9 | pub id: i64, 10 | pub username: String, 11 | aud: String, 12 | // (audience):受众 13 | exp: usize, 14 | iat: usize, 15 | // (Issued At):签发时间 16 | iss: String, 17 | // (issuer):签发人 18 | nbf: usize, 19 | // (Not Before):生效时间 20 | sub: String, 21 | // (subject):主题 22 | jti: String, // (JWT ID):编号 23 | } 24 | 25 | impl JwtToken { 26 | pub fn new(id: i64, username: &str) -> JwtToken { 27 | let now = SystemTime::now(); 28 | //过期时间 29 | let m30 = Duration::from_secs(1800000); 30 | let now = now.duration_since(UNIX_EPOCH).expect("获取系统时间失败"); 31 | 32 | JwtToken { 33 | id, 34 | username: String::from(username), 35 | aud: String::from("rust_admin"), // (audience):受众 36 | exp: (now + m30).as_secs() as usize, 37 | iat: now.as_secs() as usize, // (Issued At):签发时间 38 | iss: String::from("koobe"), // (issuer):签发人 39 | nbf: now.as_secs() as usize, // (Not Before):生效时间 40 | sub: String::from("rust_admin"), // (subject):主题 41 | jti: String::from("ignore"), // (JWT ID):编号 42 | } 43 | } 44 | 45 | /// create token 46 | /// secret: your secret string 47 | pub fn create_token(&self, secret: &str) -> Result { 48 | match encode(&Header::default(), self, &EncodingKey::from_secret(secret.as_ref())) { 49 | Ok(t) => Ok(t), 50 | Err(_) => Err(JwtTokenError("create token error".to_string())), 51 | } 52 | } 53 | /// verify token invalid 54 | /// secret: your secret string 55 | pub fn verify(secret: &str, token: &str) -> Result { 56 | let mut validation = Validation::new(Algorithm::HS256); 57 | validation.sub = Some("rust_admin".to_string()); 58 | validation.set_audience(&["rust_admin"]); 59 | validation.set_required_spec_claims(&["exp", "sub", "aud"]); 60 | match decode::(&token, &DecodingKey::from_secret(secret.as_ref()), &validation) { 61 | Ok(c) => Ok(c.claims), 62 | 63 | Err(err) => match *err.kind() { 64 | ErrorKind::InvalidToken => return Err(JwtTokenError("InvalidToken".to_string())), // Example on how to handle a specific error 65 | ErrorKind::InvalidIssuer => return Err(JwtTokenError("InvalidIssuer".to_string())), // Example on how to handle a specific error 66 | ErrorKind::ExpiredSignature => return Err(JwtTokenError("token 已经超时了".to_string())), // Example on how to handle a specific error 67 | // _ => return Err(Error::from("InvalidToken other errors")), 68 | _ => Err(JwtTokenError("InvalidToken other errors".to_string())), 69 | }, 70 | } 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use crate::utils::jwt_util::JwtToken; 77 | 78 | #[test] 79 | fn test_jwt() { 80 | let jwt = JwtToken::new(1, "koobe"); 81 | let res = jwt.create_token("123").unwrap_or_default(); 82 | println!("{:?}", res); 83 | let token = JwtToken::verify("123", &res); 84 | println!("{:?}", token) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate rbatis; 3 | 4 | pub mod common; 5 | pub mod handler; 6 | pub mod middleware; 7 | pub mod model; 8 | pub mod route; 9 | pub mod utils; 10 | pub mod vo; 11 | 12 | use axum::{middleware as md, Router}; 13 | 14 | use crate::route::system::sys_dept_route::build_sys_dept_route; 15 | use crate::route::system::sys_dict_data_route::build_sys_dict_data_route; 16 | use crate::route::system::sys_dict_type_route::build_sys_dict_type_route; 17 | use crate::route::system::sys_login_log_route::build_sys_login_log_route; 18 | use crate::route::system::sys_notice_route::build_sys_notice_route; 19 | use crate::route::system::sys_operate_log_route::build_sys_operate_log_route; 20 | use crate::route::system::sys_post_route::build_sys_post_route; 21 | use crate::utils::redis_util::init_redis; 22 | use config::{Config, File}; 23 | use middleware::auth::auth; 24 | use rbatis::RBatis; 25 | use redis::Client; 26 | use route::system::sys_menu_route::build_sys_menu_route; 27 | use route::system::sys_role_route::build_sys_role_route; 28 | use route::system::sys_user_route::build_sys_user_route; 29 | use serde::Deserialize; 30 | use std::sync::Arc; 31 | use utils::db::init_db; 32 | 33 | // 定义应用状态结构体,包含数据库连接池 34 | pub struct AppState { 35 | pub batis: RBatis, 36 | pub redis: Client, 37 | } 38 | 39 | // 配置结构体,包含服务器和数据库配置 40 | #[derive(Debug, Deserialize)] 41 | struct Config1 { 42 | server: ServerConfig, 43 | db: DbConfig, 44 | redis: RedisConfig, 45 | } 46 | 47 | // 服务器配置结构体,包含服务器地址 48 | #[derive(Debug, Deserialize)] 49 | struct ServerConfig { 50 | addr: String, 51 | } 52 | 53 | // 数据库配置结构体,包含数据库URL 54 | #[derive(Debug, Deserialize)] 55 | struct DbConfig { 56 | url: String, 57 | } 58 | 59 | #[derive(Debug, Deserialize)] 60 | struct RedisConfig { 61 | url: String, 62 | } 63 | // 主函数,使用tokio异步运行时 64 | #[tokio::main] 65 | async fn main() { 66 | // 初始化日志配置 67 | log4rs::init_file("src/config/log4rs.yaml", Default::default()).unwrap(); 68 | 69 | // 加载和解析配置文件 70 | let config = Config::builder().add_source(File::with_name("config.toml")).build().unwrap().try_deserialize::().unwrap(); 71 | println!("Config: {:?}", config); 72 | 73 | // 初始化数据库连接 74 | let rb = init_db(config.db.url.as_str()).await; 75 | let rd = init_redis(config.redis.url.as_str()).await; 76 | 77 | // 创建共享应用状态,包含数据库连接池 78 | let shared_state = Arc::new(AppState { batis: rb, redis: rd }); 79 | 80 | // 构建应用路由,并合并多个子路由 81 | let app = Router::new().nest( 82 | "/api", 83 | Router::new() 84 | .merge(build_sys_user_route()) 85 | .merge(build_sys_role_route()) 86 | .merge(build_sys_menu_route()) 87 | .merge(build_sys_dept_route()) 88 | .merge(build_sys_dict_type_route()) 89 | .merge(build_sys_dict_data_route()) 90 | .merge(build_sys_post_route()) 91 | .merge(build_sys_login_log_route()) 92 | .merge(build_sys_operate_log_route()) 93 | .merge(build_sys_notice_route()) 94 | .route_layer(md::from_fn_with_state(Arc::clone(&shared_state), auth)) // 添加认证中间件 95 | .with_state(shared_state), // 设置共享状态 96 | ); 97 | 98 | // 以下代码适用于axum 0.6.x版本 99 | // 定义服务器监听地址 100 | // let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); 101 | // log::info!("listening on {}", addr); 102 | // 使用绑定地址启动服务器 103 | // axum::Server::bind(&addr) 104 | // .serve(app.into_make_service()) 105 | // .await 106 | // .unwrap(); 107 | 108 | // 以下代码适用于axum 0.7.x版本 109 | // 创建TCP监听器 110 | let listener = tokio::net::TcpListener::bind(config.server.addr).await.unwrap(); 111 | // 使用监听器启动服务器 112 | axum::serve(listener, app).await.unwrap(); 113 | } 114 | -------------------------------------------------------------------------------- /src/model/system/sys_post_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_post_vo::{PostReq, PostResp, QueryPostListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use serde::{Deserialize, Serialize}; 8 | /* 9 | *岗位信息 10 | *author:刘飞华 11 | *date:2024/12/25 10:01:11 12 | */ 13 | #[derive(Clone, Debug, Serialize, Deserialize)] 14 | pub struct Post { 15 | pub id: Option, //岗位id 16 | pub post_code: String, //岗位编码 17 | pub post_name: String, //岗位名称 18 | pub sort: i32, //显示顺序 19 | pub status: i8, //部状态(0:停用,1:正常) 20 | pub remark: Option, //备注 21 | pub create_time: Option, //创建时间 22 | pub update_time: Option, //更新时间 23 | } 24 | 25 | /* 26 | *岗位信息基本操作 27 | *author:刘飞华 28 | *date:2024/12/25 10:01:11 29 | */ 30 | rbatis::crud!(Post {}, "sys_post"); 31 | 32 | impl From for Post { 33 | fn from(item: PostReq) -> Self { 34 | let mut model = Post { 35 | id: item.id, //岗位id 36 | post_code: item.post_code, //岗位编码 37 | post_name: item.post_name, //岗位名称 38 | sort: item.sort, //显示顺序 39 | status: item.status, //部状态(0:停用,1:正常) 40 | remark: item.remark, //备注 41 | create_time: None, //创建时间 42 | update_time: None, //更新时间 43 | }; 44 | if let None = item.id { 45 | model.create_time = Some(DateTime::now()); 46 | } else { 47 | model.update_time = Some(DateTime::now()); 48 | } 49 | model 50 | } 51 | } 52 | 53 | impl Into for Post { 54 | fn into(self) -> PostResp { 55 | PostResp { 56 | id: self.id, //岗位id 57 | post_code: self.post_code, //岗位编码 58 | post_name: self.post_name, //岗位名称 59 | sort: self.sort, //显示顺序 60 | status: self.status, //部状态(0:停用,1:正常) 61 | remark: self.remark, //备注 62 | create_time: self.create_time, //创建时间 63 | update_time: self.update_time, //更新时间 64 | } 65 | } 66 | } 67 | 68 | /* 69 | *根据id查询岗位信息 70 | *author:刘飞华 71 | *date:2024/12/25 10:01:11 72 | */ 73 | impl_select!(Post{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_post"); 74 | 75 | /* 76 | *根据post_code查询岗位信息 77 | *author:刘飞华 78 | *date:2024/12/25 10:01:11 79 | */ 80 | impl_select!(Post{select_by_code(post_code:&str) -> Option => "`where post_code = #{post_code} limit 1`"}, "sys_post"); 81 | 82 | /* 83 | *根据post_name查询岗位信息 84 | *author:刘飞华 85 | *date:2024/12/25 10:01:11 86 | */ 87 | impl_select!(Post{select_by_name(post_name:&str) -> Option => "`where post_name = #{post_name} limit 1`"}, "sys_post"); 88 | 89 | /* 90 | *分页查询岗位信息 91 | *author:刘飞华 92 | *date:2024/12/25 10:01:11 93 | */ 94 | impl_select_page!(Post{select_page() =>" 95 | if !sql.contains('count'): 96 | order by create_time desc" 97 | },"sys_post"); 98 | 99 | /* 100 | *根据条件分页查询岗位信息 101 | *author:刘飞华 102 | *date:2024/12/25 10:01:11 103 | */ 104 | impl_select_page!(Post{select_post_list(req:&QueryPostListReq) =>" 105 | where 1=1 106 | if req.postCode != null && req.postCode != '': 107 | ` and post_code like concat('%', #{req.postCode}, '%') ` 108 | if req.postName != null &&req. postName != '': 109 | ` and post_name like concat('%', #{req.postName}, '%') ` 110 | if req.status != 2: 111 | ` and status = #{req.status} ` 112 | if !sql.contains('count'): 113 | ` order by create_time desc" 114 | },"sys_post"); 115 | -------------------------------------------------------------------------------- /src/model/system/sys_login_log_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_login_log_vo::{LoginLogResp, QueryLoginLogListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use rbatis::RBatis; 8 | use serde::{Deserialize, Serialize}; 9 | /* 10 | *系统访问记录 11 | *author:刘飞华 12 | *date:2024/12/25 10:01:11 13 | */ 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct LoginLog { 16 | pub id: Option, //访问ID 17 | pub login_name: String, //登录账号 18 | pub ipaddr: String, //登录IP地址 19 | pub login_location: String, //登录地点 20 | pub platform: String, //平台信息 21 | pub browser: String, //浏览器类型 22 | pub version: String, //浏览器版本 23 | pub os: String, //操作系统 24 | pub arch: String, //体系结构信息 25 | pub engine: String, //渲染引擎信息 26 | pub engine_details: String, //渲染引擎详细信息 27 | pub extra: String, //其他信息(可选) 28 | pub status: i8, //登录状态(0:失败,1:成功) 29 | pub msg: String, //提示消息 30 | pub login_time: Option, //访问时间 31 | } 32 | 33 | /* 34 | *系统访问记录基本操作 35 | *author:刘飞华 36 | *date:2024/12/25 10:01:11 37 | */ 38 | rbatis::crud!(LoginLog {}, "sys_login_log"); 39 | 40 | impl Into for LoginLog { 41 | fn into(self) -> LoginLogResp { 42 | LoginLogResp { 43 | id: self.id, //访问ID 44 | login_name: self.login_name, //登录账号 45 | ipaddr: self.ipaddr, //登录IP地址 46 | login_location: self.login_location, //登录地点 47 | platform: self.platform, //平台信息 48 | browser: self.browser, //浏览器类型 49 | version: self.version, //浏览器版本 50 | os: self.os, //操作系统 51 | arch: self.arch, //体系结构信息 52 | engine: self.engine, //渲染引擎信息 53 | engine_details: self.engine_details, //渲染引擎详细信息 54 | extra: self.extra, //其他信息(可选) 55 | status: self.status, //登录状态(0:失败,1:成功) 56 | msg: self.msg, //提示消息 57 | login_time: self.login_time, //访问时间 58 | } 59 | } 60 | } 61 | 62 | /* 63 | *根据id查询系统访问记录 64 | *author:刘飞华 65 | *date:2024/12/25 10:01:11 66 | */ 67 | impl_select!(LoginLog{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_login_log"); 68 | 69 | /* 70 | *分页查询系统访问记录 71 | *author:刘飞华 72 | *date:2024/12/25 10:01:11 73 | */ 74 | impl_select_page!(LoginLog{select_page() =>" 75 | if !sql.contains('count'): 76 | order by login_time desc" 77 | },"sys_login_log"); 78 | 79 | /* 80 | *根据条件分页查询系统访问记录 81 | *author:刘飞华 82 | *date:2024/12/25 10:01:11 83 | */ 84 | impl_select_page!(LoginLog{select_login_log_list(req:&QueryLoginLogListReq) =>" 85 | where 1=1 86 | if req.loginName != '' && req.loginName != null: 87 | ` and login_name like concat('%', #{req.loginName}, '%') ` 88 | if req.ipaddr != '' && req.ipaddr != null: 89 | ` and ipaddr like concat('%', #{req.ipaddr}, '%') ` 90 | if req.browser != '' && req.browser != null: 91 | ` and browser like concat('%', #{req.browser}, '%') ` 92 | if req.os != '' && req.os != null: 93 | ` and os like concat('%', #{req.os}, '%') ` 94 | if req.status != 2: 95 | ` and status = #{req.status} ` 96 | if !sql.contains('count'): 97 | ` order by login_time desc `" 98 | },"sys_login_log"); 99 | 100 | /* 101 | *清空系统登录日志 102 | *author:刘飞华 103 | *date:2024/12/12 14:41:44 104 | */ 105 | #[sql("truncate table sys_login_log")] 106 | pub async fn clean_login_log(rb: &RBatis) -> Option { 107 | impled!() 108 | } 109 | -------------------------------------------------------------------------------- /src/model/system/sys_role_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use crate::vo::system::sys_role_vo::{QueryRoleListReq, RoleReq, RoleResp}; 5 | use rbatis::rbdc::datetime::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | /* 8 | *角色信息 9 | *author:刘飞华 10 | *date:2024/12/12 14:41:44 11 | */ 12 | #[derive(Clone, Debug, Serialize, Deserialize)] 13 | pub struct Role { 14 | pub id: Option, //主键 15 | pub role_name: String, //名称 16 | pub role_key: String, //角色权限字符串 17 | pub data_scope: i8, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 18 | pub status: i8, //状态(1:正常,0:禁用) 19 | pub remark: Option, //备注 20 | pub del_flag: Option, //删除标志(0代表删除 1代表存在) 21 | pub create_time: Option, //创建时间 22 | pub update_time: Option, //修改时间 23 | } 24 | 25 | /* 26 | *角色信息基本操作 27 | *author:刘飞华 28 | *date:2024/12/12 14:41:44 29 | */ 30 | rbatis::crud!(Role {}, "sys_role"); 31 | 32 | impl From for Role { 33 | fn from(item: RoleReq) -> Self { 34 | let mut model = Role { 35 | id: item.id, //主键 36 | role_name: item.role_name, //名称 37 | role_key: item.role_key, //角色权限字符串 38 | data_scope: item.data_scope, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 39 | status: item.status, //状态(1:正常,0:禁用) 40 | remark: item.remark, //备注 41 | del_flag: None, //删除标志(0代表删除 1代表存在) 42 | create_time: None, //创建时间 43 | update_time: None, //修改时间 44 | }; 45 | if let None = item.id { 46 | model.create_time = Some(DateTime::now()); 47 | } else { 48 | model.update_time = Some(DateTime::now()); 49 | } 50 | model 51 | } 52 | } 53 | 54 | impl Into for Role { 55 | fn into(self) -> RoleResp { 56 | RoleResp { 57 | id: self.id, //主键 58 | role_name: self.role_name, //名称 59 | role_key: self.role_key, //角色权限字符串 60 | data_scope: self.data_scope, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 61 | status: self.status, //状态(1:正常,0:禁用) 62 | remark: self.remark, //备注 63 | create_time: self.create_time, //创建时间 64 | update_time: self.update_time, //修改时间 65 | } 66 | } 67 | } 68 | 69 | /* 70 | *根据id查询角色信息 71 | *author:刘飞华 72 | *date:2024/12/12 14:41:44 73 | */ 74 | impl_select!(Role{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_role"); 75 | 76 | /* 77 | *根据role_name查询角色信息 78 | *author:刘飞华 79 | *date:2024/12/12 14:41:44 80 | */ 81 | impl_select!(Role{select_by_role_name(role_name:&str) -> Option => "`where role_name = #{role_name} limit 1`"}, "sys_role"); 82 | 83 | /* 84 | *根据role_key查询角色信息 85 | *author:刘飞华 86 | *date:2024/12/12 14:41:44 87 | */ 88 | impl_select!(Role{select_by_role_key(role_key:&str) -> Option => "`where role_key = #{role_key} limit 1`"}, "sys_role"); 89 | 90 | /* 91 | *分页查询角色信息 92 | *author:刘飞华 93 | *date:2024/12/12 14:41:44 94 | */ 95 | impl_select_page!(Role{select_page() =>" 96 | if !sql.contains('count'): 97 | order by create_time desc" 98 | },"sys_role"); 99 | 100 | /* 101 | *根据条件分页查询角色信息 102 | *author:刘飞华 103 | *date:2024/12/12 14:41:44 104 | */ 105 | impl_select_page!(Role{select_sys_role_list(req:&QueryRoleListReq) =>" 106 | where 1=1 107 | if req.roleName != null && req.roleName != '': 108 | ` and role_name like concat('%', #{req.roleName}, '%') ` 109 | if req.roleKey != null && req.roleKey != '': 110 | ` and role_key like concat('%', #{req.roleKey}, '%') ` 111 | if req.status != 2: 112 | ` and status = #{req.status} ` 113 | if !sql.contains('count'): 114 | ` order by create_time desc `"},"sys_role"); 115 | -------------------------------------------------------------------------------- /src/handler/system/sys_notice_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_notice_model::Notice; 4 | use crate::vo::system::sys_notice_vo::*; 5 | use crate::AppState; 6 | use axum::extract::State; 7 | use axum::response::IntoResponse; 8 | use axum::Json; 9 | use log::info; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbs::value; 12 | use std::sync::Arc; 13 | /* 14 | *添加通知公告表 15 | *author:刘飞华 16 | *date:2024/12/25 11:36:48 17 | */ 18 | pub async fn add_sys_notice(State(state): State>, Json(mut item): Json) -> impl IntoResponse { 19 | info!("add sys_notice params: {:?}", &item); 20 | let rb = &state.batis; 21 | 22 | if Notice::select_by_title(rb, &item.notice_title).await?.is_some() { 23 | return Err(AppError::BusinessError("公告标题已存在")); 24 | }; 25 | 26 | item.id = None; 27 | Notice::insert(rb, &Notice::from(item)).await.map(|_| ok_result())? 28 | } 29 | 30 | /* 31 | *删除通知公告表 32 | *author:刘飞华 33 | *date:2024/12/25 11:36:48 34 | */ 35 | pub async fn delete_sys_notice(State(state): State>, Json(item): Json) -> impl IntoResponse { 36 | info!("delete sys_notice params: {:?}", &item); 37 | let rb = &state.batis; 38 | 39 | Notice::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result())? 40 | } 41 | 42 | /* 43 | *更新通知公告表 44 | *author:刘飞华 45 | *date:2024/12/25 11:36:48 46 | */ 47 | pub async fn update_sys_notice(State(state): State>, Json(item): Json) -> impl IntoResponse { 48 | info!("update sys_notice params: {:?}", &item); 49 | let rb = &state.batis; 50 | 51 | let id = item.id; 52 | if id.is_none() { 53 | return Err(AppError::BusinessError("主键不能为空")); 54 | } 55 | 56 | if Notice::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 57 | return Err(AppError::BusinessError("通知公告表不存在")); 58 | } 59 | 60 | if let Some(x) = Notice::select_by_title(rb, &item.notice_title).await? { 61 | if x.id != id { 62 | return Err(AppError::BusinessError("公告标题已存在")); 63 | } 64 | } 65 | 66 | Notice::update_by_map(rb, &Notice::from(item), value! {"id": &id}).await.map(|_| ok_result())? 67 | } 68 | 69 | /* 70 | *更新通知公告表状态 71 | *author:刘飞华 72 | *date:2024/12/25 11:36:48 73 | */ 74 | pub async fn update_sys_notice_status(State(state): State>, Json(item): Json) -> impl IntoResponse { 75 | info!("update sys_notice_status params: {:?}", &item); 76 | let rb = &state.batis; 77 | 78 | let update_sql = format!("update sys_notice set status = ? where id in ({})", item.ids.iter().map(|_| "?").collect::>().join(", ")); 79 | 80 | let mut param = vec![value!(item.status)]; 81 | param.extend(item.ids.iter().map(|&id| value!(id))); 82 | 83 | rb.exec(&update_sql, param).await.map(|_| ok_result())? 84 | } 85 | 86 | /* 87 | *查询通知公告表详情 88 | *author:刘飞华 89 | *date:2024/12/25 11:36:48 90 | */ 91 | pub async fn query_sys_notice_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 92 | info!("query sys_notice_detail params: {:?}", &item); 93 | let rb = &state.batis; 94 | 95 | Notice::select_by_id(rb, &item.id).await?.map_or_else( 96 | || Err(AppError::BusinessError("通知公告表不存在")), 97 | |x| { 98 | let notice: NoticeResp = x.into(); 99 | ok_result_data(notice) 100 | }, 101 | ) 102 | } 103 | 104 | /* 105 | *查询通知公告表列表 106 | *author:刘飞华 107 | *date:2024/12/25 11:36:48 108 | */ 109 | pub async fn query_sys_notice_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 110 | info!("query sys_notice_list params: {:?}", &item); 111 | let rb = &state.batis; 112 | 113 | let page = &PageRequest::new(item.page_no, item.page_size); 114 | 115 | Notice::select_sys_notice_list(rb, page, &item) 116 | .await 117 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 118 | } 119 | -------------------------------------------------------------------------------- /src/middleware/auth.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::BaseResponse; 3 | use crate::utils::jwt_util::JwtToken; 4 | use crate::AppState; 5 | use axum::extract::{Request, State}; 6 | use axum::http::StatusCode; 7 | use axum::middleware::Next; 8 | use axum::response::IntoResponse; 9 | use axum::{http, response, Json}; 10 | use redis::{Client, Commands}; 11 | use std::sync::Arc; 12 | 13 | pub async fn auth(State(state): State>, mut req: Request, next: Next) -> Result { 14 | log::info!("req {:?}", req.uri()); 15 | let path = req.uri().to_string(); 16 | if path.eq("/system/user/login") { 17 | return Ok(next.run(req).await); 18 | } 19 | let auth_header = req.headers().get(http::header::AUTHORIZATION).and_then(|header| header.to_str().ok()); 20 | 21 | if auth_header.is_none() { 22 | let json = Json(BaseResponse { 23 | msg: "请求头缺少 Authorization 字段".to_string(), 24 | code: 401, 25 | data: Some("None".to_string()), 26 | }); 27 | return Ok((StatusCode::OK, json).into_response()); 28 | } 29 | let authorization = auth_header.unwrap(); 30 | 31 | let token = authorization.to_string().replace("Bearer ", ""); 32 | let jwt_token_e = JwtToken::verify("123", &token); 33 | let jwt_token = match jwt_token_e { 34 | Ok(data) => data, 35 | Err(err) => { 36 | let er = match err { 37 | AppError::JwtTokenError(s) => s, 38 | _ => "no math error".to_string(), 39 | }; 40 | let json = Json(BaseResponse { 41 | msg: er, 42 | code: 401, 43 | data: Some("None".to_string()), 44 | }); 45 | return Ok((StatusCode::OK, json).into_response()); 46 | } 47 | }; 48 | 49 | match validate_and_get_user_info(&state.redis, jwt_token.id).await { 50 | Ok((user_id, permissions, token_1, is_admin)) => { 51 | if token_1 != token { 52 | let json = Json(BaseResponse { 53 | msg: "无效的token".to_string(), 54 | code: 401, 55 | data: Some("None".to_string()), 56 | }); 57 | return Ok((StatusCode::OK, json).into_response()); 58 | } 59 | if is_admin || has_permission(&permissions, &path) { 60 | req.headers_mut().insert("user_id", user_id.to_string().parse().unwrap()); 61 | 62 | Ok(next.run(req).await) 63 | } else { 64 | let json = Json(BaseResponse { 65 | msg: format!("用户还没有授权url:{}", path), 66 | code: 401, 67 | data: Some("None".to_string()), 68 | }); 69 | Ok((StatusCode::OK, json).into_response()) 70 | } 71 | } 72 | Err(e) => { 73 | let json = Json(BaseResponse { 74 | msg: e.to_string(), 75 | code: 401, 76 | data: Some("None".to_string()), 77 | }); 78 | Ok((StatusCode::OK, json).into_response()) 79 | } 80 | } 81 | } 82 | 83 | async fn validate_and_get_user_info(redis_client: &Client, user_id: i64) -> Result<(i64, Vec, String, bool), String> { 84 | let mut conn = redis_client.get_connection().map_err(|_| "Redis连接失败".to_string())?; 85 | 86 | let key = format!("axum:admin:user:info:{}", user_id); 87 | let permissions_str: String = conn.hget(&key, "permissions").unwrap_or_else(|_| "".to_string()); 88 | let token: String = conn.hget(&key, "token").map_err(|_| "无效的token".to_string())?; 89 | let is_admin: bool = conn.hget(&key, "isAdmin").unwrap_or_default(); 90 | let permissions: Vec = if permissions_str.is_empty() { 91 | Vec::new() 92 | } else { 93 | permissions_str.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() 94 | }; 95 | Ok((user_id, permissions, token, is_admin)) 96 | } 97 | 98 | fn has_permission(permissions: &[String], path: &str) -> bool { 99 | permissions.iter().any(|permission| { 100 | // 确保权限路径以 /api 开头 101 | if permission.starts_with("/api") { 102 | // 移除 /api 前缀进行比较 103 | let permission_path = &permission[4..]; // 跳过 "/api" 的4个字符 104 | permission_path == path 105 | } else { 106 | // 如果权限路径不以 /api 开头,直接比较 107 | permission == path 108 | } 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /src/vo/system/sys_role_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use crate::common::result::serialize_datetime; 5 | use rbatis::rbdc::DateTime; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除角色信息请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeleteRoleReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 更新角色信息请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct RoleReq { 22 | pub id: Option, //主键 23 | pub role_name: String, //名称 24 | pub role_key: String, //角色权限字符串 25 | pub data_scope: i8, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 26 | pub status: i8, //状态(1:正常,0:禁用) 27 | pub remark: Option, //备注 28 | } 29 | 30 | /* 31 | 更新角色信息状态请求参数 32 | */ 33 | #[derive(Debug, Serialize, Deserialize)] 34 | pub struct UpdateRoleStatusReq { 35 | pub ids: Vec, 36 | pub status: i8, 37 | } 38 | 39 | /* 40 | 查询角色信息详情请求参数 41 | */ 42 | #[derive(Debug, Serialize, Deserialize)] 43 | pub struct QueryRoleDetailReq { 44 | pub id: i64, 45 | } 46 | 47 | /* 48 | 查询角色信息列表请求参数 49 | */ 50 | #[derive(Debug, Serialize, Deserialize)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct QueryRoleListReq { 53 | pub page_no: u64, 54 | pub page_size: u64, 55 | pub role_name: Option, //名称 56 | #[serde(default = "default_status")] 57 | pub status: Option, //状态(1:正常,0:禁用) 58 | pub role_key: Option, //角色权限字符串 59 | } 60 | fn default_status() -> Option { 61 | Some(2) 62 | } 63 | /* 64 | 查询角色信息列表响应参数 65 | */ 66 | #[derive(Debug, Serialize, Deserialize, Clone)] 67 | #[serde(rename_all = "camelCase")] 68 | pub struct RoleResp { 69 | pub id: Option, //主键 70 | pub role_name: String, //名称 71 | pub role_key: String, //角色权限字符串 72 | pub data_scope: i8, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 73 | pub status: i8, //状态(1:正常,0:禁用) 74 | pub remark: Option, //备注 75 | #[serde(serialize_with = "serialize_datetime")] 76 | pub create_time: Option, //创建时间 77 | #[serde(serialize_with = "serialize_datetime")] 78 | pub update_time: Option, //修改时间 79 | } 80 | 81 | /* 82 | 查询角色菜单信息参数 83 | */ 84 | #[derive(Debug, Deserialize)] 85 | #[serde(rename_all = "camelCase")] 86 | pub struct QueryRoleMenuReq { 87 | pub role_id: i64, //角色id 88 | } 89 | 90 | /* 91 | 角色菜单信息参数 92 | */ 93 | #[derive(Debug, Serialize)] 94 | #[serde(rename_all = "camelCase")] 95 | pub struct QueryRoleMenuData { 96 | pub menu_ids: Vec>, //菜单Ids 97 | pub menu_list: Vec, //菜单列表 98 | } 99 | 100 | /* 101 | 菜单信息参数 102 | */ 103 | #[derive(Debug, Serialize)] 104 | #[serde(rename_all = "camelCase")] 105 | pub struct MenuDataList { 106 | pub id: Option, //主键 107 | pub parent_id: Option, //父ID 108 | pub title: String, 109 | pub key: String, 110 | pub label: String, 111 | pub is_penultimate: bool, 112 | pub is_leaf: bool, 113 | } 114 | 115 | /* 116 | 更新用户角色信息 117 | */ 118 | #[derive(Debug, Deserialize)] 119 | #[serde(rename_all = "camelCase")] 120 | pub struct UpdateRoleMenuReq { 121 | pub menu_ids: Vec, 122 | pub role_id: i64, 123 | } 124 | 125 | /* 126 | 查询已分配用户角色列表 127 | */ 128 | #[derive(Debug, Deserialize)] 129 | #[serde(rename_all = "camelCase")] 130 | pub struct AllocatedListReq { 131 | pub page_no: u64, 132 | pub page_size: u64, 133 | pub role_id: i64, 134 | pub mobile: Option, 135 | pub user_name: Option, 136 | } 137 | 138 | /* 139 | 查询未分配用户角色列表 140 | */ 141 | #[derive(Debug, Deserialize)] 142 | #[serde(rename_all = "camelCase")] 143 | pub struct UnallocatedListReq { 144 | pub page_no: u64, 145 | pub page_size: u64, 146 | pub role_id: i64, 147 | pub mobile: Option, 148 | pub user_name: Option, 149 | } 150 | 151 | /* 152 | 取消授权用户 153 | */ 154 | #[derive(Debug, Deserialize)] 155 | #[serde(rename_all = "camelCase")] 156 | pub struct CancelAuthUserReq { 157 | pub user_id: i64, 158 | pub role_id: i64, 159 | } 160 | 161 | /* 162 | 批量取消授权用户 163 | */ 164 | #[derive(Debug, Deserialize)] 165 | #[serde(rename_all = "camelCase")] 166 | pub struct CancelAuthUserAllReq { 167 | pub user_ids: Vec, 168 | pub role_id: i64, 169 | } 170 | 171 | /* 172 | 批量选择用户授权 173 | */ 174 | #[derive(Debug, Deserialize)] 175 | #[serde(rename_all = "camelCase")] 176 | pub struct SelectAuthUserAllReq { 177 | pub user_ids: Vec, 178 | pub role_id: i64, 179 | } 180 | -------------------------------------------------------------------------------- /src/handler/system/sys_dict_data_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_dict_data_model::DictData; 4 | use crate::vo::system::sys_dict_data_vo::*; 5 | use crate::AppState; 6 | use axum::extract::State; 7 | use axum::response::IntoResponse; 8 | use axum::Json; 9 | use log::info; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbs::value; 12 | use std::sync::Arc; 13 | /* 14 | *添加字典数据 15 | *author:刘飞华 16 | *date:2024/12/25 11:36:48 17 | */ 18 | pub async fn add_sys_dict_data(State(state): State>, Json(mut item): Json) -> impl IntoResponse { 19 | info!("add sys_dict_data params: {:?}", &item); 20 | let rb = &state.batis; 21 | 22 | if DictData::select_by_dict_label(rb, &item.dict_type, &item.dict_label).await?.is_some() { 23 | return Err(AppError::BusinessError("字典标签已存在")); 24 | } 25 | 26 | if DictData::select_by_dict_value(rb, &item.dict_type, &item.dict_value).await?.is_some() { 27 | return Err(AppError::BusinessError("字典键值已存在")); 28 | } 29 | 30 | item.id = None; 31 | DictData::insert(rb, &DictData::from(item)).await.map(|_| ok_result())? 32 | } 33 | 34 | /* 35 | *删除字典数据 36 | *author:刘飞华 37 | *date:2024/12/25 11:36:48 38 | */ 39 | pub async fn delete_sys_dict_data(State(state): State>, Json(item): Json) -> impl IntoResponse { 40 | info!("delete sys_dict_data params: {:?}", &item); 41 | let rb = &state.batis; 42 | 43 | DictData::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result())? 44 | } 45 | 46 | /* 47 | *更新字典数据 48 | *author:刘飞华 49 | *date:2024/12/25 11:36:48 50 | */ 51 | pub async fn update_sys_dict_data(State(state): State>, Json(item): Json) -> impl IntoResponse { 52 | info!("update sys_dict_data params: {:?}", &item); 53 | let rb = &state.batis; 54 | 55 | let id = item.id; 56 | 57 | if id.is_none() { 58 | return Err(AppError::BusinessError("主键不能为空")); 59 | } 60 | 61 | if DictData::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 62 | return Err(AppError::BusinessError("字典数据不存在")); 63 | } 64 | 65 | if let Some(x) = DictData::select_by_dict_label(rb, &item.dict_type, &item.dict_label).await? { 66 | if x.id != id { 67 | return Err(AppError::BusinessError("字典标签已存在")); 68 | } 69 | } 70 | 71 | if let Some(x) = DictData::select_by_dict_value(rb, &item.dict_type, &item.dict_value).await? { 72 | if x.id != id { 73 | return Err(AppError::BusinessError("字典键值已存在")); 74 | } 75 | } 76 | 77 | DictData::update_by_map(rb, &DictData::from(item), value! {"id": &id}).await.map(|_| ok_result())? 78 | } 79 | 80 | /* 81 | *更新字典数据状态 82 | *author:刘飞华 83 | *date:2024/12/25 11:36:48 84 | */ 85 | pub async fn update_sys_dict_data_status(State(state): State>, Json(item): Json) -> impl IntoResponse { 86 | info!("update sys_dict_data_status params: {:?}", &item); 87 | let rb = &state.batis; 88 | 89 | let update_sql = format!("update sys_dict_data set status = ? where id in ({})", item.ids.iter().map(|_| "?").collect::>().join(", ")); 90 | 91 | let mut param = vec![value!(item.status)]; 92 | param.extend(item.ids.iter().map(|&id| value!(id))); 93 | rb.exec(&update_sql, param).await.map(|_| ok_result())? 94 | } 95 | 96 | /* 97 | *查询字典数据详情 98 | *author:刘飞华 99 | *date:2024/12/25 11:36:48 100 | */ 101 | pub async fn query_sys_dict_data_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 102 | info!("query sys_dict_data_detail params: {:?}", &item); 103 | let rb = &state.batis; 104 | 105 | DictData::select_by_id(rb, &item.id).await?.map_or_else( 106 | || Err(AppError::BusinessError("字典数据不存在")), 107 | |x| { 108 | let data: DictDataResp = x.into(); 109 | ok_result_data(data) 110 | }, 111 | ) 112 | } 113 | 114 | /* 115 | *查询字典数据列 116 | *author:刘飞华 117 | *date:2024/12/25 11:36:48 118 | */ 119 | pub async fn query_sys_dict_data_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 120 | info!("query sys_dict_data_list params: {:?}", &item); 121 | let rb = &state.batis; 122 | 123 | let page = &PageRequest::new(item.page_no, item.page_size); 124 | 125 | DictData::select_dict_data_list(rb, page, &item) 126 | .await 127 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 128 | } 129 | -------------------------------------------------------------------------------- /src/handler/system/sys_post_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_post_model::Post; 4 | use crate::model::system::sys_user_post_model::count_user_post_by_id; 5 | use crate::vo::system::sys_post_vo::*; 6 | use crate::AppState; 7 | use axum::extract::State; 8 | use axum::response::IntoResponse; 9 | use axum::Json; 10 | use log::info; 11 | use rbatis::plugin::page::PageRequest; 12 | use rbs::value; 13 | use std::sync::Arc; 14 | /* 15 | *添加岗位信息表 16 | *author:刘飞华 17 | *date:2024/12/25 11:36:48 18 | */ 19 | pub async fn add_sys_post(State(state): State>, Json(mut item): Json) -> impl IntoResponse { 20 | info!("add sys_post params: {:?}", &item); 21 | let rb = &state.batis; 22 | 23 | if Post::select_by_name(rb, &item.post_name).await?.is_some() { 24 | return Err(AppError::BusinessError("岗位名称已存在")); 25 | } 26 | 27 | if Post::select_by_code(rb, &item.post_code).await?.is_some() { 28 | return Err(AppError::BusinessError("岗位编码已存在")); 29 | } 30 | 31 | item.id = None; 32 | Post::insert(rb, &Post::from(item)).await.map(|_| ok_result())? 33 | } 34 | 35 | /* 36 | *删除岗位信息表 37 | *author:刘飞华 38 | *date:2024/12/25 11:36:48 39 | */ 40 | pub async fn delete_sys_post(State(state): State>, Json(item): Json) -> impl IntoResponse { 41 | info!("delete sys_post params: {:?}", &item); 42 | let rb = &state.batis; 43 | 44 | let ids = item.ids.clone(); 45 | for id in ids { 46 | if count_user_post_by_id(rb, id).await? > 0 { 47 | return Err(AppError::BusinessError("已分配,不能删除")); 48 | } 49 | } 50 | 51 | Post::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result())? 52 | } 53 | 54 | /* 55 | *更新岗位信息表 56 | *author:刘飞华 57 | *date:2024/12/25 11:36:48 58 | */ 59 | pub async fn update_sys_post(State(state): State>, Json(item): Json) -> impl IntoResponse { 60 | info!("update sys_post params: {:?}", &item); 61 | let rb = &state.batis; 62 | 63 | let id = item.id; 64 | 65 | if id.is_none() { 66 | return Err(AppError::BusinessError("主键不能为空")); 67 | } 68 | 69 | if Post::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 70 | return Err(AppError::BusinessError("岗位不存在")); 71 | } 72 | 73 | if let Some(x) = Post::select_by_name(rb, &item.post_name).await? { 74 | if x.id != id { 75 | return Err(AppError::BusinessError("岗位名称已存在")); 76 | } 77 | } 78 | 79 | if let Some(x) = Post::select_by_code(rb, &item.post_code).await? { 80 | if x.id != id { 81 | return Err(AppError::BusinessError("岗位编码已存在")); 82 | } 83 | } 84 | 85 | Post::update_by_map(rb, &Post::from(item), value! {"id": &id}).await.map(|_| ok_result())? 86 | } 87 | 88 | /* 89 | *更新岗位信息表状态 90 | *author:刘飞华 91 | *date:2024/12/25 11:36:48 92 | */ 93 | pub async fn update_sys_post_status(State(state): State>, Json(item): Json) -> impl IntoResponse { 94 | info!("update sys_post_status params: {:?}", &item); 95 | let rb = &state.batis; 96 | 97 | let update_sql = format!("update sys_post set status = ? where id in ({})", item.ids.iter().map(|_| "?").collect::>().join(", ")); 98 | 99 | let mut param = vec![value!(item.status)]; 100 | param.extend(item.ids.iter().map(|&id| value!(id))); 101 | rb.exec(&update_sql, param).await.map(|_| ok_result())? 102 | } 103 | 104 | /* 105 | *查询岗位信息表详情 106 | *author:刘飞华 107 | *date:2024/12/25 11:36:48 108 | */ 109 | pub async fn query_sys_post_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 110 | info!("query sys_post_detail params: {:?}", &item); 111 | let rb = &state.batis; 112 | 113 | Post::select_by_id(rb, &item.id).await?.map_or_else( 114 | || Err(AppError::BusinessError("岗位不存在")), 115 | |x| { 116 | let data: PostResp = x.into(); 117 | ok_result_data(data) 118 | }, 119 | ) 120 | } 121 | 122 | /* 123 | *查询岗位信息表列表 124 | *author:刘飞华 125 | *date:2024/12/25 11:36:48 126 | */ 127 | pub async fn query_sys_post_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 128 | info!("query sys_post_list params: {:?}", &item); 129 | let rb = &state.batis; 130 | 131 | let page = &PageRequest::new(item.page_no, item.page_size); 132 | 133 | Post::select_post_list(rb, page, &item) 134 | .await 135 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 136 | } 137 | -------------------------------------------------------------------------------- /src/model/system/sys_operate_log_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_operate_log_vo::{OperateLogResp, QueryOperateLogListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use rbatis::RBatis; 8 | use serde::{Deserialize, Serialize}; 9 | /* 10 | *操作日志记录 11 | *author:刘飞华 12 | *date:2024/12/25 10:01:11 13 | */ 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct OperateLog { 16 | pub id: Option, //日志主键 17 | pub title: Option, //模块标题 18 | pub business_type: Option, //业务类型(0其它 1新增 2修改 3删除) 19 | pub method: Option, //方法名称 20 | pub request_method: Option, //请求方式 21 | pub operator_type: Option, //操作类别(0其它 1后台用户 2手机端用户) 22 | pub operate_name: Option, //操作人员 23 | pub dept_name: Option, //部门名称 24 | pub operate_url: Option, //请求URL 25 | pub operate_ip: Option, //主机地址 26 | pub operate_location: Option, //操作地点 27 | pub operate_param: Option, //请求参数 28 | pub json_result: Option, //返回参数 29 | pub status: Option, //操作状态(0:异常,正常) 30 | pub error_msg: Option, //错误消息 31 | pub operate_time: Option, //操作时间 32 | pub cost_time: Option, //消耗时间 33 | } 34 | 35 | /* 36 | *操作日志记录基本操作 37 | *author:刘飞华 38 | *date:2024/12/25 10:01:11 39 | */ 40 | rbatis::crud!(OperateLog {}, "sys_operate_log"); 41 | 42 | impl Into for OperateLog { 43 | fn into(self) -> OperateLogResp { 44 | OperateLogResp { 45 | id: self.id, //日志主键 46 | title: self.title, //模块标题 47 | business_type: self.business_type, //业务类型(0其它 1新增 2修改 3删除) 48 | method: self.method, //方法名称 49 | request_method: self.request_method, //请求方式 50 | operator_type: self.operator_type, //操作类别(0其它 1后台用户 2手机端用户) 51 | operate_name: self.operate_name, //操作人员 52 | dept_name: self.dept_name, //部门名称 53 | operate_url: self.operate_url, //请求URL 54 | operate_ip: self.operate_ip, //主机地址 55 | operate_location: self.operate_location, //操作地点 56 | operate_param: self.operate_param, //请求参数 57 | json_result: self.json_result, //返回参数 58 | status: self.status, //操作状态(0:异常,正常) 59 | error_msg: self.error_msg, //错误消息 60 | operate_time: self.operate_time, //操作时间 61 | cost_time: self.cost_time, //消耗时间 62 | } 63 | } 64 | } 65 | /* 66 | *根据id查询操作日志记录 67 | *author:刘飞华 68 | *date:2024/12/25 10:01:11 69 | */ 70 | impl_select!(OperateLog{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_operate_log"); 71 | 72 | /* 73 | *分页查询操作日志记录 74 | *author:刘飞华 75 | *date:2024/12/25 10:01:11 76 | */ 77 | impl_select_page!(OperateLog{select_page() =>" 78 | if !sql.contains('count'): 79 | order by operate_time desc" 80 | },"sys_operate_log"); 81 | 82 | /* 83 | *根据条件分页查询操作日志记录 84 | *author:刘飞华 85 | *date:2024/12/25 10:01:11 86 | */ 87 | impl_select_page!(OperateLog{select_page_by_name( 88 | req:&QueryOperateLogListReq) =>" 89 | where 1=1 90 | if req.title != '' && req.title != null: 91 | ` and title like concat('%', #{req.title}, '%') ` 92 | if req.businessType != 4: 93 | ` and business_type = #{req.businessType} ` 94 | if req.method != '' && req.method != null: 95 | ` and method = #{req.method} ` 96 | if req.requestMethod != '' && req.requestMethod != null: 97 | ` and request_method = #{req.requestMethod} ` 98 | if req.operatorType != 3: 99 | ` and operator_type = #{req.operatorType} ` 100 | if req.operateName != '' && req.operateName != null: 101 | ` and operate_name = #{req.operateName} ` 102 | if req.deptName != '' && req.deptName != null: 103 | ` and dept_name = #{req.deptName} ` 104 | if req.operateUrl != '' && req.operateUrl != null: 105 | ` and operate_url = #{req.operateUrl} ` 106 | if req.operateIp != '' && req.operateIp != null: 107 | ` and operate_ip = #{req.operateIp} ` 108 | if req.status != 2: 109 | ` and status = #{req.status} ` 110 | if !sql.contains('count'): 111 | ` order by operate_time desc `" 112 | },"sys_operate_log"); 113 | 114 | /* 115 | *清空操作日志 116 | *author:刘飞华 117 | *date:2024/12/12 14:41:44 118 | */ 119 | #[sql("truncate table sys_operate_log")] 120 | pub async fn clean_operate_log(rb: &RBatis) -> Option { 121 | impled!() 122 | } 123 | -------------------------------------------------------------------------------- /src/handler/system/sys_dict_type_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_dict_data_model::{count_dict_data_by_type, update_dict_data_type}; 4 | use crate::model::system::sys_dict_type_model::DictType; 5 | use crate::vo::system::sys_dict_type_vo::*; 6 | use crate::AppState; 7 | use axum::extract::State; 8 | use axum::response::IntoResponse; 9 | use axum::Json; 10 | use log::info; 11 | use rbatis::plugin::page::PageRequest; 12 | use rbs::value; 13 | use std::sync::Arc; 14 | /* 15 | *添加字典类型 16 | *author:刘飞华 17 | *date:2024/12/25 11:36:48 18 | */ 19 | pub async fn add_sys_dict_type(State(state): State>, Json(mut item): Json) -> impl IntoResponse { 20 | info!("add sys_dict_type params: {:?}", &item); 21 | let rb = &state.batis; 22 | 23 | if DictType::select_by_dict_type(rb, &item.dict_type).await?.is_some() { 24 | return Err(AppError::BusinessError("字典类型已存在")); 25 | } 26 | 27 | item.id = None; 28 | DictType::insert(rb, &DictType::from(item)).await.map(|_| ok_result())? 29 | } 30 | 31 | /* 32 | *删除字典类型 33 | *author:刘飞华 34 | *date:2024/12/25 11:36:48 35 | */ 36 | pub async fn delete_sys_dict_type(State(state): State>, Json(item): Json) -> impl IntoResponse { 37 | info!("delete sys_dict_type params: {:?}", &item); 38 | let rb = &state.batis; 39 | 40 | let ids = item.ids.clone(); 41 | for id in ids { 42 | let p = match DictType::select_by_id(rb, &id).await? { 43 | None => return Err(AppError::BusinessError("字典类型不存在,不能删除")), 44 | Some(p) => p, 45 | }; 46 | 47 | if count_dict_data_by_type(rb, &p.dict_type).await? > 0 { 48 | return Err(AppError::BusinessError("已分配,不能删除")); 49 | } 50 | } 51 | 52 | DictType::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result())? 53 | } 54 | 55 | /* 56 | *更新字典类型 57 | *author:刘飞华 58 | *date:2024/12/25 11:36:48 59 | */ 60 | pub async fn update_sys_dict_type(State(state): State>, Json(item): Json) -> impl IntoResponse { 61 | info!("update sys_dict_type params: {:?}", &item); 62 | let rb = &state.batis; 63 | 64 | let id = item.id; 65 | 66 | if id.is_none() { 67 | return Err(AppError::BusinessError("主键不能为空")); 68 | } 69 | 70 | if DictType::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 71 | return Err(AppError::BusinessError("字典类型不存在")); 72 | } 73 | 74 | if let Some(x) = DictType::select_by_dict_type(rb, &item.dict_type).await? { 75 | if x.id != id { 76 | return Err(AppError::BusinessError("字典类型已存在")); 77 | } 78 | 79 | let dict_type = x.dict_type; 80 | update_dict_data_type(rb, &*item.dict_type, &dict_type).await?; 81 | } 82 | 83 | DictType::update_by_map(rb, &DictType::from(item), value! {"id": &id}).await.map(|_| ok_result())? 84 | } 85 | 86 | /* 87 | *更新字典类型状态 88 | *author:刘飞华 89 | *date:2024/12/25 11:36:48 90 | */ 91 | pub async fn update_sys_dict_type_status(State(state): State>, Json(item): Json) -> impl IntoResponse { 92 | info!("update sys_dict_type_status params: {:?}", &item); 93 | let rb = &state.batis; 94 | 95 | let update_sql = format!("update sys_dict_type set status = ? where id in ({})", item.ids.iter().map(|_| "?").collect::>().join(", ")); 96 | 97 | let mut param = vec![value!(item.status)]; 98 | param.extend(item.ids.iter().map(|&id| value!(id))); 99 | rb.exec(&update_sql, param).await.map(|_| ok_result())? 100 | } 101 | 102 | /* 103 | *查询字典类型详情 104 | *author:刘飞华 105 | *date:2024/12/25 11:36:48 106 | */ 107 | pub async fn query_sys_dict_type_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 108 | info!("query sys_dict_type_detail params: {:?}", &item); 109 | let rb = &state.batis; 110 | 111 | DictType::select_by_id(rb, &item.id).await?.map_or_else( 112 | || Err(AppError::BusinessError("字典类型不存在")), 113 | |x| { 114 | let data: DictTypeResp = x.into(); 115 | ok_result_data(data) 116 | }, 117 | ) 118 | } 119 | 120 | /* 121 | *查询字典类型列 122 | *author:刘飞华 123 | *date:2024/12/25 11:36:48 124 | */ 125 | pub async fn query_sys_dict_type_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 126 | info!("query sys_dict_type_list params: {:?}", &item); 127 | let rb = &state.batis; 128 | 129 | let page = &PageRequest::new(item.page_no, item.page_size); 130 | 131 | DictType::select_dict_type_list(rb, page, &item) 132 | .await 133 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 134 | } 135 | -------------------------------------------------------------------------------- /src/vo/system/sys_user_vo.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use crate::common::result::serialize_datetime; 5 | use crate::vo::system::sys_dept_vo::DeptResp; 6 | use crate::vo::system::sys_role_vo::RoleResp; 7 | use rbatis::rbdc::DateTime; 8 | use serde::{Deserialize, Serialize}; 9 | /* 10 | 删除用户信息请求参数 11 | */ 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct DeleteUserReq { 14 | pub ids: Vec, 15 | } 16 | 17 | /* 18 | 更新用户信息请求参数 19 | */ 20 | #[derive(Debug, Serialize, Deserialize)] 21 | #[serde(rename_all = "camelCase")] 22 | pub struct UserReq { 23 | pub id: Option, //主键 24 | pub mobile: String, //手机 25 | pub user_name: String, //用户账号 26 | pub nick_name: String, //用户昵称 27 | pub password: Option, //用户密码 28 | pub email: String, //用户邮箱 29 | #[serde(default = "default_avatar")] 30 | pub avatar: Option, //头像路径 31 | pub status: i8, //状态(1:正常,0:禁用) 32 | pub dept_id: i64, //部门ID 33 | pub remark: Option, //备注 34 | pub post_ids: Vec, //岗位ids 35 | } 36 | fn default_avatar() -> Option { 37 | Some("https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png".to_string()) 38 | } 39 | /* 40 | 更新用户信息状态请求参数 41 | */ 42 | #[derive(Debug, Serialize, Deserialize)] 43 | pub struct UpdateUserStatusReq { 44 | pub ids: Vec, 45 | pub status: i8, 46 | } 47 | 48 | /* 49 | 查询用户信息详情请求参数 50 | */ 51 | #[derive(Debug, Serialize, Deserialize)] 52 | pub struct QueryUserDetailReq { 53 | pub id: i64, 54 | } 55 | 56 | /* 57 | 查询用户信息列表请求参数 58 | */ 59 | #[derive(Debug, Serialize, Deserialize)] 60 | #[serde(rename_all = "camelCase")] 61 | pub struct QueryUserListReq { 62 | pub page_no: u64, 63 | pub page_size: u64, 64 | pub mobile: Option, //手机 65 | pub user_name: Option, //姓名 66 | #[serde(default = "default_status")] 67 | pub status: Option, //状态(1:正常,0:禁用) 68 | pub dept_id: Option, //部门ID 69 | } 70 | fn default_status() -> Option { 71 | Some(2) 72 | } 73 | /* 74 | 查询用户信息列表响应参数 75 | */ 76 | #[derive(Debug, Serialize, Deserialize)] 77 | #[serde(rename_all = "camelCase")] 78 | pub struct UserResp { 79 | pub id: Option, //主键 80 | pub mobile: String, //手机 81 | pub user_name: String, //用户账号 82 | pub nick_name: String, //用户昵称 83 | pub user_type: String, //用户类型(00系统用户) 84 | pub email: String, //用户邮箱 85 | pub avatar: Option, //头像路径 86 | pub status: i8, //状态(1:正常,0:禁用) 87 | pub dept_id: i64, //部门ID 88 | pub login_ip: String, //最后登录IP 89 | #[serde(serialize_with = "serialize_datetime")] 90 | pub login_date: Option, //最后登录时间 91 | pub login_browser: String, //浏览器类型 92 | pub login_os: String, //操作系统 93 | #[serde(serialize_with = "serialize_datetime")] 94 | pub pwd_update_date: Option, //密码最后更新时间 95 | pub remark: Option, //备注 96 | #[serde(serialize_with = "serialize_datetime")] 97 | pub create_time: Option, //创建时间 98 | #[serde(serialize_with = "serialize_datetime")] 99 | pub update_time: Option, //修改时间 100 | pub dept_info: Option, //部门详细信息 101 | pub post_ids: Option>, //岗位ids 102 | } 103 | /* 104 | 登录请求参数 105 | */ 106 | #[derive(Debug, Deserialize)] 107 | pub struct UserLoginReq { 108 | pub account: String, //手机 109 | pub password: String, //密码 110 | } 111 | 112 | /* 113 | 查询用户菜单响应参数 114 | */ 115 | #[derive(Debug, Serialize)] 116 | #[serde(rename_all = "camelCase")] 117 | pub struct QueryUserMenuResp { 118 | pub sys_menu: Vec, 119 | pub btn_menu: Vec, 120 | pub avatar: Option, 121 | pub name: String, 122 | } 123 | 124 | /* 125 | 用户菜单参数 126 | */ 127 | #[derive(Debug, Serialize, Clone)] 128 | #[serde(rename_all = "camelCase")] 129 | pub struct MenuList { 130 | pub id: Option, 131 | pub parent_id: Option, 132 | pub name: String, 133 | pub path: String, 134 | pub api_url: String, 135 | pub menu_type: i8, 136 | pub icon: String, 137 | } 138 | 139 | /* 140 | 查询用户关联角色请求参数 141 | */ 142 | #[derive(Debug, Deserialize)] 143 | #[serde(rename_all = "camelCase")] 144 | pub struct QueryUserRoleReq { 145 | pub user_id: i64, 146 | } 147 | 148 | /* 149 | 用户关联角色响应参数 150 | */ 151 | #[derive(Debug, Serialize)] 152 | #[serde(rename_all = "camelCase")] 153 | pub struct QueryUserRoleResp { 154 | pub sys_role_list: Vec, 155 | pub user_role_ids: Vec, 156 | } 157 | 158 | /* 159 | 更新用户关联角色请求参数 160 | */ 161 | #[derive(Debug, Deserialize)] 162 | #[serde(rename_all = "camelCase")] 163 | pub struct UpdateUserRoleReq { 164 | pub user_id: i64, //用户主键 165 | pub role_ids: Vec, //角色主键 166 | } 167 | 168 | /* 169 | 重置密码 170 | */ 171 | #[derive(Debug, Deserialize)] 172 | pub struct ResetUserPwdReq { 173 | pub id: i64, //用户主键 174 | pub password: String, //用户密码 175 | } 176 | 177 | /* 178 | 重置密码 179 | */ 180 | #[derive(Debug, Deserialize)] 181 | #[serde(rename_all = "camelCase")] 182 | pub struct UpdateUserPwdReq { 183 | pub pwd: String, //用户密码 184 | pub re_pwd: String, //用户密码 185 | } 186 | -------------------------------------------------------------------------------- /src/model/system/sys_dict_data_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_dict_data_vo::{DictDataReq, DictDataResp, QueryDictDataListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use rbatis::RBatis; 8 | use serde::{Deserialize, Serialize}; 9 | /* 10 | *字典数据表 11 | *author:刘飞华 12 | *date:2024/12/25 10:01:11 13 | */ 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct DictData { 16 | pub id: Option, //字典编码 17 | pub dict_sort: i32, //字典排序 18 | pub dict_label: String, //字典标签 19 | pub dict_value: String, //字典键值 20 | pub dict_type: String, //字典类型 21 | pub css_class: String, //样式属性(其他样式扩展) 22 | pub list_class: String, //表格回显样式 23 | pub is_default: String, //是否默认(Y是 N否) 24 | pub status: i8, //状态(0:停用,1:正常) 25 | pub remark: Option, //备注 26 | pub create_time: Option, //创建时间 27 | pub update_time: Option, //修改时间 28 | } 29 | 30 | /* 31 | *字典数据表基本操作 32 | *author:刘飞华 33 | *date:2024/12/25 10:01:11 34 | */ 35 | rbatis::crud!(DictData {}, "sys_dict_data"); 36 | 37 | impl From for DictData { 38 | fn from(item: DictDataReq) -> Self { 39 | let mut model = DictData { 40 | id: item.id, //字典编码 41 | dict_sort: item.dict_sort, //字典排序 42 | dict_label: item.dict_label, //字典标签 43 | dict_value: item.dict_value, //字典键值 44 | dict_type: item.dict_type, //字典类型 45 | css_class: item.css_class, //样式属性(其他样式扩展) 46 | list_class: item.list_class, //格回显样式 47 | is_default: item.is_default, //是否默认(Y是 N否) 48 | status: item.status, //状态(0:停用,1:正常) 49 | remark: item.remark, //备注 50 | create_time: None, //创建时间 51 | update_time: None, //修改时间 52 | }; 53 | if let None = item.id { 54 | model.create_time = Some(DateTime::now()); 55 | } else { 56 | model.update_time = Some(DateTime::now()); 57 | } 58 | model 59 | } 60 | } 61 | 62 | impl Into for DictData { 63 | fn into(self) -> DictDataResp { 64 | DictDataResp { 65 | id: self.id, //字典编码 66 | dict_sort: self.dict_sort, //字典排序 67 | dict_label: self.dict_label, //字典标签 68 | dict_value: self.dict_value, //字典键值 69 | dict_type: self.dict_type, //字典类型 70 | css_class: self.css_class, //样式属性(其他样式扩展) 71 | list_class: self.list_class, //格回显样式 72 | is_default: self.is_default, //是否默认(Y是 N否) 73 | status: self.status, //状态(0:停用,1:正常) 74 | remark: self.remark, //备注 75 | create_time: self.create_time, //创建时间 76 | update_time: self.update_time, //修改时间 77 | } 78 | } 79 | } 80 | 81 | /* 82 | *根据id查询字典数据表 83 | *author:刘飞华 84 | *date:2024/12/25 10:01:11 85 | */ 86 | impl_select!(DictData{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_dict_data"); 87 | 88 | /* 89 | *根据dict_type和dict_label查询字典数据表 90 | *author:刘飞华 91 | *date:2024/12/25 10:01:11 92 | */ 93 | impl_select!(DictData{select_by_dict_label(dict_type:&str, dict_label:&str) -> Option => "`where dict_type = #{dict_type} and dict_label = #{dict_label}`"}, "sys_dict_data"); 94 | 95 | /* 96 | *根据dict_type和dict_value查询字典数据表 97 | *author:刘飞华 98 | *date:2024/12/25 10:01:11 99 | */ 100 | impl_select!(DictData{select_by_dict_value(dict_type:&str, dict_value:&str) -> Option => "`where dict_type = #{dict_type} and dict_value = #{dict_value}`"}, "sys_dict_data"); 101 | 102 | /* 103 | *分页查询字典数据表 104 | *author:刘飞华 105 | *date:2024/12/25 10:01:11 106 | */ 107 | impl_select_page!(DictData{select_page() =>" 108 | if !sql.contains('count'): 109 | order by create_time desc" 110 | },"sys_dict_data"); 111 | 112 | /* 113 | *根据条件分页查询字典数据表 114 | *author:刘飞华 115 | *date:2024/12/25 10:01:11 116 | */ 117 | impl_select_page!(DictData{select_dict_data_list(req:&QueryDictDataListReq) =>" 118 | where 1=1 119 | if req.dictLabel != null && req.dictLabel != '': 120 | ` and dict_label like concat('%', #{req.dictLabel}, '%') ` 121 | if req.dictType != null && req.dictType != '': 122 | ` and dict_type like concat('%', #{req.dictType}, '%') ` 123 | if req.status != 2: 124 | ` and status = #{req.status} ` 125 | if !sql.contains('count'): 126 | ` order by create_time desc" 127 | },"sys_dict_data"); 128 | 129 | /* 130 | *同步修改字典类型 131 | *author:刘飞华 132 | *date:2024/12/25 10:01:11 133 | */ 134 | #[sql("update sys_dict_data set dict_type = ? where dict_type = ?")] 135 | pub async fn update_dict_data_type(rb: &RBatis, new_dict_type: &str, old_dict_type: &str) -> Option { 136 | impled!() 137 | } 138 | 139 | /* 140 | *查询字典数据 141 | *author:刘飞华 142 | *date:2024/12/25 10:01:11 143 | */ 144 | #[sql("select count(1) from sys_dict_data where dict_type= ?")] 145 | pub async fn count_dict_data_by_type(rb: &RBatis, dict_type: &str) -> rbatis::Result { 146 | impled!() 147 | } 148 | -------------------------------------------------------------------------------- /src/model/system/sys_dept_model.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // createTime:2024/12/25 10:01:11 4 | 5 | use crate::vo::system::sys_dept_vo::{DeptReq, DeptResp, QueryDeptListReq}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use rbatis::RBatis; 8 | use serde::{Deserialize, Serialize}; 9 | use validator::Validate; 10 | /* 11 | *部门 12 | *author:刘飞华 13 | *date:2024/12/25 10:01:11 14 | */ 15 | #[derive(Clone, Debug, Serialize, Deserialize, Validate)] 16 | pub struct Dept { 17 | pub id: Option, //部门id 18 | pub parent_id: i64, //父部门id 19 | pub ancestors: Option, //祖级列 20 | pub dept_name: String, //部门名称 21 | pub sort: i32, //显示顺序 22 | pub leader: String, //负责人 23 | pub phone: String, //联系电话 24 | pub email: String, //邮箱 25 | pub status: i8, //部状态(0:停用,1:正常) 26 | pub del_flag: Option, //删除标志(0代删除 1代存在) 27 | pub create_time: Option, //创建时间 28 | pub update_time: Option, //修改时间 29 | } 30 | 31 | /* 32 | *部门基本操作 33 | *author:刘飞华 34 | *date:2024/12/25 10:01:11 35 | */ 36 | rbatis::crud!(Dept {}, "sys_dept"); 37 | 38 | impl From for Dept { 39 | fn from(item: DeptReq) -> Self { 40 | let mut model = Dept { 41 | id: item.id, //部门id 42 | parent_id: item.parent_id, //父部门id 43 | ancestors: item.ancestors, //祖级列表 44 | dept_name: item.dept_name, //部门名称 45 | sort: item.sort, //显示顺序 46 | leader: item.leader, //负责人 47 | phone: item.phone, //联系电话 48 | email: item.email, //邮箱 49 | status: item.status, //部状态(0:停用,1:正常) 50 | del_flag: None, //删除标志(0代表删除 1代表存在) 51 | create_time: None, //创建时间 52 | update_time: None, //修改时间 53 | }; 54 | if let None = item.id { 55 | model.create_time = Some(DateTime::now()); 56 | } else { 57 | model.update_time = Some(DateTime::now()); 58 | } 59 | model 60 | } 61 | } 62 | 63 | impl Into for Dept { 64 | fn into(self) -> DeptResp { 65 | DeptResp { 66 | id: self.id, //部门id 67 | key: self.id.unwrap().to_string(), //部门id 68 | parent_id: self.parent_id, //父部门id 69 | ancestors: self.ancestors, //祖级列表 70 | dept_name: self.dept_name.clone(), //部门名称 71 | title: self.dept_name, //部门名称 72 | sort: self.sort, //显示顺序 73 | leader: self.leader, //负责人 74 | phone: self.phone, //联系电话 75 | email: self.email, //邮箱 76 | status: self.status, //部状态(0:停用,1:正常) 77 | create_time: self.create_time, //创建时间 78 | update_time: self.update_time, //修改时间 79 | } 80 | } 81 | } 82 | /* 83 | *根据id查询部门 84 | *author:刘飞华 85 | *date:2024/12/25 10:01:11 86 | */ 87 | impl_select!(Dept{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_dept"); 88 | 89 | /* 90 | *根据部门名称查询部门 91 | *author:刘飞华 92 | *date:2024/12/25 10:01:11 93 | */ 94 | impl_select!(Dept{select_by_dept_name(dept_name:&str, parent_id:i64) -> Option => "`where dept_name = #{dept_name} and parent_id = #{parent_id} limit 1`"}, "sys_dept"); 95 | 96 | /* 97 | *分页查询部门 98 | *author:刘飞华 99 | *date:2024/12/25 10:01:11 100 | */ 101 | impl_select_page!(Dept{select_page() =>" 102 | if !sql.contains('count'): 103 | order by create_time desc" 104 | },"sys_dept"); 105 | 106 | /* 107 | *根据条件分页查询部门 108 | *author:刘飞华 109 | *date:2024/12/25 10:01:11 110 | */ 111 | impl_select!(Dept{select_page_dept_list(req:&QueryDeptListReq) =>" 112 | where 1=1 113 | if req.deptName != null && req.deptName != '': 114 | ` and dept_name = #{req.deptName} ` 115 | if req.status != 2: 116 | ` and status = #{req.status} ` 117 | if !sql.contains('count'): 118 | ` order by sort" 119 | },"sys_dept"); 120 | 121 | /* 122 | *根据部门id查询是否有下级部门 123 | *author:刘飞华 124 | *date:2024/12/12 14:41:44 125 | */ 126 | #[sql("select count(*) from sys_dept where status = 1 and del_flag = '1' and find_in_set(?, ancestors)")] 127 | pub async fn select_normal_children_dept_by_id(rb: &RBatis, id: &i64) -> rbatis::Result { 128 | impled!() 129 | } 130 | 131 | /* 132 | *根据父部门id查询下级部门数量 133 | *author:刘飞华 134 | *date:2024/12/12 14:41:44 135 | */ 136 | #[sql("select count(1) from sys_dept where del_flag = '1' and parent_id = ?")] 137 | pub async fn select_dept_count(rb: &RBatis, id: &i64) -> rbatis::Result { 138 | impled!() 139 | } 140 | 141 | /* 142 | *查询部门是否存在用户 143 | *author:刘飞华 144 | *date:2024/12/12 14:41:44 145 | */ 146 | #[sql("select count(1) from sys_user where dept_id = ? and del_flag = '1'")] 147 | pub async fn check_dept_exist_user(rb: &RBatis, id: &i64) -> rbatis::Result { 148 | impled!() 149 | } 150 | 151 | /* 152 | * 描述:根据部门id查询是所有下级部门 153 | * author:刘飞华 154 | * date:2025/1/6 11:29 155 | */ 156 | #[sql("select * from sys_dept where find_in_set(?, ancestors)")] 157 | pub async fn select_children_dept_by_id(rb: &RBatis, id: &i64) -> rbatis::Result> { 158 | impled!() 159 | } 160 | -------------------------------------------------------------------------------- /src/model/system/sys_menu_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use crate::rbatis::rbatis_codegen::IntoSql; 5 | use crate::vo::system::sys_menu_vo::{MenuReq, MenuResp}; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use rbatis::RBatis; 8 | use serde::{Deserialize, Serialize}; 9 | /* 10 | *菜单信息 11 | *author:刘飞华 12 | *date:2024/12/12 14:41:44 13 | */ 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct Menu { 16 | pub id: Option, //主键 17 | pub menu_name: String, //菜单名称 18 | pub menu_type: i8, //菜单类型(1:目录 2:菜单 3:按钮) 19 | pub visible: i8, //菜单状态(0:隐藏, 显示:1) 20 | pub status: i8, //状态(1:正常,0:禁用) 21 | pub sort: i32, //排序 22 | pub parent_id: Option, //父ID 23 | pub menu_url: Option, //路由路径 24 | pub api_url: Option, //接口URL 25 | pub menu_icon: Option, //菜单图标 26 | pub remark: Option, //备注 27 | pub create_time: Option, //创建时间 28 | pub update_time: Option, //修改时间 29 | } 30 | 31 | /* 32 | *菜单信息基本操作 33 | *author:刘飞华 34 | *date:2024/12/12 14:41:44 35 | */ 36 | rbatis::crud!(Menu {}, "sys_menu"); 37 | 38 | impl From for Menu { 39 | fn from(item: MenuReq) -> Self { 40 | let mut model = Menu { 41 | id: item.id, //主键 42 | menu_name: item.menu_name, //菜单名称 43 | menu_type: item.menu_type, //菜单类型(1:目录 2:菜单 3:按钮) 44 | visible: item.visible, //菜单状态(0:隐藏, 显示:1) 45 | status: item.status, //状态(1:正常,0:禁用) 46 | sort: item.sort, //排序 47 | parent_id: item.parent_id, //父ID 48 | menu_url: item.menu_url, //路由路径 49 | api_url: item.api_url, //接口URL 50 | menu_icon: item.menu_icon, //菜单图标 51 | remark: item.remark, //备注 52 | create_time: None, //创建时间 53 | update_time: None, //修改时间 54 | }; 55 | if let None = item.id { 56 | model.create_time = Some(DateTime::now()); 57 | } else { 58 | model.update_time = Some(DateTime::now()); 59 | } 60 | model 61 | } 62 | } 63 | 64 | impl Into for Menu { 65 | fn into(self) -> MenuResp { 66 | MenuResp { 67 | id: self.id, //主键 68 | menu_name: self.menu_name, //菜单名称 69 | menu_type: self.menu_type, //菜单类型(1:目录 2:菜单 3:按钮) 70 | visible: self.visible, //菜单状态(0:隐藏, 显示:1) 71 | status: self.status, //状态(1:正常,0:禁用) 72 | sort: self.sort, //排序 73 | parent_id: self.parent_id, //父ID 74 | menu_url: self.menu_url, //路由路径 75 | api_url: self.api_url, //接口URL 76 | menu_icon: self.menu_icon, //菜单图标 77 | remark: self.remark, //备注 78 | create_time: self.create_time, //创建时间 79 | update_time: self.update_time, //修改时间 80 | } 81 | } 82 | } 83 | 84 | /* 85 | *根据id查询菜单信息 86 | *author:刘飞华 87 | *date:2024/12/12 14:41:44 88 | */ 89 | impl_select!(Menu{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_menu"); 90 | 91 | /* 92 | *检查菜单名称的唯一性 93 | *author:刘飞华 94 | *date:2024/12/12 14:41:44 95 | */ 96 | impl_select!(Menu{check_menu_name_unique(menu_name:&str, id: Option) -> Option => " 97 | where menu_name = #{menu_name} 98 | if id != null: 99 | ` and id != #{id} ` 100 | ` limit 1` " 101 | }, "sys_menu"); 102 | 103 | /* 104 | *检查菜单路径的唯一性 105 | *author:刘飞华 106 | *date:2025/01/04 22:24:01 107 | */ 108 | impl_select!(Menu{check_menu_url_unique(menu_url:&str, id: Option) -> Option => " 109 | where menu_url = #{menu_url} 110 | if id != null: 111 | ` and id != #{id} ` 112 | ` limit 1` " 113 | }, "sys_menu"); 114 | 115 | /* 116 | *根据ids查询菜单信息 117 | *author:刘飞华 118 | *date:2024/12/12 14:41:44 119 | */ 120 | impl_select!(Menu{select_by_ids(ids:&[i64]) -> Vec => "`where status = 1 and id in ${ids.sql()} order by sort asc`"}, "sys_menu"); 121 | 122 | /* 123 | *查询菜单数量 124 | *author:刘飞华 125 | *date:2024/12/25 10:01:11 126 | */ 127 | #[sql("select count(1) from sys_menu where parent_id= ?")] 128 | pub async fn select_count_menu_by_parent_id(rb: &RBatis, parent_id: &i64) -> rbatis::Result { 129 | impled!() 130 | } 131 | 132 | /* 133 | *查询菜单信息(排除按钮) 134 | *author:刘飞华 135 | *date:2025/01/04 22:24:01 136 | */ 137 | impl_select!(Menu{select_menu_list() -> Vec => "`where menu_type != 3 and status = 1`"}, "sys_menu"); 138 | 139 | /* 140 | *查询菜单列表 141 | *author:刘飞华 142 | *date:2024/12/25 10:01:11 143 | */ 144 | impl_select!(Menu{query_sys_menu_list(menu_name:Option, parent_id: Option, status:Option) =>" 145 | where menu_type != 3 146 | if menu_name != '' && menu_name != null: 147 | ` and menu_name like concat('%', #{menu_name}, '%') ` 148 | if parent_id != 0 && parent_id != null: 149 | ` and parent_id = #{parent_id} ` 150 | if status != 2 && status != null: 151 | ` and status = #{status} ` 152 | if !sql.contains('count'): 153 | ` order by sort asc `" 154 | },"sys_menu"); 155 | 156 | /* 157 | *查询菜单资源 158 | *author:刘飞华 159 | *date:2024/12/25 10:01:11 160 | */ 161 | impl_select_page!(Menu{query_sys_menu_resource_list(menu_name:Option, parent_id: Option, status:Option) =>" 162 | where menu_type = 3 163 | if menu_name != '' && menu_name != null: 164 | ` and menu_name like concat('%', #{menu_name}, '%') ` 165 | if parent_id != 0 && parent_id != null: 166 | ` and parent_id = #{parent_id} ` 167 | if status != 2 && status != null: 168 | ` and status = #{status} ` 169 | if !sql.contains('count'): 170 | ` order by sort asc `" 171 | },"sys_menu"); 172 | -------------------------------------------------------------------------------- /src/handler/system/sys_menu_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 3 | use crate::model::system::sys_menu_model::{select_count_menu_by_parent_id, Menu}; 4 | use crate::model::system::sys_role_menu_model::select_count_menu_by_menu_id; 5 | use crate::vo::system::sys_menu_vo::*; 6 | use crate::AppState; 7 | use axum::extract::State; 8 | use axum::response::IntoResponse; 9 | use axum::Json; 10 | use log::info; 11 | use rbatis::PageRequest; 12 | use rbs::value; 13 | use std::sync::Arc; 14 | /* 15 | *添加菜单信息 16 | *author:刘飞华 17 | *date:2024/12/12 14:41:44 18 | */ 19 | pub async fn add_sys_menu(State(state): State>, Json(mut item): Json) -> impl IntoResponse { 20 | info!("add sys_menu params: {:?}", &item); 21 | let rb = &state.batis; 22 | 23 | if Menu::check_menu_name_unique(rb, &item.menu_name, None).await?.is_some() { 24 | return Err(AppError::BusinessError("菜单名称已存在")); 25 | } 26 | 27 | if let Some(url) = item.menu_url.clone() { 28 | if url != "".to_string() { 29 | if Menu::check_menu_url_unique(rb, &url, None).await?.is_some() { 30 | return Err(AppError::BusinessError("路由路径已存在")); 31 | } 32 | } 33 | } 34 | 35 | item.id = None; 36 | Menu::insert(rb, &Menu::from(item)).await.map(|_| ok_result())? 37 | } 38 | 39 | /* 40 | *删除菜单信息 41 | *author:刘飞华 42 | *date:2024/12/12 14:41:44 43 | */ 44 | pub async fn delete_sys_menu(State(state): State>, Json(item): Json) -> impl IntoResponse { 45 | info!("delete sys_menu params: {:?}", &item); 46 | let rb = &state.batis; 47 | 48 | let ids = item.ids; 49 | for x in ids.clone() { 50 | if select_count_menu_by_parent_id(rb, &x).await? > 0 { 51 | return Err(AppError::BusinessError("存在子菜单,不允许删除")); 52 | } 53 | 54 | if select_count_menu_by_menu_id(rb, &x).await? > 0 { 55 | return Err(AppError::BusinessError("菜单已分配,不允许删除")); 56 | } 57 | } 58 | 59 | Menu::delete_by_map(rb, value! {"id": &ids}).await.map(|_| ok_result())? 60 | } 61 | 62 | /* 63 | *更新菜单信息 64 | *author:刘飞华 65 | *date:2024/12/12 14:41:44 66 | */ 67 | pub async fn update_sys_menu(State(state): State>, Json(item): Json) -> impl IntoResponse { 68 | info!("update sys_menu params: {:?}", &item); 69 | let rb = &state.batis; 70 | 71 | let id = item.id; 72 | 73 | if id.is_none() { 74 | return Err(AppError::BusinessError("主键不能为空")); 75 | } 76 | 77 | if Menu::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 78 | return Err(AppError::BusinessError("菜单信息不存在")); 79 | } 80 | 81 | if Menu::check_menu_name_unique(rb, &item.menu_name, id).await?.is_some() { 82 | return Err(AppError::BusinessError("菜单名称已存在")); 83 | } 84 | 85 | if let Some(url) = item.menu_url.clone() { 86 | if url != "".to_string() { 87 | if Menu::check_menu_url_unique(rb, &url, id).await?.is_some() { 88 | return Err(AppError::BusinessError("路由路径已存在")); 89 | } 90 | } 91 | } 92 | 93 | Menu::update_by_map(rb, &Menu::from(item), value! {"id": &id}).await.map(|_| ok_result())? 94 | } 95 | 96 | /* 97 | *更新菜单信息状态 98 | *author:刘飞华 99 | *date:2024/12/12 14:41:44 100 | */ 101 | pub async fn update_sys_menu_status(State(state): State>, Json(item): Json) -> impl IntoResponse { 102 | info!("update sys_menu_status params: {:?}", &item); 103 | let rb = &state.batis; 104 | 105 | let ids = item.ids.iter().map(|_| "?").collect::>().join(", "); 106 | let update_sql = format!("update sys_menu set status = ? where id in ({})", ids); 107 | 108 | let mut param = vec![value!(item.status)]; 109 | param.extend(item.ids.iter().map(|&id| value!(id))); 110 | rb.exec(&update_sql, param).await.map(|_| ok_result())? 111 | } 112 | 113 | /* 114 | *查询菜单信息详情 115 | *author:刘飞华 116 | *date:2024/12/12 14:41:44 117 | */ 118 | pub async fn query_sys_menu_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 119 | info!("query sys_menu_detail params: {:?}", &item); 120 | let rb = &state.batis; 121 | 122 | Menu::select_by_id(rb, &item.id).await?.map_or_else( 123 | || Err(AppError::BusinessError("菜单信息不存在")), 124 | |x| { 125 | let data: MenuResp = x.into(); 126 | ok_result_data(data) 127 | }, 128 | ) 129 | } 130 | 131 | /* 132 | *查询菜单信息列表 133 | *author:刘飞华 134 | *date:2024/12/12 14:41:44 135 | */ 136 | pub async fn query_sys_menu_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 137 | info!("query sys_menu_list params: {:?}", &item); 138 | let rb = &state.batis; 139 | 140 | let menu_name = item.menu_name; 141 | let parent_id = item.parent_id; 142 | let status = item.status; 143 | 144 | Menu::query_sys_menu_list(rb, menu_name, parent_id, status) 145 | .await 146 | .map(|x| ok_result_data(x.into_iter().map(|x| x.into()).collect::>()))? 147 | } 148 | 149 | /* 150 | *查询菜单信息(排除按钮) 151 | *author:刘飞华 152 | *date:2024/12/12 14:41:44 153 | */ 154 | pub async fn query_sys_menu_list_simple(State(state): State>) -> impl IntoResponse { 155 | let rb = &state.batis; 156 | 157 | Menu::select_menu_list(rb) 158 | .await 159 | .map(|x| ok_result_data(x.into_iter().map(|x| MenuSimpleResp::from(x)).collect::>()))? 160 | } 161 | 162 | /* 163 | *查询菜单信息列表 164 | *author:刘飞华 165 | *date:2024/12/12 14:41:44 166 | */ 167 | pub async fn query_sys_menu_resource_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 168 | info!("query sys_menu_list params: {:?}", &item); 169 | let rb = &state.batis; 170 | 171 | let menu_name = item.menu_name; 172 | let parent_id = item.parent_id; 173 | let status = item.status; 174 | 175 | let page = &PageRequest::new(item.page_no, item.page_size); 176 | 177 | Menu::query_sys_menu_resource_list(rb, page, menu_name, parent_id, status) 178 | .await 179 | .map(|x| ok_result_page(x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 180 | } 181 | -------------------------------------------------------------------------------- /src/handler/system/sys_dept_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppError; 2 | use crate::common::result::{ok_result, ok_result_data}; 3 | use crate::model::system::sys_dept_model::{check_dept_exist_user, select_children_dept_by_id, select_dept_count, select_normal_children_dept_by_id, Dept}; 4 | use crate::vo::system::sys_dept_vo::*; 5 | use crate::AppState; 6 | use axum::extract::State; 7 | use axum::response::IntoResponse; 8 | use axum::Json; 9 | use axum_valid::Valid; 10 | use log::info; 11 | use rbatis::rbatis_codegen::ops::AsProxy; 12 | use rbatis::rbdc::DateTime; 13 | use rbs::value; 14 | use std::sync::Arc; 15 | use validator::Validate; 16 | /* 17 | *添加部门表 18 | *author:刘飞华 19 | *date:2024/12/25 11:36:48 20 | */ 21 | pub async fn add_sys_dept(State(state): State>, Valid(Json(item)): Valid>) -> impl IntoResponse { 22 | info!("add sys_dept params: {:?}", &item); 23 | let rb = &state.batis; 24 | 25 | if Dept::select_by_dept_name(rb, &item.dept_name, item.parent_id).await?.is_some() { 26 | return Err(AppError::BusinessError("部门名称已存在")); 27 | } 28 | 29 | match Dept::select_by_id(rb, &item.parent_id).await? { 30 | None => Err(AppError::BusinessError("添加失败,上级部门不存在")), 31 | Some(dept) => { 32 | if dept.status == 0 { 33 | return Err(AppError::BusinessError("部门停用,不允许添加")); 34 | } 35 | let ancestors = format!("{},{}", dept.ancestors.unwrap_or_default(), &item.parent_id); 36 | let mut sys_dept = Dept::from(item); 37 | sys_dept.ancestors = Some(ancestors); 38 | if let Err(e) = sys_dept.validate() { 39 | return Err(AppError::validation_error(&e)); 40 | } 41 | Dept::insert(rb, &sys_dept).await.map(|_| ok_result())? 42 | } 43 | } 44 | } 45 | 46 | /* 47 | *删除部门表 48 | *author:刘飞华 49 | *date:2024/12/25 11:36:48 50 | */ 51 | pub async fn delete_sys_dept(State(state): State>, Json(item): Json) -> impl IntoResponse { 52 | info!("delete sys_dept params: {:?}", &item); 53 | let rb = &state.batis; 54 | 55 | if select_dept_count(rb, &item.id).await? > 0 { 56 | return Err(AppError::BusinessError("存在下级部门,不允许删除")); 57 | } 58 | 59 | if check_dept_exist_user(rb, &item.id).await? > 0 { 60 | return Err(AppError::BusinessError("部门存在用户,不允许删除")); 61 | } 62 | 63 | Dept::delete_by_map(rb, value! {"id": &item.id}).await.map(|_| ok_result())? 64 | } 65 | 66 | /* 67 | *更新部门表 68 | *author:刘飞华 69 | *date:2024/12/25 11:36:48 70 | */ 71 | pub async fn update_sys_dept(State(state): State>, Valid(Json(mut item)): Valid>) -> impl IntoResponse { 72 | info!("update sys_dept params: {:?}", &item); 73 | let rb = &state.batis; 74 | 75 | let id = item.id; 76 | 77 | if id.is_none() { 78 | return Err(AppError::BusinessError("主键不能为空")); 79 | } 80 | 81 | if Some(item.parent_id) == id { 82 | return Err(AppError::BusinessError("上级部门不能是自己")); 83 | } 84 | 85 | let old_ancestors = match Dept::select_by_id(rb, &id.unwrap_or_default()).await? { 86 | None => return Err(AppError::BusinessError("部门不存在")), 87 | Some(dept) => dept.ancestors.unwrap_or_default(), 88 | }; 89 | 90 | let ancestors = match Dept::select_by_id(rb, &item.parent_id).await? { 91 | None => return Err(AppError::BusinessError("上级部门不存在")), 92 | Some(dept) => { 93 | format!("{},{}", dept.ancestors.unwrap_or_default(), &item.parent_id) 94 | } 95 | }; 96 | 97 | if let Some(dept) = Dept::select_by_dept_name(rb, &item.dept_name, item.parent_id).await? { 98 | if dept.id != id { 99 | return Err(AppError::BusinessError("部门名称已存在")); 100 | } 101 | } 102 | 103 | if select_normal_children_dept_by_id(rb, &id.unwrap_or_default()).await? > 0 && item.status == 0 { 104 | return Err(AppError::BusinessError("该部门包含未停用的子部门")); 105 | } 106 | 107 | for mut x in select_children_dept_by_id(rb, &id.unwrap_or_default()).await? { 108 | x.ancestors = Some(x.ancestors.unwrap_or_default().replace(old_ancestors.as_str(), ancestors.as_str())); 109 | Dept::update_by_map(rb, &x, value! {"id": &x.id}).await?; 110 | } 111 | 112 | if item.status == 1 && ancestors != "0" { 113 | let ids = ancestors.split(",").map(|s| s.i64()).collect::>(); 114 | 115 | let update_sql = format!( 116 | "update sys_dept set status = ? ,update_time = ? where id in ({})", 117 | ids.iter().map(|_| "?").collect::>().join(", ") 118 | ); 119 | 120 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 121 | param.extend(ids.iter().map(|&id| value!(id))); 122 | 123 | rb.exec(&update_sql, param).await?; 124 | } 125 | item.ancestors = Some(ancestors.clone()); 126 | 127 | let data = Dept::from(item); 128 | if let Err(e) = data.validate() { 129 | return Err(AppError::validation_error(&e)); 130 | } 131 | Dept::update_by_map(rb, &data, value! {"id": &id}).await.map(|_| ok_result())? 132 | } 133 | 134 | /* 135 | *更新部门表状态 136 | *author:刘飞华 137 | *date:2024/12/25 11:36:48 138 | */ 139 | pub async fn update_sys_dept_status(State(state): State>, Json(item): Json) -> impl IntoResponse { 140 | info!("update sys_dept_status params: {:?}", &item); 141 | let rb = &state.batis; 142 | 143 | let mut ids = vec![item.id]; 144 | if item.status == 1 { 145 | if let Some(x) = Dept::select_by_id(rb, &item.id).await? { 146 | ids.extend(&x.ancestors.unwrap_or_default().split(",").map(|s| s.i64()).collect::>()) 147 | } 148 | } 149 | let update_sql = format!("update sys_dept set status = ? where id in ({})", ids.iter().map(|_| "?").collect::>().join(", ")); 150 | 151 | let mut param = vec![value!(item.status)]; 152 | param.extend(ids.iter().map(|&id| value!(id))); 153 | rb.exec(&update_sql, param).await.map(|_| ok_result())? 154 | } 155 | /* 156 | *查询部门表详情 157 | *author:刘飞华 158 | *date:2024/12/25 11:36:48 159 | */ 160 | pub async fn query_sys_dept_detail(State(state): State>, Json(item): Json) -> impl IntoResponse { 161 | info!("query sys_dept_detail params: {:?}", &item); 162 | let rb = &state.batis; 163 | 164 | Dept::select_by_id(rb, &item.id).await?.map_or_else( 165 | || Err(AppError::BusinessError("部门不存在")), 166 | |x| { 167 | let data: DeptResp = x.into(); 168 | ok_result_data(data) 169 | }, 170 | ) 171 | } 172 | 173 | /* 174 | *查询部门表列表 175 | *author:刘飞华 176 | *date:2024/12/25 11:36:48 177 | */ 178 | pub async fn query_sys_dept_list(State(state): State>, Json(item): Json) -> impl IntoResponse { 179 | info!("query sys_dept_list params: {:?}", &item); 180 | let rb = &state.batis; 181 | 182 | Dept::select_page_dept_list(rb, &item) 183 | .await 184 | .map(|x| ok_result_data(x.into_iter().map(|x| x.into()).collect::>()))? 185 | } 186 | -------------------------------------------------------------------------------- /src/model/system/sys_user_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use crate::vo::system::sys_user_vo::{QueryUserListReq, UserReq, UserResp}; 5 | use rbatis::executor::Executor; 6 | use rbatis::rbdc::datetime::DateTime; 7 | use rbatis::rbdc::Error; 8 | use serde::{Deserialize, Serialize}; 9 | /* 10 | *用户信息 11 | *author:刘飞华 12 | *date:2024/12/12 14:41:44 13 | */ 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct User { 16 | pub id: Option, //主键 17 | pub mobile: String, //手机 18 | pub user_name: String, //用户账号 19 | pub nick_name: String, //用户昵称 20 | pub user_type: Option, //用户类型(00系统用户) 21 | pub email: String, //用户邮箱 22 | pub avatar: Option, //头像路径 23 | pub password: String, //密码 24 | pub status: i8, //状态(1:正常,0:禁用) 25 | pub dept_id: i64, //部门ID 26 | pub login_ip: String, //最后登录IP 27 | pub login_date: Option, //最后登录时间 28 | pub login_browser: String, //浏览器类型 29 | pub login_os: String, //操作系统 30 | pub pwd_update_date: Option, //密码最后更新时间 31 | pub remark: Option, //备注 32 | pub del_flag: i8, //删除标志(0代表删除 1代表存在) 33 | pub create_time: Option, //创建时间 34 | pub update_time: Option, //修改时间 35 | } 36 | 37 | /* 38 | *用户信息基本操作 39 | *author:刘飞华 40 | *date:2024/12/12 14:41:44 41 | */ 42 | rbatis::crud!(User {}, "sys_user"); 43 | 44 | impl From for User { 45 | fn from(item: UserReq) -> Self { 46 | let mut model = User { 47 | id: None, //主键 48 | mobile: item.mobile, //手机 49 | user_name: item.user_name, //用户账号 50 | nick_name: item.nick_name, //用户昵称 51 | user_type: Some("01".to_string()), //用户类型(00系统用户) 52 | email: item.email, //用户邮箱 53 | avatar: item.avatar, //头像路径 54 | password: item.password.unwrap_or_default(), //密码 55 | status: item.status, //状态(1:正常,0:禁用) 56 | dept_id: item.dept_id, //部门ID 57 | login_ip: "".to_string(), //最后登录IP 58 | login_date: None, //最后登录时间 59 | login_browser: "".to_string(), //浏览器类型 60 | login_os: "".to_string(), //操作系统 61 | pwd_update_date: None, //密码最后更新时间 62 | remark: item.remark, //备注 63 | del_flag: 1, //删除标志(0代表删除 1代表存在) 64 | create_time: None, //创建时间 65 | update_time: None, //修改时间 66 | }; 67 | if let None = item.id { 68 | model.create_time = Some(DateTime::now()); 69 | } else { 70 | model.update_time = Some(DateTime::now()); 71 | } 72 | model 73 | } 74 | } 75 | 76 | impl Into for User { 77 | fn into(self) -> UserResp { 78 | UserResp { 79 | id: self.id, //主键 80 | mobile: self.mobile, //手机 81 | user_name: self.user_name, //姓名 82 | nick_name: self.nick_name, //用户昵称 83 | user_type: self.user_type.unwrap_or_default(), //用户类型(00系统用户) 84 | email: self.email, //用户邮箱 85 | avatar: self.avatar, //头像路径 86 | status: self.status, //状态(1:正常,0:禁用) 87 | dept_id: self.dept_id, //部门ID 88 | login_ip: self.login_ip, //最后登录IP 89 | login_date: self.login_date, //最后登录时间 90 | login_browser: self.login_browser, //浏览器类型 91 | login_os: self.login_os, //操作系统 92 | pwd_update_date: self.pwd_update_date, //密码最后更新时间 93 | remark: self.remark, //备注 94 | create_time: self.create_time, //创建时间 95 | update_time: self.update_time, //修改时间 96 | dept_info: None, 97 | post_ids: None, 98 | } 99 | } 100 | } 101 | /* 102 | *根据id查询用户信息 103 | *author:刘飞华 104 | *date:2024/12/12 14:41:44 105 | */ 106 | impl_select!(User{select_by_id(id:i64) -> Option => "`where id = #{id} limit 1`"}, "sys_user"); 107 | 108 | /* 109 | *根据account查询用户信息 110 | *author:刘飞华 111 | *date:2025/09/26 13:42:44 112 | */ 113 | impl_select!(User{select_by_account(account:&str) -> Option => "`where user_name = #{account} or mobile = #{account} or email = #{account} limit 1`"},"sys_user"); 114 | 115 | /* 116 | *根据mobile查询用户信息 117 | *author:刘飞华 118 | *date:2024/12/12 14:41:44 119 | */ 120 | impl_select!(User{select_by_mobile(mobile:&str) -> Option => "`where mobile = #{mobile} limit 1`"},"sys_user"); 121 | 122 | /* 123 | *根据user_name查询用户信息 124 | *author:刘飞华 125 | *date:2024/12/12 14:41:44 126 | */ 127 | impl_select!(User{select_by_user_name(user_name:&str) -> Option => "`where user_name = #{user_name} limit 1`"}, "sys_user"); 128 | 129 | /* 130 | *根据email查询用户信息 131 | *author:刘飞华 132 | *date:2024/12/12 14:41:44 133 | */ 134 | impl_select!(User{select_by_email(email:&str) -> Option => "`where email = #{email} limit 1`"}, "sys_user"); 135 | 136 | /* 137 | *分页查询用户信息 138 | *author:刘飞华 139 | *date:2024/12/12 14:41:44 140 | */ 141 | impl_select_page!(User{select_page() =>" 142 | if !sql.contains('count'): 143 | order by create_time desc" 144 | },"sys_user"); 145 | 146 | /* 147 | *根据条件分页查询用户信息 148 | *author:刘飞华 149 | *date:2024/12/12 14:41:44 150 | */ 151 | impl_select_page!(User{select_sys_user_list(req:&QueryUserListReq) =>" 152 | where 1=1 153 | if req.mobile != null && req.mobile != '': 154 | ` and mobile like concat('%', #{req.mobile}, '%') ` 155 | if req.userName != null && req.userName != '': 156 | ` and user_name like concat('%', #{req.userName}, '%') ` 157 | if req.status != 2: 158 | ` and status = #{req.status} ` 159 | if req.deptId != 0: 160 | ` and (dept_id = #{req.deptId} OR dept_id IN (SELECT id FROM sys_dept WHERE find_in_set(#{req.deptId}, ancestors))) ` 161 | if !sql.contains('count'): 162 | ` order by create_time desc `"},"sys_user"); 163 | 164 | /* 165 | *根据条件分页查询已配用户角色列表 166 | *author:刘飞华 167 | *date:2024/12/12 14:41:44 168 | */ 169 | #[py_sql( 170 | "`select u.* from sys_user u left join sys_user_role ur on u.id = ur.user_id where u.del_flag = 1 and ur.role_id = #{role_id} ` 171 | if mobile != '': 172 | ` and u.mobile = #{mobile} ` 173 | if user_name != '': 174 | ` and u.user_name = #{user_name} ` 175 | limit #{page_no},#{page_size}` " 176 | )] 177 | async fn select_allocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str, page_no: u64, page_size: u64) -> Result, Error> { 178 | impled!() 179 | } 180 | 181 | /* 182 | * 描述:根据条件分页查询已配用户角色数量 183 | * author:刘飞华 184 | * date:2025/1/6 16:13 185 | */ 186 | #[py_sql( 187 | "`select count(1) from sys_user u left join sys_user_role ur on u.id = ur.user_id where u.del_flag = 1 and ur.role_id = #{role_id} ` 188 | if mobile != '': 189 | ` and u.mobile = #{mobile} ` 190 | if user_name != '': 191 | ` and u.user_name = #{user_name} `" 192 | )] 193 | async fn count_allocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str) -> Result { 194 | impled!() 195 | } 196 | 197 | /* 198 | * 描述:根据条件分页查询未分配用户角色列表 199 | * author:刘飞华 200 | * date:2025/1/6 16:17 201 | */ 202 | #[py_sql( 203 | "`select u.* from sys_user u left join sys_user_role ur on u.id = ur.user_id where u.del_flag = 1 and (ur.role_id != #{role_id} or ur.role_id is null) ` 204 | if mobile != '': 205 | ` and u.mobile = #{mobile} ` 206 | if user_name != '': 207 | ` and u.user_name = #{user_name} ` 208 | limit #{page_no},#{page_size}` " 209 | )] 210 | pub async fn select_unallocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str, page_no: u64, page_size: u64) -> rbatis::Result> { 211 | impled!() 212 | } 213 | 214 | /* 215 | * 描述:根据条件分页查询未分配用户角色数量 216 | * author:刘飞华 217 | * date:2025/1/6 16:17 218 | */ 219 | #[py_sql( 220 | "`select count(1) from sys_user u left join sys_user_role ur on u.id = ur.user_id where u.del_flag = 1 and (ur.role_id != #{role_id} or ur.role_id is null) ` 221 | if mobile != '': 222 | ` and u.mobile = #{mobile} ` 223 | if user_name != '': 224 | ` and u.user_name = #{user_name} `" 225 | )] 226 | pub async fn count_unallocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str) -> rbatis::Result { 227 | impled!() 228 | } 229 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2023] [name of copyright feihua] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------