├── docs ├── sql │ ├── other │ │ └── test.sql │ └── system │ │ ├── sys_role_dept.sql │ │ ├── sys_role_menu.sql │ │ ├── sys_user_role.sql │ │ ├── sys_user_post.sql │ │ ├── sys_dict_type.sql │ │ ├── sys_notice.sql │ │ ├── sys_post.sql │ │ ├── sys_oper_log.sql │ │ ├── sys_role.sql │ │ ├── sys_login_log.sql │ │ ├── sys_user.sql │ │ ├── sys_dict_data.sql │ │ └── sys_dept.sql ├── images │ ├── dept.jpg │ ├── dict.jpg │ ├── menu.jpg │ ├── notice.jpg │ ├── post.jpg │ ├── role.jpg │ ├── user.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_role.http │ └── sys_user.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_dict_type_model.rs │ │ ├── sys_login_log_model.rs │ │ ├── sys_post_model.rs │ │ ├── sys_role_model.rs │ │ ├── sys_operate_log_model.rs │ │ ├── sys_notice_model.rs │ │ ├── sys_dict_data_model.rs │ │ ├── sys_dept_model.rs │ │ ├── sys_menu_model.rs │ │ └── sys_user_model.rs ├── vo │ ├── other │ │ └── mod.rs │ ├── mod.rs │ └── system │ │ ├── mod.rs │ │ ├── sys_login_log_vo.rs │ │ ├── sys_dict_type_vo.rs │ │ ├── sys_post_vo.rs │ │ ├── sys_notice_vo.rs │ │ ├── sys_dept_vo.rs │ │ ├── sys_operate_log_vo.rs │ │ ├── sys_dict_data_vo.rs │ │ ├── sys_menu_vo.rs │ │ ├── sys_role_vo.rs │ │ └── sys_user_vo.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 ├── routes │ ├── other │ │ └── 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 │ └── mod.rs ├── middleware │ ├── mod.rs │ └── auth.rs ├── common │ ├── mod.rs │ ├── error.rs │ └── result.rs ├── utils │ ├── mod.rs │ ├── redis_util.rs │ ├── time_util.rs │ ├── user_agent_util.rs │ └── jwt_util.rs ├── config │ └── log4rs.yaml └── main.rs ├── .gitignore ├── .rustfmt.toml ├── .vscode ├── tasks.json └── launch.json ├── 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/routes/other/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/middleware/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod auth; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | log 3 | *.log 4 | .idea -------------------------------------------------------------------------------- /src/vo/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod other; 2 | pub mod system; 3 | -------------------------------------------------------------------------------- /.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 | pub mod system; 3 | -------------------------------------------------------------------------------- /docs/images/dept.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/dept.jpg -------------------------------------------------------------------------------- /docs/images/dict.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/dict.jpg -------------------------------------------------------------------------------- /docs/images/menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/menu.jpg -------------------------------------------------------------------------------- /docs/images/notice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/notice.jpg -------------------------------------------------------------------------------- /docs/images/post.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/post.jpg -------------------------------------------------------------------------------- /docs/images/role.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/role.jpg -------------------------------------------------------------------------------- /docs/images/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/user.jpg -------------------------------------------------------------------------------- /docs/images/dict_data.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/dict_data.jpg -------------------------------------------------------------------------------- /docs/images/login_log.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/login_log.jpg -------------------------------------------------------------------------------- /docs/images/role_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/role_menu.jpg -------------------------------------------------------------------------------- /docs/images/role_user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/role_user.jpg -------------------------------------------------------------------------------- /docs/images/user_role.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feihua/salvo-admin/HEAD/docs/images/user_role.jpg -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod jwt_util; 2 | pub mod redis_util; 3 | pub mod time_util; 4 | pub mod user_agent_util; 5 | -------------------------------------------------------------------------------- /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:8100" 4 | }, 5 | "pro": { 6 | "host": "http://175.178.110.17:8100" 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 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "build", 7 | "problemMatcher": [ 8 | "$rustc" 9 | ], 10 | "label": "cargo build" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /docs/sql/system/sys_role_dept.sql: -------------------------------------------------------------------------------- 1 | create table sys_role_dept 2 | ( 3 | id bigint auto_increment comment '主键' 4 | primary key, 5 | role_id bigint not null comment '角色id', 6 | dept_id bigint not null comment '部门id' 7 | ) comment = '角色和部门关联表'; 8 | -------------------------------------------------------------------------------- /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/salvo-admin /app/ 10 | 11 | CMD ["./salvo-admin"] -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | # 配置服务器的基本信息 2 | [server] 3 | # 定义服务器监听的端口和主机地址 4 | addr = "0.0.0.0:8100" 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" 12 | [jwt] 13 | secret = "123456" 14 | -------------------------------------------------------------------------------- /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/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_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 | ) 8 | comment '菜单角色关联表'; 9 | -------------------------------------------------------------------------------- /src/routes/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/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_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 | ) 8 | comment '角色用户关联表'; 9 | INSERT INTO sys_user_role (user_id, role_id) VALUES (1, 1) -------------------------------------------------------------------------------- /docs/sql/system/sys_user_post.sql: -------------------------------------------------------------------------------- 1 | create table sys_user_post 2 | ( 3 | id bigint auto_increment comment '主键' 4 | primary key, 5 | user_id bigint not null comment '用户id', 6 | post_id bigint not null comment '岗位id' 7 | ) comment = '用户与岗位关联表'; 8 | 9 | 10 | insert into sys_user_post(user_id, post_id) values ('1', '1'); 11 | insert into sys_user_post(user_id, post_id) values ('2', '2'); 12 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Rust", 6 | "type": "lldb", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/target/debug/salvo-admin", 9 | "args": [], 10 | "cwd": "${workspaceFolder}", 11 | "preLaunchTask": "cargo build" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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/routes/system/sys_login_log_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_login_log_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建系统访问记录路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_login_log_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/loginLog/deleteLoginLog").post(delete_sys_login_log)) 11 | .push(Router::new().path("/system/loginLog/queryLoginLogDetail").post(query_sys_login_log_detail)) 12 | .push(Router::new().path("/system/loginLog/queryLoginLogList").post(query_sys_login_log_list)) 13 | //记得在main.rs中的route()函数中添加构建系统访问记录路由build_sys_login_log_route() 14 | } 15 | -------------------------------------------------------------------------------- /src/routes/system/sys_operate_log_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_operate_log_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建操作日志记录路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_operate_log_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/operateLog/deleteOperateLog").post(delete_sys_operate_log)) 11 | .push(Router::new().path("/system/operateLog/queryOperateLogDetail").post(query_sys_operate_log_detail)) 12 | .push(Router::new().path("/system/operateLog/queryOperateLogList").post(query_sys_operate_log_list)) 13 | //记得在main.rs中的route()函数中添加构建操作日志记录路由build_sys_operate_log_route() 14 | } 15 | -------------------------------------------------------------------------------- /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/salvo-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-salvo-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 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | script 3 | # 该函数用于构建并部署一个名为 `salvo-admin` 的 Docker 容器。 4 | # 它首先使用 Cargo 构建项目,然后停止并删除现有的容器和镜像, 5 | # 最后构建新的 Docker 镜像并运行容器。 6 | 7 | # 使用 Cargo 构建项目,`-r` 参数表示以 release 模式构建 8 | cargo build -r 9 | 10 | # 停止名为 `salvo-admin` 的 Docker 容器 11 | docker stop salvo-admin 12 | 13 | # 强制删除名为 `salvo-admin` 的 Docker 容器 14 | docker rm -f salvo-admin 15 | 16 | # 强制删除名为 `salvo-admin:v1` 的 Docker 镜像 17 | docker rmi -f salvo-admin:v1 18 | 19 | # 删除所有标记为 `` 的 Docker 镜像 20 | docker rmi -f $(docker images | grep "none" | awk '{print $3}') 21 | 22 | # 使用当前目录下的 `Dockerfile` 构建名为 `salvo-admin:v1` 的 Docker 镜像 23 | docker build -t salvo-admin:v1 -f Dockerfile . 24 | 25 | # 以交互式后台模式运行 `salvo-admin:v1` 镜像,容器命名为 `salvo-admin`,并使用主机网络模式 26 | docker run -itd --net=host --name=salvo-admin salvo-admin:v1 27 | 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/routes/system/sys_dept_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_dept_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建部门表路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_dept_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/dept/addDept").post(add_sys_dept)) 11 | .push(Router::new().path("/system/dept/deleteDept").post(delete_sys_dept)) 12 | .push(Router::new().path("/system/dept/updateDept").post(update_sys_dept)) 13 | .push(Router::new().path("/system/dept/updateDeptStatus").post(update_sys_dept_status)) 14 | .push(Router::new().path("/system/dept/queryDeptDetail").post(query_sys_dept_detail)) 15 | .push(Router::new().path("/system/dept/queryDeptList").post(query_sys_dept_list)) 16 | //记得在main.rs中的route()函数中添加构建部门表路由build_sys_dept_route() 17 | } 18 | -------------------------------------------------------------------------------- /src/routes/system/sys_post_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_post_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建岗位信息表路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_post_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/post/addPost").post(add_sys_post)) 11 | .push(Router::new().path("/system/post/deletePost").post(delete_sys_post)) 12 | .push(Router::new().path("/system/post/updatePost").post(update_sys_post)) 13 | .push(Router::new().path("/system/post/updatePostStatus").post(update_sys_post_status)) 14 | .push(Router::new().path("/system/post/queryPostDetail").post(query_sys_post_detail)) 15 | .push(Router::new().path("/system/post/queryPostList").post(query_sys_post_list)) 16 | //记得在main.rs中的route()函数中添加构建岗位信息表路由build_sys_post_route() 17 | } 18 | -------------------------------------------------------------------------------- /src/routes/system/sys_notice_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_notice_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建通知公告表路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_notice_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/notice/addNotice").post(add_sys_notice)) 11 | .push(Router::new().path("/system/notice/deleteNotice").post(delete_sys_notice)) 12 | .push(Router::new().path("/system/notice/updateNotice").post(update_sys_notice)) 13 | .push(Router::new().path("/system/notice/updateNoticeStatus").post(update_sys_notice_status)) 14 | .push(Router::new().path("/system/notice/queryNoticeDetail").post(query_sys_notice_detail)) 15 | .push(Router::new().path("/system/notice/queryNoticeList").post(query_sys_notice_list)) 16 | //记得在main.rs中的route()函数中添加构建通知公告表路由build_sys_notice_route() 17 | } 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "salvo-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 | salvo = { version = "0.82.0", features = ["affix-state"] } 10 | 11 | tokio = { version = "1", features = ["macros"] } 12 | #tracing = "0.1" 13 | #tracing-subscriber = "0.3" 14 | 15 | serde = { version = "1.0", features = ["derive"] } 16 | 17 | log = "0.4" 18 | log4rs = "1.0" 19 | 20 | rbs = { version = "4.6"} 21 | rbatis = { version = "4.6", features = [] } 22 | #rbatis = { version = "4.0",features = ["debug_mode"]} 23 | rbdc-mysql={version="4.6"} 24 | rbdc-pool-fast={version="4.6"} 25 | 26 | jsonwebtoken = "9.3.0" 27 | 28 | redis = "0.32.3" 29 | deadpool-redis = "0.22.0" 30 | 31 | thiserror = "2.0.3" 32 | 33 | once_cell = "1.18.0" 34 | regex = "1.11.1" 35 | config = "0.15.9" 36 | chrono = "0.4.38" -------------------------------------------------------------------------------- /src/routes/system/sys_dict_data_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_dict_data_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建字典数据表路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_dict_data_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/dictData/addDictData").post(add_sys_dict_data)) 11 | .push(Router::new().path("/system/dictData/deleteDictData").post(delete_sys_dict_data)) 12 | .push(Router::new().path("/system/dictData/updateDictData").post(update_sys_dict_data)) 13 | .push(Router::new().path("/system/dictData/updateDictDataStatus").post(update_sys_dict_data_status)) 14 | .push(Router::new().path("/system/dictData/queryDictDataDetail").post(query_sys_dict_data_detail)) 15 | .push(Router::new().path("/system/dictData/queryDictDataList").post(query_sys_dict_data_list)) 16 | //记得在main.rs中的route()函数中添加构建字典数据表路由build_sys_dict_data_route() 17 | } 18 | -------------------------------------------------------------------------------- /src/routes/system/sys_dict_type_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_dict_type_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建字典类型表路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_dict_type_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/dictType/addDictType").post(add_sys_dict_type)) 11 | .push(Router::new().path("/system/dictType/deleteDictType").post(delete_sys_dict_type)) 12 | .push(Router::new().path("/system/dictType/updateDictType").post(update_sys_dict_type)) 13 | .push(Router::new().path("/system/dictType/updateDictTypeStatus").post(update_sys_dict_type_status)) 14 | .push(Router::new().path("/system/dictType/queryDictTypeDetail").post(query_sys_dict_type_detail)) 15 | .push(Router::new().path("/system/dictType/queryDictTypeList").post(query_sys_dict_type_list)) 16 | //记得在main.rs中的route()函数中添加构建字典类型表路由build_sys_dict_type_route() 17 | } 18 | -------------------------------------------------------------------------------- /docs/sql/system/sys_dict_type.sql: -------------------------------------------------------------------------------- 1 | drop table if exists sys_dict_type; 2 | create table sys_dict_type 3 | ( 4 | id bigint not null auto_increment comment '字典主键', 5 | dict_name varchar(100) default '' not null comment '字典名称', 6 | dict_type varchar(100) default '' not null comment '字典类型', 7 | status tinyint default 0 not null comment '状态(0:停用,1:正常)', 8 | remark varchar(500) default '' not null comment '备注', 9 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 10 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间', 11 | primary key (id), 12 | unique (dict_type) 13 | ) comment = '字典类型表'; 14 | 15 | INSERT INTO sys_dict_type (dict_name, dict_type, status, remark) VALUES ('用户性别', 'sys_user_sex', 1, '用户性别列表'); 16 | INSERT INTO sys_dict_type (dict_name, dict_type, status, remark) VALUES ('通知类型', 'sys_notice_type', 1, '通知类型列表'); 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # salvo-admin 2 | 3 | 是基于salvo和rbatis的rbac权限管理系统 4 | 5 | # 预览地址 6 | 7 | # 前端项目 8 | 9 | ``` 10 | https://github.com/feihua/antd-admin.git 11 | ``` 12 | 13 | 14 | # 本地启动 15 | 16 | ``` 17 | 1.创建数据库并导入sql脚本 18 | 2.修改comfig.toml中的数据库连接地址 19 | 3.启动 cargo run .\src\main.rs 20 | 21 | ``` 22 | 23 | # 系统截图 24 | 25 | ## 用户界面 26 | 27 | ![user](docs/images/user.jpg) 28 | 29 | ## 角色分配界面 30 | 31 | ![user-role](docs/images/user_role.jpg) 32 | 33 | ## 角色界面 34 | 35 | ![role](docs/images/role.jpg) 36 | 37 | ## 角色用户界面 38 | 39 | ![role](docs/images/role_user.jpg) 40 | 41 | ## 菜单分配界面 42 | 43 | ![role-menu](docs/images/role_menu.jpg) 44 | 45 | ## 菜单界面 46 | 47 | ![menu](docs/images/menu.jpg) 48 | 49 | ## 部门界面 50 | 51 | ![menu](docs/images/dept.jpg) 52 | 53 | ## 岗位界面 54 | 55 | ![menu](docs/images/post.jpg) 56 | 57 | ## 字典界面 58 | 59 | ![menu](docs/images/dict.jpg) 60 | 61 | ## 字典数字界面 62 | 63 | ![menu](docs/images/dict_data.jpg) 64 | 65 | ## 通知界面 66 | 67 | ![menu](docs/images/notice.jpg) 68 | 69 | ## 登录日志 70 | 71 | ![menu](docs/images/login_log.jpg) -------------------------------------------------------------------------------- /src/routes/system/sys_menu_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_menu_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建菜单信息路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_menu_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/menu/addMenu").post(add_sys_menu)) 11 | .push(Router::new().path("/system/menu/deleteMenu").post(delete_sys_menu)) 12 | .push(Router::new().path("/system/menu/updateMenu").post(update_sys_menu)) 13 | .push(Router::new().path("/system/menu/updateMenuStatus").post(update_sys_menu_status)) 14 | .push(Router::new().path("/system/menu/queryMenuDetail").post(query_sys_menu_detail)) 15 | .push(Router::new().path("/system/menu/queryMenuList").post(query_sys_menu_list)) 16 | .push(Router::new().path("/system/menu/queryMenuListSimple").post(query_sys_menu_list_simple)) 17 | .push(Router::new().path("/system/menu/queryMenuResourceList").post(query_sys_menu_resource_list)) 18 | //记得在main.rs中的route()函数中添加构建菜单信息路由build_sys_menu_route() 19 | } 20 | -------------------------------------------------------------------------------- /docs/sql/system/sys_post.sql: -------------------------------------------------------------------------------- 1 | create table sys_post 2 | ( 3 | id bigint auto_increment comment '岗位id' 4 | primary key, 5 | post_code varchar(64) not null comment '岗位编码', 6 | post_name varchar(50) not null comment '岗位名称', 7 | sort int default 0 not null comment '显示顺序', 8 | status tinyint default 0 not null comment '岗位状态(0:停用,1:正常)', 9 | remark varchar(500) 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 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('ceo', '董事长', 1, 1, ''); 15 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('se', '项目经理', 2, 1, ''); 16 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('hr', '人力资源', 3, 1, ''); 17 | INSERT INTO sys_post (post_code, post_name, sort, status, remark) VALUES ('user', '普通员工', 1, 1, ''); 18 | -------------------------------------------------------------------------------- /src/model/system/sys_role_menu_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use rbatis::RBatis; 5 | use serde::{Deserialize, Serialize}; 6 | use std::collections::HashMap; 7 | 8 | /* 9 | *菜单角色关联表 10 | *author:刘飞华 11 | *date:2024/12/12 14:41:44 12 | */ 13 | #[derive(Clone, Debug, Serialize, Deserialize)] 14 | pub struct RoleMenu { 15 | pub id: Option, //主键 16 | pub role_id: i64, //角色ID 17 | pub menu_id: i64, //菜单ID 18 | } 19 | 20 | /* 21 | *菜单角色关联表基本操作 22 | *author:刘飞华 23 | *date:2024/12/12 14:41:44 24 | */ 25 | rbatis::crud!(RoleMenu {}, "sys_role_menu"); 26 | 27 | /* 28 | *根据角色id查询菜单ids 29 | *author:刘飞华 30 | *date:2024/12/12 14:41:44 31 | */ 32 | #[sql("select menu_id from sys_role_menu where role_id = ?")] 33 | pub async fn query_menu_by_role(rb: &RBatis, role_id: i64) -> rbatis::Result>> { 34 | impled!() 35 | } 36 | 37 | /* 38 | *查询菜单使用数量 39 | *author:刘飞华 40 | *date:2024/12/25 10:01:11 41 | */ 42 | #[sql("select count(1) from sys_role_menu where menu_id= ?")] 43 | pub async fn select_count_menu_by_menu_id(rb: &RBatis, menu_id: &i64) -> rbatis::Result { 44 | impled!() 45 | } 46 | -------------------------------------------------------------------------------- /src/routes/system/sys_user_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_user_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建用户信息路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_user_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/user/addUser").post(add_sys_user)) 11 | .push(Router::new().path("/system/user/deleteUser").post(delete_sys_user)) 12 | .push(Router::new().path("/system/user/updateUser").post(update_sys_user)) 13 | .push(Router::new().path("/system/user/updateUserStatus").post(update_sys_user_status)) 14 | .push(Router::new().path("/system/user/queryUserDetail").post(query_sys_user_detail)) 15 | .push(Router::new().path("/system/user/queryUserList").post(query_sys_user_list)) 16 | .push(Router::new().path("/system/user/queryUserMenu").get(query_user_menu)) 17 | .push(Router::new().path("/system/user/queryUserRole").post(query_user_role)) 18 | .push(Router::new().path("/system/user/updateUserRole").post(update_user_role)) 19 | .push(Router::new().path("/system/user/updateUserPassword").post(update_sys_user_password)) 20 | //记得在main.rs中的route()函数中添加构建用户信息路由build_sys_user_route() 21 | } 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 id: Option, //主键 15 | pub role_id: i64, //角色id 16 | pub dept_id: i64, //部门id 17 | } 18 | 19 | /* 20 | *角色和部门关联表基本操作 21 | *author:刘飞华 22 | *date:2024/12/25 10:01:11 23 | */ 24 | rbatis::crud!(RoleDept {}, "sys_role_dept"); 25 | 26 | /* 27 | *根据id查询角色和部门关联表 28 | *author:刘飞华 29 | *date:2024/12/25 10:01:11 30 | */ 31 | impl_select!(RoleDept{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_role_dept"); 32 | 33 | /* 34 | *分页查询角色和部门关联表 35 | *author:刘飞华 36 | *date:2024/12/25 10:01:11 37 | */ 38 | impl_select_page!(RoleDept{select_page() =>" 39 | if !sql.contains('count'): 40 | order by create_time desc" 41 | },"sys_role_dept"); 42 | 43 | /* 44 | *根据条件分页查询角色和部门关联表 45 | *author:刘飞华 46 | *date:2024/12/25 10:01:11 47 | */ 48 | impl_select_page!(RoleDept{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_role_dept"); 54 | -------------------------------------------------------------------------------- /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": "abc1", 8 | "postName": "测试1", 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": 1, 31 | "postCode": "abc1", 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": 1 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 | -------------------------------------------------------------------------------- /src/routes/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::routes::system::sys_dept_route::build_sys_dept_route; 2 | use crate::routes::system::sys_dict_data_route::build_sys_dict_data_route; 3 | use crate::routes::system::sys_dict_type_route::build_sys_dict_type_route; 4 | use crate::routes::system::sys_login_log_route::build_sys_login_log_route; 5 | use crate::routes::system::sys_menu_route::build_sys_menu_route; 6 | use crate::routes::system::sys_notice_route::build_sys_notice_route; 7 | use crate::routes::system::sys_operate_log_route::build_sys_operate_log_route; 8 | use crate::routes::system::sys_post_route::build_sys_post_route; 9 | use crate::routes::system::sys_role_route::build_sys_role_route; 10 | use crate::routes::system::sys_user_route::build_sys_user_route; 11 | use salvo::Router; 12 | 13 | pub mod other; 14 | pub mod system; 15 | 16 | pub fn build_system_route() -> Router { 17 | Router::new() 18 | .push(build_sys_user_route()) 19 | .push(build_sys_role_route()) 20 | .push(build_sys_menu_route()) 21 | .push(build_sys_dept_route()) 22 | .push(build_sys_dict_data_route()) 23 | .push(build_sys_dict_type_route()) 24 | .push(build_sys_login_log_route()) 25 | .push(build_sys_operate_log_route()) 26 | .push(build_sys_post_route()) 27 | .push(build_sys_notice_route()) 28 | } 29 | 30 | pub fn build_other_route() -> Router { 31 | Router::new() 32 | } 33 | -------------------------------------------------------------------------------- /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": [1], 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/model/system/sys_user_role_model.rs: -------------------------------------------------------------------------------- 1 | // author:刘飞华 2 | // createTime:2024/12/12 14:41:44 3 | 4 | use rbatis::RBatis; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /* 8 | *角色用户关联表 9 | *author:刘飞华 10 | *date:2024/12/12 14:41:44 11 | */ 12 | #[derive(Clone, Debug, Serialize, Deserialize)] 13 | pub struct UserRole { 14 | pub id: Option, //主键 15 | pub user_id: i64, //用户ID 16 | pub role_id: i64, //角色ID 17 | } 18 | 19 | /* 20 | *角色用户关联表基本操作 21 | *author:刘飞华 22 | *date:2024/12/12 14:41:44 23 | */ 24 | rbatis::crud!(UserRole {}, "sys_user_role"); 25 | 26 | /* 27 | *查询是否为超级管理员(role_id=1是预设超级管理的id) 28 | *author:刘飞华 29 | *date:2024/12/12 14:41:44 30 | */ 31 | #[sql("select count(1) from sys_user_role where role_id = 1 and user_id = ?")] 32 | pub async fn is_admin(rb: &RBatis, user_id: &i64) -> rbatis::Result { 33 | impled!() 34 | } 35 | 36 | /* 37 | *通过角色id查询角色使用数量 38 | *author:刘飞华 39 | *date:2024/12/12 14:41:44 40 | */ 41 | #[sql("select count(1) from sys_user_role where role_id = ?")] 42 | pub async fn count_user_role_by_role_id(rb: &RBatis, role_id: i64) -> rbatis::Result { 43 | impled!() 44 | } 45 | 46 | /* 47 | *通过角色id和用户id删除 48 | *author:刘飞华 49 | *date:2024/12/12 14:41:44 50 | */ 51 | #[sql("delete from sys_user_role where role_id = ? and user_id = ?")] 52 | pub async fn delete_user_role_by_role_id_user_id(rb: &RBatis, role_id: i64, user_id: i64) -> Option { 53 | impled!() 54 | } 55 | -------------------------------------------------------------------------------- /src/routes/system/sys_role_route.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::system::sys_role_handler::*; 2 | use salvo::Router; 3 | /* 4 | *构建角色信息路由 5 | *author:刘飞华 6 | *date:2025/01/08 13:51:14 7 | */ 8 | pub fn build_sys_role_route() -> Router { 9 | Router::new() 10 | .push(Router::new().path("/system/role/addRole").post(add_sys_role)) 11 | .push(Router::new().path("/system/role/deleteRole").post(delete_sys_role)) 12 | .push(Router::new().path("/system/role/updateRole").post(update_sys_role)) 13 | .push(Router::new().path("/system/role/updateRoleStatus").post(update_sys_role_status)) 14 | .push(Router::new().path("/system/role/queryRoleDetail").post(query_sys_role_detail)) 15 | .push(Router::new().path("/system/role/queryRoleList").post(query_sys_role_list)) 16 | .push(Router::new().path("/system/role/queryRoleMenu").post(query_role_menu)) 17 | .push(Router::new().path("/system/role/updateRoleMenu").post(update_role_menu)) 18 | .push(Router::new().path("/system/role/queryAllocatedList").post(query_allocated_list)) 19 | .push(Router::new().path("/system/role/queryUnallocatedList").post(query_unallocated_list)) 20 | .push(Router::new().path("/system/role/cancelAuthUser").post(cancel_auth_user)) 21 | .push(Router::new().path("/system/role/batchCancelAuthUser").post(batch_cancel_auth_user)) 22 | .push(Router::new().path("/system/role/batchAuthUser").post(batch_auth_user)) 23 | //记得在main.rs中的route()函数中添加构建角色信息路由build_sys_role_route() 24 | } 25 | -------------------------------------------------------------------------------- /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": "123gg11g1", 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": "测试通知12311", 32 | "noticeType": 1, 33 | "noticeContent": "这是一条测试通知内容", 34 | "status": 1, 35 | "remark": "" 36 | } 37 | 38 | ###更新通知公告表 updateNoticeStatus状态 39 | POST {{host}}/api/system/notice/updateNoticeStatus 40 | Content-Type: application/json 41 | Authorization: Bearer {{token}} 42 | 43 | { 44 | "ids": [1], 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 | "noticeType": 1 74 | } 75 | -------------------------------------------------------------------------------- /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 id: Option, //主键 16 | pub user_id: i64, //用户id 17 | pub post_id: i64, //岗位id 18 | } 19 | 20 | /* 21 | *用户与岗位关联基本操作 22 | *author:刘飞华 23 | *date:2024/12/25 10:01:11 24 | */ 25 | rbatis::crud!(UserPost {}, "sys_user_post"); 26 | 27 | /* 28 | *根据id查询用户与岗位关联 29 | *author:刘飞华 30 | *date:2024/12/25 10:01:11 31 | */ 32 | impl_select!(UserPost{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_user_post"); 33 | 34 | /* 35 | *分页查询用户与岗位关联 36 | *author:刘飞华 37 | *date:2024/12/25 10:01:11 38 | */ 39 | impl_select_page!(UserPost{select_page() =>" 40 | if !sql.contains('count'): 41 | order by create_time desc" 42 | },"sys_user_post"); 43 | 44 | /* 45 | *根据条件分页查询用户与岗位关联 46 | *author:刘飞华 47 | *date:2024/12/25 10:01:11 48 | */ 49 | impl_select_page!(UserPost{select_page_by_name(name:&str) =>" 50 | if name != null && name != '': 51 | where real_name != #{name} 52 | if name == '': 53 | where real_name != ''" 54 | },"sys_user_post"); 55 | 56 | /* 57 | *通过岗位id查询岗位使用数量 58 | *author:刘飞华 59 | *date:2024/12/12 14:41:44 60 | */ 61 | #[sql("select count(1) from sys_user_post where post_id = ? ")] 62 | pub async fn count_user_post_by_id(rb: &RBatis, post_id: i64) -> rbatis::Result { 63 | impled!() 64 | } 65 | -------------------------------------------------------------------------------- /src/common/error.rs: -------------------------------------------------------------------------------- 1 | use crate::common::result::BaseResponse; 2 | use redis::RedisError; 3 | use salvo::prelude::Json; 4 | use salvo::{Depot, Request, Response, Writer}; 5 | use thiserror::Error; 6 | 7 | #[derive(Error, Debug)] 8 | pub enum AppError { 9 | // #[error("Failed to complete an HTTP request")] 10 | // Http { #[from] source: reqwest::Error }, 11 | // 12 | #[error("Failed to read the cache file")] 13 | DiskCacheRead { source: std::io::Error }, 14 | // 15 | // #[error("Failed to update the cache file")] 16 | // DiskCacheWrite { source: std::io::Error }, 17 | #[error("")] 18 | JwtTokenError(String), 19 | 20 | #[error("解析请求参数错误: {0}")] 21 | ParseError(#[from] salvo::http::ParseError), 22 | 23 | #[error("redis错误: {0}")] 24 | RedisError(#[from] RedisError), 25 | 26 | #[error("数据库错误: {0}")] 27 | DbError(#[from] rbatis::Error), 28 | 29 | #[error("业务异常: {0}")] 30 | BusinessError(&'static str), 31 | } 32 | pub type AppResult = Result<(), AppError>; 33 | 34 | #[async_trait] 35 | impl Writer for AppError { 36 | async fn write(mut self, _req: &mut Request, _: &mut Depot, res: &mut Response) { 37 | // let user_id = depot.get::("userId").copied().unwrap(); 38 | // let username = depot.get::("username").unwrap(); 39 | // log::info!("query user user_id params {:?}", user_id); 40 | // log::info!("query user username params {:?}", username); 41 | 42 | res.render(Json(BaseResponse { 43 | msg: self.to_string(), 44 | code: 1, 45 | data: Some("None".to_string()), 46 | })) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 | "id": 1, 35 | "dictSort": 1, 36 | "dictLabel": "男", 37 | "dictValue": "0", 38 | "dictType": "sys_user_sex", 39 | "cssClass": "1", 40 | "listClass": "1", 41 | "isDefault": "N", 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": [1], 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": 3, 9 | "status": 1, 10 | "sort": 6, 11 | "parentId": 24, 12 | "menuUrl": "", 13 | "apiUrl": "/api/system/menu/queryMenuList", 14 | "menu_icon": "", 15 | "remark": "查询菜单信息列表" 16 | } 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": 31, 34 | "menuName": "查询菜单信息列表12", 35 | "menuType": 3, 36 | "status": 1, 37 | "sort": 6, 38 | "parentId": 24, 39 | "menuUrl": "", 40 | "apiUrl": "/api/system/menu/queryMenuList", 41 | "menu_icon": "", 42 | "remark": "查询菜单信息列表" 43 | } 44 | 45 | ###更新菜单信息 updateMenuStatus状态 46 | POST {{host}}/api/system/menu/updateMenuStatus 47 | Content-Type: application/json 48 | Authorization: Bearer {{token}} 49 | 50 | { 51 | "ids": [31], 52 | "status": 0 53 | } 54 | ###查询菜单信息详情 queryMenu 55 | POST {{host}}/api/system/menu/queryMenuDetail 56 | Content-Type: application/json 57 | Authorization: Bearer {{token}} 58 | 59 | { 60 | "id": 1 61 | } 62 | 63 | 64 | ###查询菜单信息列表 queryMenuList 65 | POST {{host}}/api/system/menu/queryMenuList 66 | Content-Type: application/json 67 | Authorization: Bearer {{token}} 68 | 69 | { 70 | 71 | } 72 | 73 | ###查询菜单信息列表 queryMenuListSimple 74 | GET {{host}}/api/system/menu/queryMenuListSimple 75 | Authorization: Bearer {{token}} 76 | 77 | -------------------------------------------------------------------------------- /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": [4], 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 | -------------------------------------------------------------------------------- /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, email, password, status, remark) VALUES (1, '18613030111', 'admin','admin', 'xx@qq.com','123456', 1, '超级管理员'); 29 | INSERT INTO sys_user (id, mobile, user_name, nick_name, email, password, status, remark) VALUES (2, '18613030222', 'test', 'test', '123@qq.com','123456', 1, '演示权限'); 30 | 31 | -------------------------------------------------------------------------------- /docs/sql/system/sys_dict_data.sql: -------------------------------------------------------------------------------- 1 | drop table if exists sys_dict_data; 2 | create table sys_dict_data 3 | ( 4 | id bigint not null auto_increment comment '字典编码', 5 | dict_sort int default 0 not null comment '字典排序', 6 | dict_label varchar(100) default '' not null comment '字典标签', 7 | dict_value varchar(100) default '' not null comment '字典键值', 8 | dict_type varchar(100) default '' not null comment '字典类型', 9 | css_class varchar(100) default '' not null comment '样式属性(其他样式扩展)', 10 | list_class varchar(100) default '' not null comment '表格回显样式', 11 | is_default char(1) default 'N' not null comment '是否默认(Y是 N否)', 12 | status tinyint default 0 not null comment '状态(0:停用,1:正常)', 13 | remark varchar(500) default '' not null comment '备注', 14 | create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', 15 | update_time datetime null on update CURRENT_TIMESTAMP comment '修改时间', 16 | primary key (id) 17 | )comment = '字典数据表'; 18 | 19 | 20 | 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, '性别男'); 21 | 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, '性别女'); 22 | 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, '性别未知'); 23 | 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, '通知'); 24 | 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, '公告'); 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/handler/system/sys_login_log_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_login_log_model::LoginLog; 8 | use crate::vo::system::sys_login_log_vo::*; 9 | use crate::RB; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbs::value; 12 | use salvo::prelude::*; 13 | use salvo::{Request, Response}; 14 | /* 15 | *删除系统访问记录 16 | *author:刘飞华 17 | *date:2025/01/08 13:51:14 18 | */ 19 | #[handler] 20 | pub async fn delete_sys_login_log(req: &mut Request, res: &mut Response) -> AppResult { 21 | let item = req.parse_json::().await?; 22 | log::info!("delete sys_login_log params: {:?}", &item); 23 | 24 | let rb = &mut RB.clone(); 25 | 26 | LoginLog::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result(res))? 27 | } 28 | 29 | /* 30 | *查询系统访问记录详情 31 | *author:刘飞华 32 | *date:2025/01/08 13:51:14 33 | */ 34 | #[handler] 35 | pub async fn query_sys_login_log_detail(req: &mut Request, res: &mut Response) -> AppResult { 36 | let item = req.parse_json::().await?; 37 | log::info!("query sys_login_log_detail params: {:?}", &item); 38 | 39 | LoginLog::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 40 | || Err(AppError::BusinessError("系统访问记录不存在")), 41 | |x| { 42 | let data: LoginLogResp = x.into(); 43 | ok_result_data(res, data) 44 | }, 45 | ) 46 | } 47 | 48 | /* 49 | *查询系统访问记录列表 50 | *author:刘飞华 51 | *date:2025/01/08 13:51:14 52 | */ 53 | #[handler] 54 | pub async fn query_sys_login_log_list(req: &mut Request, res: &mut Response) -> AppResult { 55 | let req = req.parse_json::().await?; 56 | log::info!("query sys_login_log_list params: {:?}", &req); 57 | 58 | let rb = &mut RB.clone(); 59 | let item = &req; 60 | 61 | LoginLog::select_login_log_list(rb, &PageRequest::from(item), item) 62 | .await 63 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 64 | } 65 | -------------------------------------------------------------------------------- /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 rbatis::PageRequest; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除系统访问记录请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeleteLoginLogReq { 13 | pub ids: Vec, 14 | } 15 | 16 | /* 17 | 查询系统访问记录详情请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | pub struct QueryLoginLogDetailReq { 21 | pub id: i64, 22 | } 23 | 24 | /* 25 | 查询系统访问记录列表请求参数 26 | */ 27 | #[derive(Debug, Serialize, Deserialize)] 28 | #[serde(rename_all = "camelCase")] 29 | pub struct QueryLoginLogListReq { 30 | pub page_no: u64, 31 | pub page_size: u64, 32 | pub login_name: Option, //登录账号 33 | pub ipaddr: Option, //登录IP地址 34 | pub login_location: Option, //登录地点 35 | pub browser: Option, //浏览器类型 36 | pub os: Option, //操作系统 37 | #[serde(default = "default_status")] 38 | pub status: Option, //登录状态(0:失败,1:成功) 39 | } 40 | fn default_status() -> Option { 41 | Some(2) 42 | } 43 | impl From<&QueryLoginLogListReq> for PageRequest { 44 | fn from(value: &QueryLoginLogListReq) -> Self { 45 | PageRequest::new(value.page_no, value.page_size) 46 | } 47 | } 48 | /* 49 | 查询系统访问记录列表响应参数 50 | */ 51 | #[derive(Debug, Serialize, Deserialize)] 52 | #[serde(rename_all = "camelCase")] 53 | pub struct LoginLogResp { 54 | pub id: Option, //访问ID 55 | pub login_name: String, //登录账号 56 | pub ipaddr: String, //登录IP地址 57 | pub login_location: String, //登录地点 58 | pub platform: String, //平台信息 59 | pub browser: String, //浏览器类型 60 | pub version: String, //浏览器版本 61 | pub os: String, //操作系统 62 | pub arch: String, //体系结构信息 63 | pub engine: String, //渲染引擎信息 64 | pub engine_details: String, //渲染引擎详细信息 65 | pub extra: String, //其他信息(可选) 66 | pub status: i8, //登录状态(0:失败,1:成功) 67 | pub msg: String, //提示消息 68 | #[serde(serialize_with = "serialize_datetime")] 69 | pub login_time: Option, //访问时间 70 | } 71 | -------------------------------------------------------------------------------- /src/handler/system/sys_operate_log_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_operate_log_model::OperateLog; 8 | use crate::vo::system::sys_operate_log_vo::*; 9 | use crate::RB; 10 | use rbatis::PageRequest; 11 | use rbs::value; 12 | use salvo::prelude::*; 13 | use salvo::{Request, Response}; 14 | /* 15 | *删除操作日志记录 16 | *author:刘飞华 17 | *date:2025/01/08 13:51:14 18 | */ 19 | #[handler] 20 | pub async fn delete_sys_operate_log(req: &mut Request, res: &mut Response) -> AppResult { 21 | let item = req.parse_json::().await?; 22 | log::info!("delete sys_operate_log params: {:?}", &item); 23 | 24 | let rb = &mut RB.clone(); 25 | 26 | OperateLog::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result(res))? 27 | } 28 | 29 | /* 30 | *查询操作日志记录详情 31 | *author:刘飞华 32 | *date:2025/01/08 13:51:14 33 | */ 34 | #[handler] 35 | pub async fn query_sys_operate_log_detail(req: &mut Request, res: &mut Response) -> AppResult { 36 | let item = req.parse_json::().await?; 37 | 38 | log::info!("query sys_operate_log_detail params: {:?}", &item); 39 | 40 | OperateLog::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 41 | || Err(AppError::BusinessError("操作日志不存在")), 42 | |x| { 43 | let data: OperateLogResp = x.into(); 44 | ok_result_data(res, data) 45 | }, 46 | ) 47 | } 48 | 49 | /* 50 | *查询操作日志记录列表 51 | *author:刘飞华 52 | *date:2025/01/08 13:51:14 53 | */ 54 | #[handler] 55 | pub async fn query_sys_operate_log_list(req: &mut Request, res: &mut Response) -> AppResult { 56 | let req = req.parse_json::().await?; 57 | log::info!("query sys_operate_log_list params: {:?}", &req); 58 | 59 | let rb = &mut RB.clone(); 60 | let item = &req; 61 | 62 | OperateLog::select_page_by_name(rb, &PageRequest::from(item), item) 63 | .await 64 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 65 | } 66 | -------------------------------------------------------------------------------- /src/common/result.rs: -------------------------------------------------------------------------------- 1 | use crate::common::error::AppResult; 2 | use rbatis::rbdc::DateTime; 3 | use salvo::prelude::Json; 4 | use salvo::Response; 5 | use serde::Serialize; 6 | use std::fmt::Debug; 7 | 8 | // 统一返回vo 9 | #[derive(Serialize, Debug, Clone)] 10 | pub struct BaseResponse { 11 | pub code: i32, 12 | pub msg: String, 13 | pub data: Option, 14 | } 15 | 16 | #[derive(Serialize, Debug, Clone)] 17 | pub struct ResponsePage { 18 | pub code: i32, 19 | pub msg: &'static str, 20 | pub total: u64, 21 | pub success: bool, 22 | pub data: Option, 23 | } 24 | 25 | pub fn ok_result(res: &mut Response) -> AppResult { 26 | ok_result_msg(res, "操作成功".to_string()) 27 | } 28 | 29 | pub fn ok_result_msg(res: &mut Response, msg: String) -> AppResult { 30 | let response = BaseResponse { 31 | msg, 32 | code: 0, 33 | data: Some("None".to_string()), 34 | }; 35 | res.render(Json(response)); 36 | Ok(()) 37 | } 38 | 39 | pub fn ok_result_data(res: &mut Response, data: T) -> AppResult { 40 | let response = BaseResponse { 41 | msg: "操作成功".to_string(), 42 | code: 0, 43 | data: Some(data), 44 | }; 45 | res.render(Json(response)); 46 | Ok(()) 47 | } 48 | 49 | pub fn err_result_msg(res: &mut Response, msg: String) -> AppResult { 50 | let resp = BaseResponse { 51 | msg, 52 | code: 1, 53 | data: Some("None".to_string()), 54 | }; 55 | res.render(Json(resp)); 56 | Ok(()) 57 | } 58 | 59 | pub fn ok_result_page(res: &mut Response, data: T, total: u64) -> AppResult { 60 | let page = ResponsePage { 61 | msg: "操作成功", 62 | code: 0, 63 | success: true, 64 | data: Some(data), 65 | total, 66 | }; 67 | res.render(Json(page)); 68 | Ok(()) 69 | } 70 | 71 | pub fn serialize_datetime(dt: &Option, serializer: S) -> Result 72 | where 73 | S: serde::Serializer, 74 | { 75 | match dt { 76 | Some(datetime) => { 77 | let formatted = datetime.format("YYYY-MM-DD hh:mm:ss"); 78 | serializer.serialize_str(&formatted) 79 | } 80 | None => serializer.serialize_str(""), 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /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 rbatis::PageRequest; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /* 10 | 删除字典类型表请求参数 11 | */ 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct DeleteDictTypeReq { 14 | pub ids: Vec, 15 | } 16 | 17 | /* 18 | 更新字典类型表请求参数 19 | */ 20 | #[derive(Debug, Serialize, Deserialize)] 21 | #[serde(rename_all = "camelCase")] 22 | pub struct DictTypeReq { 23 | pub id: Option, //字典主键 24 | pub dict_name: String, //字典名称 25 | pub dict_type: String, //字典类型 26 | pub status: i8, //状态(0:停用,1:正常) 27 | pub remark: Option, //备注 28 | } 29 | 30 | /* 31 | 更新字典类型表状态请求参数 32 | */ 33 | #[derive(Debug, Serialize, Deserialize)] 34 | pub struct UpdateDictTypeStatusReq { 35 | pub ids: Vec, 36 | pub status: i8, 37 | } 38 | 39 | /* 40 | 查询字典类型表详情请求参数 41 | */ 42 | #[derive(Debug, Serialize, Deserialize)] 43 | pub struct QueryDictTypeDetailReq { 44 | pub id: i64, 45 | } 46 | 47 | /* 48 | 查询字典类型表列表请求参数 49 | */ 50 | #[derive(Debug, Serialize, Deserialize)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct QueryDictTypeListReq { 53 | pub page_no: u64, 54 | pub page_size: u64, 55 | pub dict_name: Option, //字典名称 56 | pub dict_type: Option, //字典类型 57 | #[serde(default = "default_status")] 58 | pub status: Option, //状态(0:停用,1:正常) 59 | } 60 | fn default_status() -> Option { 61 | Some(2) 62 | } 63 | impl From<&QueryDictTypeListReq> for PageRequest { 64 | fn from(value: &QueryDictTypeListReq) -> Self { 65 | PageRequest::new(value.page_no, value.page_size) 66 | } 67 | } 68 | /* 69 | 查询字典类型表列表响应参数 70 | */ 71 | #[derive(Debug, Serialize, Deserialize)] 72 | #[serde(rename_all = "camelCase")] 73 | pub struct DictTypeResp { 74 | pub id: Option, //字典主键 75 | pub dict_name: String, //字典名称 76 | pub dict_type: String, //字典类型 77 | pub status: i8, //状态(0:停用,1:正常) 78 | pub remark: Option, //备注 79 | #[serde(serialize_with = "serialize_datetime")] 80 | pub create_time: Option, //创建时间 81 | #[serde(serialize_with = "serialize_datetime")] 82 | pub update_time: Option, //修改时间 83 | } 84 | -------------------------------------------------------------------------------- /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 rbatis::PageRequest; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /* 10 | 删除岗位信息表请求参数 11 | */ 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct DeletePostReq { 14 | pub ids: Vec, 15 | } 16 | 17 | /* 18 | 更新岗位信息表请求参数 19 | */ 20 | #[derive(Debug, Serialize, Deserialize)] 21 | #[serde(rename_all = "camelCase")] 22 | pub struct PostReq { 23 | pub id: Option, //岗位id 24 | pub post_code: String, //岗位编码 25 | pub post_name: String, //岗位名称 26 | pub sort: i32, //显示顺序 27 | pub status: i8, //部状态(0:停用,1:正常) 28 | pub remark: Option, //备注 29 | } 30 | 31 | /* 32 | 更新岗位信息表状态请求参数 33 | */ 34 | #[derive(Debug, Serialize, Deserialize)] 35 | pub struct UpdatePostStatusReq { 36 | pub ids: Vec, 37 | pub status: i8, 38 | } 39 | 40 | /* 41 | 查询岗位信息表详情请求参数 42 | */ 43 | #[derive(Debug, Serialize, Deserialize)] 44 | pub struct QueryPostDetailReq { 45 | pub id: i64, 46 | } 47 | 48 | /* 49 | 查询岗位信息表列表请求参数 50 | */ 51 | #[derive(Debug, Serialize, Deserialize)] 52 | #[serde(rename_all = "camelCase")] 53 | pub struct QueryPostListReq { 54 | pub page_no: u64, 55 | pub page_size: u64, 56 | pub post_code: Option, //岗位编码 57 | pub post_name: Option, //岗位名称 58 | #[serde(default = "default_status")] 59 | pub status: Option, //部状态(0:停用,1:正常) 60 | } 61 | fn default_status() -> Option { 62 | Some(2) 63 | } 64 | impl From<&QueryPostListReq> for PageRequest { 65 | fn from(value: &QueryPostListReq) -> Self { 66 | PageRequest::new(value.page_no, value.page_size) 67 | } 68 | } 69 | /* 70 | 查询岗位信息表列表响应参数 71 | */ 72 | #[derive(Debug, Serialize, Deserialize)] 73 | #[serde(rename_all = "camelCase")] 74 | pub struct PostResp { 75 | pub id: Option, //岗位id 76 | pub post_code: String, //岗位编码 77 | pub post_name: String, //岗位名称 78 | pub sort: i32, //显示顺序 79 | pub status: i8, //部状态(0:停用,1:正常) 80 | pub remark: Option, //备注 81 | #[serde(serialize_with = "serialize_datetime")] 82 | pub create_time: Option, //创建时间 83 | #[serde(serialize_with = "serialize_datetime")] 84 | pub update_time: Option, //修改时间 85 | } 86 | -------------------------------------------------------------------------------- /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_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 rbatis::PageRequest; 7 | use serde::{Deserialize, Serialize}; 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 | impl From<&QueryNoticeListReq> for PageRequest { 65 | fn from(value: &QueryNoticeListReq) -> Self { 66 | PageRequest::new(value.page_no, value.page_size) 67 | } 68 | } 69 | 70 | /* 71 | 查询通知公告表列表响应参数 72 | */ 73 | #[derive(Debug, Serialize, Deserialize)] 74 | #[serde(rename_all = "camelCase")] 75 | pub struct NoticeResp { 76 | pub id: Option, //公告ID 77 | pub notice_title: String, //公告标题 78 | pub notice_type: i8, //公告类型(1:通知,2:公告) 79 | pub notice_content: String, //公告内容 80 | pub status: i8, //公告状态(0:关闭,1:正常 ) 81 | pub remark: Option, //备注 82 | #[serde(serialize_with = "serialize_datetime")] 83 | pub create_time: Option, //创建时间 84 | #[serde(serialize_with = "serialize_datetime")] 85 | pub update_time: Option, //修改时间 86 | } 87 | -------------------------------------------------------------------------------- /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 serde::{Deserialize, Serialize}; 7 | 8 | /* 9 | 删除部门表请求参数 10 | */ 11 | #[derive(Debug, Serialize, Deserialize)] 12 | pub struct DeleteDeptReq { 13 | pub id: i64, 14 | } 15 | 16 | /* 17 | 更新部门表请求参数 18 | */ 19 | #[derive(Debug, Serialize, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct DeptReq { 22 | pub id: Option, //部门id 23 | pub parent_id: i64, //父部门id 24 | pub dept_name: String, //部门名称 25 | pub ancestors: Option, //祖级列表 26 | pub sort: i32, //显示顺序 27 | pub leader: String, //负责人 28 | pub phone: String, //联系电话 29 | pub email: String, //邮箱 30 | pub status: i8, //部状态(0:停用,1:正常) 31 | } 32 | 33 | /* 34 | 更新部门表状态请求参数 35 | */ 36 | #[derive(Debug, Serialize, Deserialize)] 37 | pub struct UpdateDeptStatusReq { 38 | pub ids: Vec, 39 | pub status: i8, 40 | } 41 | 42 | /* 43 | 查询部门表详情请求参数 44 | */ 45 | #[derive(Debug, Serialize, Deserialize)] 46 | pub struct QueryDeptDetailReq { 47 | pub id: i64, 48 | } 49 | 50 | /* 51 | 查询部门表列表请求参数 52 | */ 53 | #[derive(Debug, Serialize, Deserialize)] 54 | #[serde(rename_all = "camelCase")] 55 | pub struct QueryDeptListReq { 56 | pub dept_name: Option, //部门名称 57 | pub leader: Option, //负责人 58 | pub phone: Option, //联系电话 59 | pub email: Option, //邮箱 60 | #[serde(default = "default_status")] 61 | pub status: Option, //部状态(0:停用,1:正常) 62 | } 63 | fn default_status() -> Option { 64 | Some(2) 65 | } 66 | /* 67 | 查询部门表列表响应参数 68 | */ 69 | #[derive(Debug, Serialize, Deserialize)] 70 | #[serde(rename_all = "camelCase")] 71 | pub struct DeptResp { 72 | pub id: Option, //部门id 73 | pub key: String, //部门id(angular tree使用) 74 | pub parent_id: i64, //父部门id 75 | pub ancestors: Option, //祖级列表 76 | pub dept_name: String, //部门名称 77 | pub title: String, //部门名称(angular treeSelect使用) 78 | pub sort: i32, //显示顺序 79 | pub leader: String, //负责人 80 | pub phone: String, //联系电话 81 | pub email: String, //邮箱 82 | pub status: i8, //部状态(0:停用,1:正常) 83 | #[serde(serialize_with = "serialize_datetime")] 84 | pub create_time: Option, //创建时间 85 | #[serde(serialize_with = "serialize_datetime")] 86 | pub update_time: Option, //修改时间 87 | } 88 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ###普通用户登录 login 12 | POST {{host}}/api/system/user/login 13 | Content-Type: application/json 14 | 15 | { 16 | "account": "18613030222", 17 | "password": "123456" 18 | } 19 | > {% client.global.set("token", response.body.data); %} 20 | 21 | 22 | ###查询用户菜单 query_user_menu 23 | GET {{host}}/api/system/user/queryUserMenu 24 | Authorization: Bearer {{token}} 25 | 26 | ###添加用户信息 addUser 27 | POST {{host}}/api/system/user/addUser 28 | Content-Type: application/json 29 | Authorization: Bearer {{token}} 30 | 31 | { 32 | "mobile": "1861303021", 33 | "userName": "11", 34 | "nickName": "12", 35 | "userType": "00", 36 | "email": "xx1@qq.com", 37 | "avatar": "", 38 | "status": 1, 39 | "deptId": 1, 40 | "password": "test", 41 | "remark": "test", 42 | "postIds": [1] 43 | } 44 | 45 | ###删除用户信息 deleteUser 46 | POST {{host}}/api/system/user/deleteUser 47 | Content-Type: application/json 48 | Authorization: Bearer {{token}} 49 | 50 | { 51 | "ids": [15] 52 | } 53 | 54 | ###更新用户信息 updateUser 55 | POST {{host}}/api/system/user/updateUser 56 | Content-Type: application/json 57 | Authorization: Bearer {{token}} 58 | 59 | { 60 | "id": 13, 61 | "mobile": "", 62 | "userName": "", 63 | "password": "", 64 | "status": 0, 65 | "sort": 0, 66 | "remark": "" 67 | } 68 | 69 | ###更新用户信息 updateUserStatus状态 70 | POST {{host}}/api/system/user/updateUserStatus 71 | Content-Type: application/json 72 | Authorization: Bearer {{token}} 73 | 74 | { 75 | "ids": [12,13], 76 | "status": 0 77 | } 78 | ###查询用户信息详情 queryUserDetail 79 | POST {{host}}/api/system/user/queryUserDetail 80 | Content-Type: application/json 81 | Authorization: Bearer {{token}} 82 | 83 | { 84 | "id": 1 85 | } 86 | 87 | 88 | ###查询用户信息列表 queryUserList 89 | POST {{host}}/api/system/user/queryUserList 90 | Content-Type: application/json 91 | Authorization: Bearer {{token}} 92 | 93 | { 94 | "pageNo": 1, 95 | "pageSize": 10, 96 | "mobile": "", 97 | "userName": "", 98 | "status": 1 99 | } 100 | 101 | ###查询用户角色列表 queryUserRole 102 | POST {{host}}/api/system/user/queryUserRole 103 | Content-Type: application/json 104 | Authorization: Bearer {{token}} 105 | 106 | { 107 | "userId": 1 108 | } 109 | 110 | ###更新用户角色列表 updateUserRole 111 | POST {{host}}/api/system/user/updateUserRole 112 | Content-Type: application/json 113 | Authorization: Bearer {{token}} 114 | 115 | { 116 | "userId": 2, 117 | "roleIds": [1,2] 118 | } 119 | -------------------------------------------------------------------------------- /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 rbatis::PageRequest; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /* 10 | 删除操作日志记录请求参数 11 | */ 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct DeleteOperateLogReq { 14 | pub ids: Vec, 15 | } 16 | 17 | /* 18 | 查询操作日志记录详情请求参数 19 | */ 20 | #[derive(Debug, Serialize, Deserialize)] 21 | pub struct QueryOperateLogDetailReq { 22 | pub id: i64, 23 | } 24 | 25 | /* 26 | 查询操作日志记录列表请求参数 27 | */ 28 | #[derive(Debug, Serialize, Deserialize)] 29 | #[serde(rename_all = "camelCase")] 30 | pub struct QueryOperateLogListReq { 31 | pub page_no: u64, 32 | pub page_size: u64, 33 | pub title: Option, //模块标题 34 | pub business_type: Option, //业务类型(0其它 1新增 2修改 3删除) 35 | pub method: Option, //方法名称 36 | pub request_method: Option, //请求方式 37 | pub operator_type: Option, //操作类别(0其它 1后台用户 2手机端用户) 38 | pub operate_name: Option, //操作人员 39 | pub dept_name: Option, //部门名称 40 | pub operate_url: Option, //请求URL 41 | pub operate_ip: Option, //主机地址 42 | pub operate_location: Option, //操作地点 43 | #[serde(default = "default_status")] 44 | pub status: Option, //操作状态(0:异常,正常) 45 | } 46 | fn default_status() -> Option { 47 | Some(2) 48 | } 49 | impl From<&QueryOperateLogListReq> for PageRequest { 50 | fn from(value: &QueryOperateLogListReq) -> Self { 51 | PageRequest::new(value.page_no, value.page_size) 52 | } 53 | } 54 | /* 55 | 查询操作日志记录列表响应参数 56 | */ 57 | #[derive(Debug, Serialize, Deserialize)] 58 | #[serde(rename_all = "camelCase")] 59 | pub struct OperateLogResp { 60 | pub id: Option, //日志主键 61 | pub title: Option, //模块标题 62 | pub business_type: Option, //业务类型(0其它 1新增 2修改 3删除) 63 | pub method: Option, //方法名称 64 | pub request_method: Option, //请求方式 65 | pub operator_type: Option, //操作类别(0其它 1后台用户 2手机端用户) 66 | pub operate_name: Option, //操作人员 67 | pub dept_name: Option, //部门名称 68 | pub operate_url: Option, //请求URL 69 | pub operate_ip: Option, //主机地址 70 | pub operate_location: Option, //操作地点 71 | pub operate_param: Option, //请求参数 72 | pub json_result: Option, //返回参数 73 | pub status: Option, //操作状态(0:异常,正常) 74 | pub error_msg: Option, //错误消息 75 | #[serde(serialize_with = "serialize_datetime")] 76 | pub operate_time: Option, //操作时间 77 | pub cost_time: Option, //消耗时间 78 | } 79 | -------------------------------------------------------------------------------- /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 rbatis::PageRequest; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /* 10 | 删除字典数据表请求参数 11 | */ 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct DeleteDictDataReq { 14 | pub ids: Vec, 15 | } 16 | 17 | /* 18 | 更新字典数据表请求参数 19 | */ 20 | #[derive(Debug, Serialize, Deserialize)] 21 | #[serde(rename_all = "camelCase")] 22 | pub struct DictDataReq { 23 | pub id: Option, //字典编码 24 | pub dict_sort: i32, //字典排序 25 | pub dict_label: String, //字典标签 26 | pub dict_value: String, //字典键值 27 | pub dict_type: String, //字典类型 28 | pub css_class: String, //样式属性(其他样式扩展) 29 | pub list_class: String, //表格回显样式 30 | pub is_default: String, //是否默认(Y是 N否) 31 | pub status: i8, //状态(0:停用,1:正常) 32 | pub remark: Option, //备注 33 | } 34 | 35 | /* 36 | 更新字典数据表状态请求参数 37 | */ 38 | #[derive(Debug, Serialize, Deserialize)] 39 | pub struct UpdateDictDataStatusReq { 40 | pub ids: Vec, 41 | pub status: i8, 42 | } 43 | 44 | /* 45 | 查询字典数据表详情请求参数 46 | */ 47 | #[derive(Debug, Serialize, Deserialize)] 48 | pub struct QueryDictDataDetailReq { 49 | pub id: i64, 50 | } 51 | 52 | /* 53 | 查询字典数据表列表请求参数 54 | */ 55 | #[derive(Debug, Serialize, Deserialize)] 56 | #[serde(rename_all = "camelCase")] 57 | pub struct QueryDictDataListReq { 58 | pub page_no: u64, 59 | pub page_size: u64, 60 | pub dict_label: Option, //字典标签 61 | pub dict_value: Option, //字典键值 62 | pub dict_type: Option, //字典类型 63 | #[serde(default = "default_status")] 64 | pub status: Option, //状态(0:停用,1:正常) 65 | } 66 | fn default_status() -> Option { 67 | Some(2) 68 | } 69 | 70 | impl From<&QueryDictDataListReq> for PageRequest { 71 | fn from(value: &QueryDictDataListReq) -> Self { 72 | PageRequest::new(value.page_no, value.page_size) 73 | } 74 | } 75 | /* 76 | 查询字典数据表列表响应参数 77 | */ 78 | #[derive(Debug, Serialize, Deserialize)] 79 | #[serde(rename_all = "camelCase")] 80 | pub struct DictDataResp { 81 | pub id: Option, //字典编码 82 | pub dict_sort: i32, //字典排序 83 | pub dict_label: String, //字典标签 84 | pub dict_value: String, //字典键值 85 | pub dict_type: String, //字典类型 86 | pub css_class: String, //样式属性(其他样式扩展) 87 | pub list_class: String, //表格回显样式 88 | pub is_default: String, //是否默认(Y是 N否) 89 | pub status: i8, //状态(0:停用,1:正常) 90 | pub remark: Option, //备注 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/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate rbatis; 3 | 4 | use crate::middleware::auth::auth_token; 5 | use crate::routes::{build_other_route, build_system_route}; 6 | use config::{Config, File}; 7 | use handler::system::sys_user_handler::*; 8 | use once_cell::sync::Lazy; 9 | use rbatis::rbdc::pool::{ConnectionManager, Pool}; 10 | use rbatis::RBatis; 11 | use rbdc_mysql::MysqlDriver; 12 | use rbdc_pool_fast::FastPool; 13 | use salvo::prelude::*; 14 | use serde::Deserialize; 15 | 16 | pub mod common; 17 | pub mod handler; 18 | pub mod middleware; 19 | pub mod model; 20 | pub mod routes; 21 | pub mod utils; 22 | pub mod vo; 23 | 24 | pub static RB: Lazy = Lazy::new(RBatis::new); 25 | 26 | #[handler] 27 | async fn hello() -> &'static str { 28 | "Hello World123123" 29 | } 30 | // 定义应用状态结构体,包含数据库连接池 31 | pub struct AppState { 32 | pub batis: RBatis, 33 | } 34 | 35 | // 定义配置结构体,包含服务器和数据库配置 36 | #[derive(Debug, Deserialize)] 37 | struct Config1 { 38 | server: ServerConfig, 39 | db: DbConfig, 40 | redis: RedisConfig, 41 | jwt: JwtConfig, 42 | } 43 | 44 | // 定义服务器配置结构体 45 | #[derive(Debug, Deserialize)] 46 | struct ServerConfig { 47 | addr: String, 48 | } 49 | 50 | // 定义数据库配置结构体 51 | #[derive(Debug, Deserialize)] 52 | struct DbConfig { 53 | url: String, 54 | } 55 | 56 | #[derive(Debug, Deserialize)] 57 | struct RedisConfig { 58 | url: String, 59 | } 60 | 61 | #[derive(Debug, Deserialize)] 62 | struct JwtConfig { 63 | secret: String, 64 | } 65 | // 主函数,异步运行 66 | #[tokio::main] 67 | async fn main() { 68 | // 初始化日志系统 69 | log4rs::init_file("src/config/log4rs.yaml", Default::default()).unwrap(); 70 | // tracing_subscriber::fmt().init(); 71 | 72 | // 加载和解析配置文件 73 | let config = Config::builder().add_source(File::with_name("config.toml")).build().unwrap().try_deserialize::().unwrap(); 74 | println!("Config: {:?}", config); 75 | 76 | // 初始化数据库连接 77 | let manager = ConnectionManager::new(MysqlDriver {}, config.db.url.as_str()).expect("create connection manager error"); 78 | let pool = FastPool::new(manager).expect("create db pool error"); 79 | 80 | RB.init_pool(pool).expect("init db pool error"); 81 | 82 | // 创建TCP监听器并启动服务器 83 | let acceptor = TcpListener::new(config.server.addr).bind().await; 84 | Server::new(acceptor).serve(route(config.redis.url.as_str(), config.jwt.secret)).await; 85 | } 86 | 87 | // 定义路由配置函数 88 | fn route(url: &str, secret: String) -> Router { 89 | let cfg = deadpool_redis::Config::from_url(url); 90 | let pool = cfg.create_pool(Some(deadpool_redis::Runtime::Tokio1)).unwrap(); 91 | 92 | // 创建路由实例,配置API路径和处理函数 93 | Router::new() 94 | .hoop(affix_state::insert("pool", pool).insert("secret", secret)) 95 | .path("/api") 96 | .get(hello) 97 | .push(Router::new().path("/system/user/login").post(login)) 98 | .push(Router::new().hoop(auth_token).push(build_system_route()).push(build_other_route())) 99 | } 100 | -------------------------------------------------------------------------------- /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)] 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/utils/jwt_util.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 2 | 3 | use jsonwebtoken::{decode, encode, errors::ErrorKind, Algorithm, DecodingKey, EncodingKey, Header, Validation}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::common::error::AppError; 7 | use crate::common::error::AppError::JwtTokenError; 8 | 9 | #[derive(Debug, Serialize, Deserialize, Clone)] 10 | pub struct JwtToken { 11 | pub id: i64, 12 | pub username: String, 13 | aud: String, 14 | // (audience):受众 15 | exp: usize, 16 | iat: usize, 17 | // (Issued At):签发时间 18 | iss: String, 19 | // (issuer):签发人 20 | nbf: usize, 21 | // (Not Before):生效时间 22 | sub: String, 23 | // (subject):主题 24 | jti: String, // (JWT ID):编号 25 | } 26 | 27 | impl JwtToken { 28 | pub fn new(id: i64, username: &str) -> JwtToken { 29 | let now = SystemTime::now(); 30 | //过期时间 31 | let m30 = Duration::from_secs(1800000); 32 | let now = now.duration_since(UNIX_EPOCH).expect("获取系统时间失败"); 33 | 34 | JwtToken { 35 | id, 36 | username: String::from(username), 37 | aud: String::from("rust_admin"), // (audience):受众 38 | exp: (now + m30).as_secs() as usize, 39 | iat: now.as_secs() as usize, // (Issued At):签发时间 40 | iss: String::from("koobe"), // (issuer):签发人 41 | nbf: now.as_secs() as usize, // (Not Before):生效时间 42 | sub: String::from("rust_admin"), // (subject):主题 43 | jti: String::from("ignore"), // (JWT ID):编号 44 | } 45 | } 46 | 47 | /// create token 48 | /// secret: your secret string 49 | pub fn create_token(&self, secret: &str) -> Result { 50 | return match encode(&Header::default(), self, &EncodingKey::from_secret(secret.as_ref())) { 51 | Ok(t) => Ok(t), 52 | Err(_) => Err(JwtTokenError("create token error".to_string())), 53 | }; 54 | } 55 | /// verify token invalid 56 | /// secret: your secret string 57 | pub fn verify(secret: &str, token: &str) -> Result { 58 | let mut validation = Validation::new(Algorithm::HS256); 59 | validation.sub = Some("rust_admin".to_string()); 60 | validation.set_audience(&["rust_admin"]); 61 | validation.set_required_spec_claims(&["exp", "sub", "aud"]); 62 | return match decode::(&token, &DecodingKey::from_secret(secret.as_ref()), &validation) { 63 | Ok(c) => Ok(c.claims), 64 | 65 | Err(err) => match *err.kind() { 66 | ErrorKind::InvalidToken => return Err(JwtTokenError("InvalidToken".to_string())), // Example on how to handle a specific error 67 | ErrorKind::InvalidIssuer => return Err(JwtTokenError("InvalidIssuer".to_string())), // Example on how to handle a specific error 68 | ErrorKind::ExpiredSignature => return Err(JwtTokenError("token 已经超时了".to_string())), // Example on how to handle a specific error 69 | // _ => return Err(Error::from("InvalidToken other errors")), 70 | _ => Err(JwtTokenError("create token error".to_string())), 71 | }, 72 | }; 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use crate::utils::jwt_util::JwtToken; 79 | 80 | #[test] 81 | fn test_jwt() { 82 | let jwt = JwtToken::new(1, "koobe"); 83 | let res = jwt.create_token("123"); 84 | println!("{:?}", res); 85 | let token = JwtToken::verify("123", &res.unwrap_or_default()); 86 | println!("{:?}", token) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /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; 6 | use crate::vo::system::sys_dict_type_vo::DictTypeResp; 7 | use crate::vo::system::sys_dict_type_vo::QueryDictTypeListReq; 8 | use rbatis::rbdc::datetime::DateTime; 9 | use serde::{Deserialize, Serialize}; 10 | /* 11 | *字典类型 12 | *author:刘飞华 13 | *date:2024/12/25 10:01:11 14 | */ 15 | #[derive(Clone, Debug, Serialize, Deserialize)] 16 | pub struct DictType { 17 | pub id: Option, //字典主键 18 | pub dict_name: String, //字典名称 19 | pub dict_type: String, //字典类型 20 | pub status: i8, //状态(0:停用,1:正常) 21 | pub remark: Option, //备注 22 | pub create_time: Option, //创建时间 23 | pub update_time: Option, //修改时间 24 | } 25 | 26 | /* 27 | *字典类型基本操作 28 | *author:刘飞华 29 | *date:2024/12/25 10:01:11 30 | */ 31 | rbatis::crud!(DictType {}, "sys_dict_type"); 32 | impl From for DictType { 33 | fn from(item: DictTypeReq) -> Self { 34 | let mut model = DictType { 35 | id: item.id, //字典主键 36 | dict_name: item.dict_name, //字典名称 37 | dict_type: item.dict_type, //字典类型 38 | status: item.status, //状态(0:停用,1:正常) 39 | remark: item.remark, //备注 40 | create_time: None, //创建时间 41 | update_time: None, //修改时间 42 | }; 43 | if let None = item.id { 44 | model.create_time = Some(DateTime::now()); 45 | } else { 46 | model.update_time = Some(DateTime::now()); 47 | } 48 | model 49 | } 50 | } 51 | 52 | impl Into for DictType { 53 | fn into(self) -> DictTypeResp { 54 | DictTypeResp { 55 | id: self.id, //字典主键 56 | dict_name: self.dict_name, //字典名称 57 | dict_type: self.dict_type, //字典类型 58 | status: self.status, //状态(0:停用,1:正常) 59 | remark: self.remark, //备注 60 | create_time: self.create_time, //创建时间 61 | update_time: self.update_time, //修改时间 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/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; 6 | use crate::vo::system::sys_login_log_vo::QueryLoginLogListReq; 7 | use rbatis::rbdc::datetime::DateTime; 8 | use rbatis::RBatis; 9 | use serde::{Deserialize, Serialize}; 10 | /* 11 | *系统访问记录 12 | *author:刘飞华 13 | *date:2024/12/25 10:01:11 14 | */ 15 | #[derive(Clone, Debug, Serialize, Deserialize)] 16 | pub struct LoginLog { 17 | pub id: Option, //访问ID 18 | pub login_name: String, //登录账号 19 | pub ipaddr: String, //登录IP地址 20 | pub login_location: String, //登录地点 21 | pub platform: String, //平台信息 22 | pub browser: String, //浏览器类型 23 | pub version: String, //浏览器版本 24 | pub os: String, //操作系统 25 | pub arch: String, //体系结构信息 26 | pub engine: String, //渲染引擎信息 27 | pub engine_details: String, //渲染引擎详细信息 28 | pub extra: String, //其他信息(可选) 29 | pub status: i8, //登录状态(0:失败,1:成功) 30 | pub msg: String, //提示消息 31 | pub login_time: Option, //访问时间 32 | } 33 | 34 | /* 35 | *系统访问记录基本操作 36 | *author:刘飞华 37 | *date:2024/12/25 10:01:11 38 | */ 39 | rbatis::crud!(LoginLog {}, "sys_login_log"); 40 | 41 | impl Into for LoginLog { 42 | fn into(self) -> LoginLogResp { 43 | LoginLogResp { 44 | id: self.id, //访问ID 45 | login_name: self.login_name, //登录账号 46 | ipaddr: self.ipaddr, //登录IP地址 47 | login_location: self.login_location, //登录地点 48 | platform: self.platform, //平台信息 49 | browser: self.browser, //浏览器类型 50 | version: self.version, //浏览器版本 51 | os: self.os, //操作系统 52 | arch: self.arch, //体系结构信息 53 | engine: self.engine, //渲染引擎信息 54 | engine_details: self.engine_details, //渲染引擎详细信息 55 | extra: self.extra, //其他信息(可选) 56 | status: self.status, //登录状态(0:失败,1:成功) 57 | msg: self.msg, //提示消息 58 | login_time: self.login_time, //访问时间 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_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; 6 | use crate::vo::system::sys_post_vo::PostResp; 7 | use crate::vo::system::sys_post_vo::QueryPostListReq; 8 | use rbatis::rbdc::datetime::DateTime; 9 | use serde::{Deserialize, Serialize}; 10 | /* 11 | *岗位信息 12 | *author:刘飞华 13 | *date:2024/12/25 10:01:11 14 | */ 15 | #[derive(Clone, Debug, Serialize, Deserialize)] 16 | pub struct Post { 17 | pub id: Option, //岗位id 18 | pub post_code: String, //岗位编码 19 | pub post_name: String, //岗位名称 20 | pub sort: i32, //显示顺序 21 | pub status: i8, //部状态(0:停用,1:正常) 22 | pub remark: Option, //备注 23 | pub create_time: Option, //创建时间 24 | pub update_time: Option, //更新时间 25 | } 26 | 27 | /* 28 | *岗位信息基本操作 29 | *author:刘飞华 30 | *date:2024/12/25 10:01:11 31 | */ 32 | rbatis::crud!(Post {}, "sys_post"); 33 | impl From for Post { 34 | fn from(item: PostReq) -> Self { 35 | let mut model = Post { 36 | id: item.id, //岗位id 37 | post_code: item.post_code, //岗位编码 38 | post_name: item.post_name, //岗位名称 39 | sort: item.sort, //显示顺序 40 | status: item.status, //部状态(0:停用,1:正常) 41 | remark: item.remark, //备注 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 Post { 55 | fn into(self) -> PostResp { 56 | PostResp { 57 | id: self.id, //岗位id 58 | post_code: self.post_code, //岗位编码 59 | post_name: self.post_name, //岗位名称 60 | sort: self.sort, //显示顺序 61 | status: self.status, //部状态(0:停用,1:正常) 62 | remark: self.remark, //备注 63 | create_time: self.create_time, //创建时间 64 | update_time: self.update_time, //更新时间 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, id:Option) -> Option => " 81 | `where post_code = #{post_code} ` 82 | if id != null: 83 | ` and id != #{id} ` 84 | limit 1" 85 | }, "sys_post"); 86 | 87 | /* 88 | *根据post_name查询岗位信息 89 | *author:刘飞华 90 | *date:2024/12/25 10:01:11 91 | */ 92 | impl_select!(Post{select_by_name(post_name:&str, id:Option) -> Option => " 93 | `where post_name = #{post_name} ` 94 | if id != null: 95 | ` and id != #{id} ` 96 | limit 1" 97 | }, "sys_post"); 98 | 99 | /* 100 | *分页查询岗位信息 101 | *author:刘飞华 102 | *date:2024/12/25 10:01:11 103 | */ 104 | impl_select_page!(Post{select_page() =>" 105 | if !sql.contains('count'): 106 | order by create_time desc" 107 | },"sys_post"); 108 | 109 | /* 110 | *根据条件分页查询岗位信息 111 | *author:刘飞华 112 | *date:2024/12/25 10:01:11 113 | */ 114 | impl_select_page!(Post{select_post_list(req:&QueryPostListReq) =>" 115 | where 1=1 116 | if req.postCode != null && req.postCode != '': 117 | ` and post_code like concat('%', #{req.postCode}, '%') ` 118 | if req.postName != null &&req. postName != '': 119 | ` and post_name like concat('%', #{req.postName}, '%') ` 120 | if req.status != 2: 121 | ` and status = #{req.status} ` 122 | if !sql.contains('count'): 123 | ` order by create_time desc" 124 | },"sys_post"); 125 | -------------------------------------------------------------------------------- /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; 5 | use crate::vo::system::sys_role_vo::RoleReq; 6 | use crate::vo::system::sys_role_vo::RoleResp; 7 | use rbatis::rbdc::datetime::DateTime; 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 Role { 16 | pub id: Option, //主键 17 | pub role_name: String, //名称 18 | pub role_key: String, //角色权限字符串 19 | pub data_scope: i8, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 20 | pub status: i8, //状态(1:正常,0:禁用) 21 | pub remark: Option, //备注 22 | pub del_flag: Option, //删除标志(0代表删除 1代表存在) 23 | pub create_time: Option, //创建时间 24 | pub update_time: Option, //修改时间 25 | } 26 | 27 | /* 28 | *角色信息基本操作 29 | *author:刘飞华 30 | *date:2024/12/12 14:41:44 31 | */ 32 | rbatis::crud!(Role {}, "sys_role"); 33 | impl From for Role { 34 | fn from(item: RoleReq) -> Self { 35 | let mut model = Role { 36 | id: item.id, //主键 37 | role_name: item.role_name, //名称 38 | role_key: item.role_key, //角色权限字符串 39 | data_scope: item.data_scope, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 40 | status: item.status, //状态(1:正常,0:禁用) 41 | remark: item.remark, //备注 42 | del_flag: None, //删除标志(0代表删除 1代表存在) 43 | create_time: None, //创建时间 44 | update_time: None, //修改时间 45 | }; 46 | if let None = item.id { 47 | model.create_time = Some(DateTime::now()); 48 | } else { 49 | model.update_time = Some(DateTime::now()); 50 | } 51 | model 52 | } 53 | } 54 | 55 | impl Into for Role { 56 | fn into(self) -> RoleResp { 57 | RoleResp { 58 | id: self.id, //主键 59 | role_name: self.role_name, //名称 60 | role_key: self.role_key, //角色权限字符串 61 | data_scope: self.data_scope, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 62 | status: self.status, //状态(1:正常,0:禁用) 63 | remark: self.remark, //备注 64 | create_time: self.create_time, //创建时间 65 | update_time: self.update_time, //修改时间 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/middleware/auth.rs: -------------------------------------------------------------------------------- 1 | use crate::common::result::BaseResponse; 2 | use crate::utils::jwt_util::JwtToken; 3 | use salvo::prelude::*; 4 | use salvo::{Depot, FlowCtrl, Request, Response}; 5 | use std::collections::HashMap; 6 | 7 | #[handler] 8 | pub async fn auth_token(req: &mut Request, res: &mut Response, ctrl: &mut FlowCtrl, depot: &mut Depot) { 9 | let item = match req.parse_headers::>() { 10 | Ok(item) => item, 11 | Err(e) => { 12 | return er_res(res, ctrl, e.to_string().as_str()); 13 | } 14 | }; 15 | 16 | let authorization = item.get("authorization"); 17 | let path = req.uri().path().to_string(); 18 | log::info!("req url:{}", path); 19 | 20 | match authorization { 21 | None => er_res(res, ctrl, "token不能为空"), 22 | Some(token) => { 23 | let split_vec = token.split_whitespace().collect::>(); 24 | if split_vec.len() != 2 || split_vec[0] != "Bearer" { 25 | return er_res(res, ctrl, "token格式错误"); 26 | } 27 | let token = split_vec[1]; 28 | 29 | match depot.get::("secret") { 30 | Ok(secret) => { 31 | let jwt_token_e = JwtToken::verify(secret, &token); 32 | let jwt_token = match jwt_token_e { 33 | Ok(data) => data, 34 | Err(err) => { 35 | return er_res(res, ctrl, err.to_string().as_str()); 36 | } 37 | }; 38 | 39 | if let Ok(pool) = depot.get::("pool") { 40 | if let Ok(mut conn) = pool.get().await { 41 | let key = format!("salvo:admin:user:info:{:?}", jwt_token.id); 42 | let values: HashMap = deadpool_redis::redis::cmd("HGETALL").arg(key).query_async(&mut conn).await.unwrap_or_default(); 43 | let token_1 = values.get("token").cloned().unwrap_or_default(); 44 | if token != token_1 { 45 | return er_res(res, ctrl, "无效的token"); 46 | } 47 | let permissions_str = values.get("permissions").cloned().unwrap_or_default(); 48 | let permissions: Vec = if permissions_str.is_empty() { 49 | Vec::new() 50 | } else { 51 | permissions_str.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() 52 | }; 53 | 54 | if permissions.len() == 0 { 55 | return er_res(res, ctrl, format!("你没有权限访问: {}", path).as_str()); 56 | } 57 | let is_admin = values.get("is_admin").map(|v| v == "1").unwrap_or(false); 58 | 59 | if is_admin || has_permission(&permissions, path.as_str()) { 60 | depot.insert("userId", jwt_token.id.clone()); 61 | depot.insert("username", jwt_token.username.clone()); 62 | } else { 63 | log::error!("你没有权限访问: {:?}", path); 64 | er_res(res, ctrl, format!("你没有权限访问: {}", path).as_str()) 65 | } 66 | } else { 67 | er_res(res, ctrl, "获取redis conn失败") 68 | } 69 | } else { 70 | er_res(res, ctrl, "获取redis pool异常") 71 | } 72 | } 73 | Err(_) => er_res(res, ctrl, "获取jwt密钥异常"), 74 | } 75 | } 76 | } 77 | } 78 | 79 | fn er_res(res: &mut Response, ctrl: &mut FlowCtrl, msg: &str) { 80 | let resp = BaseResponse { 81 | msg: msg.to_string(), 82 | code: 1, 83 | data: Some("None"), 84 | }; 85 | ctrl.skip_rest(); 86 | res.render(Json(resp)); 87 | } 88 | 89 | fn has_permission(permissions: &[String], path: &str) -> bool { 90 | permissions.iter().any(|permission| permission == path) 91 | } 92 | -------------------------------------------------------------------------------- /src/handler/system/sys_notice_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_notice_model::Notice; 8 | use crate::vo::system::sys_notice_vo::*; 9 | use crate::RB; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbatis::rbdc::DateTime; 12 | use rbs::value; 13 | use salvo::prelude::*; 14 | use salvo::{Request, Response}; 15 | 16 | /* 17 | *添加通知公告 18 | *author:刘飞华 19 | *date:2025/01/08 13:51:14 20 | */ 21 | #[handler] 22 | pub async fn add_sys_notice(req: &mut Request, res: &mut Response) -> AppResult { 23 | let mut item = req.parse_json::().await?; 24 | log::info!("add sys_notice params: {:?}", &item); 25 | 26 | let rb = &mut RB.clone(); 27 | 28 | if Notice::exists_by_title(rb, &item.notice_title).await? { 29 | return Err(AppError::BusinessError("公告标题已存在")); 30 | } 31 | 32 | item.id = None; 33 | Notice::insert(rb, &Notice::from(item)).await.map(|_| ok_result(res))? 34 | } 35 | 36 | /* 37 | *删除通知公告 38 | *author:刘飞华 39 | *date:2025/01/08 13:51:14 40 | */ 41 | #[handler] 42 | pub async fn delete_sys_notice(req: &mut Request, res: &mut Response) -> AppResult { 43 | let item = req.parse_json::().await?; 44 | 45 | log::info!("delete sys_notice params: {:?}", &item); 46 | 47 | let rb = &mut RB.clone(); 48 | 49 | Notice::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result(res))? 50 | } 51 | 52 | /* 53 | *更新通知公告 54 | *author:刘飞华 55 | *date:2025/01/08 13:51:14 56 | */ 57 | #[handler] 58 | pub async fn update_sys_notice(req: &mut Request, res: &mut Response) -> AppResult { 59 | let item = req.parse_json::().await?; 60 | log::info!("update sys_notice params: {:?}", &item); 61 | 62 | let rb = &mut RB.clone(); 63 | let id = item.id; 64 | if id.is_none() { 65 | return Err(AppError::BusinessError("主键不能为空")); 66 | } 67 | if Notice::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 68 | return Err(AppError::BusinessError("通知公告不存在")); 69 | }; 70 | 71 | if Notice::exists_by_title_except_id(rb, &item.notice_title, id.unwrap_or_default()).await? { 72 | return Err(AppError::BusinessError("公告标题已存在")); 73 | } 74 | 75 | Notice::update_by_map(rb, &Notice::from(item), value! {"id": &id}).await.map(|_| ok_result(res))? 76 | } 77 | 78 | /* 79 | *更新通知公告状态 80 | *author:刘飞华 81 | *date:2025/01/08 13:51:14 82 | */ 83 | #[handler] 84 | pub async fn update_sys_notice_status(req: &mut Request, res: &mut Response) -> AppResult { 85 | let item = req.parse_json::().await?; 86 | log::info!("update sys_notice_status params: {:?}", &item); 87 | 88 | let update_sql = format!( 89 | "update sys_notice set status = ? ,update_time = ? where id in ({})", 90 | item.ids.iter().map(|_| "?").collect::>().join(", ") 91 | ); 92 | 93 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 94 | param.extend(item.ids.iter().map(|&id| value!(id))); 95 | 96 | RB.clone().exec(&update_sql, param).await.map(|_| ok_result(res))? 97 | } 98 | 99 | /* 100 | *查询通知公告详情 101 | *author:刘飞华 102 | *date:2025/01/08 13:51:14 103 | */ 104 | #[handler] 105 | pub async fn query_sys_notice_detail(req: &mut Request, res: &mut Response) -> AppResult { 106 | let item = req.parse_json::().await?; 107 | 108 | log::info!("query sys_notice_detail params: {:?}", &item); 109 | 110 | Notice::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 111 | || Err(AppError::BusinessError("通知公告不存在")), 112 | |x| { 113 | let notice: NoticeResp = x.into(); 114 | ok_result_data(res, notice) 115 | }, 116 | ) 117 | } 118 | 119 | /* 120 | *查询通知公告列表 121 | *author:刘飞华 122 | *date:2025/01/08 13:51:14 123 | */ 124 | #[handler] 125 | pub async fn query_sys_notice_list(req: &mut Request, res: &mut Response) -> AppResult { 126 | let req = req.parse_json::().await?; 127 | log::info!("query sys_notice_list params: {:?}", &req); 128 | 129 | let rb = &mut RB.clone(); 130 | let item = &req; 131 | 132 | Notice::select_sys_notice_list(rb, &PageRequest::from(item), item) 133 | .await 134 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 135 | } 136 | -------------------------------------------------------------------------------- /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 rbatis::PageRequest; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /* 10 | 删除角色信息请求参数 11 | */ 12 | #[derive(Debug, Serialize, Deserialize)] 13 | pub struct DeleteRoleReq { 14 | pub ids: Vec, 15 | } 16 | 17 | /* 18 | 更新角色信息请求参数 19 | */ 20 | #[derive(Debug, Serialize, Deserialize)] 21 | #[serde(rename_all = "camelCase")] 22 | pub struct RoleReq { 23 | pub id: Option, //主键 24 | pub role_name: String, //名称 25 | pub role_key: String, //角色权限字符串 26 | pub data_scope: i8, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 27 | pub status: i8, //状态(1:正常,0:禁用) 28 | pub remark: Option, //备注 29 | } 30 | 31 | /* 32 | 更新角色信息状态请求参数 33 | */ 34 | #[derive(Debug, Serialize, Deserialize)] 35 | pub struct UpdateRoleStatusReq { 36 | pub ids: Vec, 37 | pub status: i8, 38 | } 39 | 40 | /* 41 | 查询角色信息详情请求参数 42 | */ 43 | #[derive(Debug, Serialize, Deserialize)] 44 | pub struct QueryRoleDetailReq { 45 | pub id: i64, 46 | } 47 | 48 | /* 49 | 查询角色信息列表请求参数 50 | */ 51 | #[derive(Debug, Serialize, Deserialize)] 52 | #[serde(rename_all = "camelCase")] 53 | pub struct QueryRoleListReq { 54 | pub page_no: u64, 55 | pub page_size: u64, 56 | pub role_name: Option, //名称 57 | #[serde(default = "default_status")] 58 | pub status: Option, //状态(1:正常,0:禁用) 59 | pub role_key: Option, //角色权限字符串 60 | } 61 | fn default_status() -> Option { 62 | Some(2) 63 | } 64 | impl From<&QueryRoleListReq> for PageRequest { 65 | fn from(value: &QueryRoleListReq) -> Self { 66 | PageRequest::new(value.page_no, value.page_size) 67 | } 68 | } 69 | /* 70 | 查询角色信息列表响应参数 71 | */ 72 | #[derive(Debug, Serialize, Deserialize)] 73 | #[serde(rename_all = "camelCase")] 74 | pub struct RoleResp { 75 | pub id: Option, //主键 76 | pub role_name: String, //名称 77 | pub role_key: String, //角色权限字符串 78 | pub data_scope: i8, //数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) 79 | pub status: i8, //状态(1:正常,0:禁用) 80 | pub remark: Option, //备注 81 | #[serde(serialize_with = "serialize_datetime")] 82 | pub create_time: Option, //创建时间 83 | #[serde(serialize_with = "serialize_datetime")] 84 | pub update_time: Option, //修改时间 85 | } 86 | 87 | /* 88 | 查询角色菜单信息参数 89 | */ 90 | #[derive(Debug, Deserialize)] 91 | #[serde(rename_all = "camelCase")] 92 | pub struct QueryRoleMenuReq { 93 | pub role_id: i64, //角色id 94 | } 95 | 96 | /* 97 | 角色菜单信息参数 98 | */ 99 | #[derive(Debug, Serialize)] 100 | #[serde(rename_all = "camelCase")] 101 | pub struct QueryRoleMenuData { 102 | pub menu_ids: Vec>, //菜单Ids 103 | pub menu_list: Vec, //菜单列表 104 | } 105 | 106 | /* 107 | 菜单信息参数 108 | */ 109 | #[derive(Debug, Serialize)] 110 | #[serde(rename_all = "camelCase")] 111 | pub struct MenuDataList { 112 | pub id: Option, //主键 113 | pub parent_id: Option, //父ID 114 | pub title: String, 115 | pub key: String, 116 | pub label: String, 117 | pub is_penultimate: bool, 118 | pub is_leaf: bool, 119 | } 120 | 121 | /* 122 | 更新用户角色信息 123 | */ 124 | #[derive(Debug, Deserialize)] 125 | #[serde(rename_all = "camelCase")] 126 | pub struct UpdateRoleMenuReq { 127 | pub menu_ids: Vec, 128 | pub role_id: i64, 129 | } 130 | 131 | /* 132 | 查询已分配用户角色列表 133 | */ 134 | #[derive(Debug, Deserialize)] 135 | #[serde(rename_all = "camelCase")] 136 | pub struct AllocatedListReq { 137 | pub page_no: u64, 138 | pub page_size: u64, 139 | pub role_id: i64, 140 | pub mobile: Option, 141 | pub user_name: Option, 142 | } 143 | 144 | /* 145 | 查询未分配用户角色列表 146 | */ 147 | #[derive(Debug, Deserialize)] 148 | #[serde(rename_all = "camelCase")] 149 | pub struct UnallocatedListReq { 150 | pub page_no: u64, 151 | pub page_size: u64, 152 | pub role_id: i64, 153 | pub mobile: Option, 154 | pub user_name: Option, 155 | } 156 | 157 | /* 158 | 取消授权用户 159 | */ 160 | #[derive(Debug, Deserialize)] 161 | #[serde(rename_all = "camelCase")] 162 | pub struct CancelAuthUserReq { 163 | pub user_id: i64, 164 | pub role_id: i64, 165 | } 166 | 167 | /* 168 | 批量取消授权用户 169 | */ 170 | #[derive(Debug, Deserialize)] 171 | #[serde(rename_all = "camelCase")] 172 | pub struct CancelAuthUserAllReq { 173 | pub user_ids: Vec, 174 | pub role_id: i64, 175 | } 176 | 177 | /* 178 | 批量选择用户授权 179 | */ 180 | #[derive(Debug, Deserialize)] 181 | #[serde(rename_all = "camelCase")] 182 | pub struct SelectAuthUserAllReq { 183 | pub user_ids: Vec, 184 | pub role_id: i64, 185 | } 186 | -------------------------------------------------------------------------------- /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; 6 | use crate::vo::system::sys_operate_log_vo::QueryOperateLogListReq; 7 | use rbatis::rbdc::datetime::DateTime; 8 | use rbatis::RBatis; 9 | use serde::{Deserialize, Serialize}; 10 | /* 11 | *操作日志记录 12 | *author:刘飞华 13 | *date:2024/12/25 10:01:11 14 | */ 15 | #[derive(Clone, Debug, Serialize, Deserialize)] 16 | pub struct OperateLog { 17 | pub id: Option, //日志主键 18 | pub title: Option, //模块标题 19 | pub business_type: Option, //业务类型(0其它 1新增 2修改 3删除) 20 | pub method: Option, //方法名称 21 | pub request_method: Option, //请求方式 22 | pub operator_type: Option, //操作类别(0其它 1后台用户 2手机端用户) 23 | pub operate_name: Option, //操作人员 24 | pub dept_name: Option, //部门名称 25 | pub operate_url: Option, //请求URL 26 | pub operate_ip: Option, //主机地址 27 | pub operate_location: Option, //操作地点 28 | pub operate_param: Option, //请求参数 29 | pub json_result: Option, //返回参数 30 | pub status: Option, //操作状态(0:异常,正常) 31 | pub error_msg: Option, //错误消息 32 | pub operate_time: Option, //操作时间 33 | pub cost_time: Option, //消耗时间 34 | } 35 | 36 | impl Into for OperateLog { 37 | fn into(self) -> OperateLogResp { 38 | OperateLogResp { 39 | id: self.id, //日志主键 40 | title: self.title, //模块标题 41 | business_type: self.business_type, //业务类型(0其它 1新增 2修改 3删除) 42 | method: self.method, //方法名称 43 | request_method: self.request_method, //请求方式 44 | operator_type: self.operator_type, //操作类别(0其它 1后台用户 2手机端用户) 45 | operate_name: self.operate_name, //操作人员 46 | dept_name: self.dept_name, //部门名称 47 | operate_url: self.operate_url, //请求URL 48 | operate_ip: self.operate_ip, //主机地址 49 | operate_location: self.operate_location, //操作地点 50 | operate_param: self.operate_param, //请求参数 51 | json_result: self.json_result, //返回参数 52 | status: self.status, //操作状态(0:异常,正常) 53 | error_msg: self.error_msg, //错误消息 54 | operate_time: self.operate_time, //操作时间 55 | cost_time: self.cost_time, //消耗时间 56 | } 57 | } 58 | } 59 | /* 60 | *操作日志记录基本操作 61 | *author:刘飞华 62 | *date:2024/12/25 10:01:11 63 | */ 64 | rbatis::crud!(OperateLog {}, "sys_operate_log"); 65 | 66 | /* 67 | *根据id查询操作日志记录 68 | *author:刘飞华 69 | *date:2024/12/25 10:01:11 70 | */ 71 | impl_select!(OperateLog{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_operate_log"); 72 | 73 | /* 74 | *分页查询操作日志记录 75 | *author:刘飞华 76 | *date:2024/12/25 10:01:11 77 | */ 78 | impl_select_page!(OperateLog{select_page() =>" 79 | if !sql.contains('count'): 80 | order by operate_time desc" 81 | },"sys_operate_log"); 82 | 83 | /* 84 | *根据条件分页查询操作日志记录 85 | *author:刘飞华 86 | *date:2024/12/25 10:01:11 87 | */ 88 | impl_select_page!(OperateLog{select_page_by_name( 89 | req:&QueryOperateLogListReq) =>" 90 | where 1=1 91 | if req.title != '' && req.title != null: 92 | ` and title like concat('%', #{req.title}, '%') ` 93 | if req.businessType != 4: 94 | ` and business_type = #{req.businessType} ` 95 | if req.method != '' && req.method != null: 96 | ` and method = #{req.method} ` 97 | if req.requestMethod != '' && req.requestMethod != null: 98 | ` and request_method = #{req.requestMethod} ` 99 | if req.operatorType != 3: 100 | ` and operator_type = #{req.operatorType} ` 101 | if req.operateName != '' && req.operateName != null: 102 | ` and operate_name = #{req.operateName} ` 103 | if req.deptName != '' && req.deptName != null: 104 | ` and dept_name = #{req.deptName} ` 105 | if req.operateUrl != '' && req.operateUrl != null: 106 | ` and operate_url = #{req.operateUrl} ` 107 | if req.operateIp != '' && req.operateUrl != null: 108 | ` and operate_ip = #{req.operateUrl} ` 109 | if req.status != 2: 110 | ` and status = #{req.status} ` 111 | if !sql.contains('count'): 112 | ` order by operate_time desc `" 113 | },"sys_operate_log"); 114 | 115 | /* 116 | *清空操作日志 117 | *author:刘飞华 118 | *date:2024/12/12 14:41:44 119 | */ 120 | #[sql("truncate table sys_operate_log")] 121 | pub async fn clean_operate_log(rb: &RBatis) -> Option { 122 | impled!() 123 | } 124 | -------------------------------------------------------------------------------- /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; 6 | use crate::vo::system::sys_notice_vo::NoticeResp; 7 | use crate::vo::system::sys_notice_vo::QueryNoticeListReq; 8 | use rbatis::rbdc::datetime::DateTime; 9 | use rbatis::{Error, RBatis}; 10 | use serde::{Deserialize, Serialize}; 11 | /* 12 | *通知公告表 13 | *author:刘飞华 14 | *date:2024/12/25 10:01:11 15 | */ 16 | #[derive(Clone, Debug, Serialize, Deserialize)] 17 | pub struct Notice { 18 | pub id: Option, //公告ID 19 | pub notice_title: String, //公告标题 20 | pub notice_type: i8, //公告类型(1:通知,2:公告) 21 | pub notice_content: String, //公告内容 22 | pub status: i8, //公告状态(0:关闭,1:正常 ) 23 | pub remark: Option, //备注 24 | pub create_time: Option, //创建时间 25 | pub update_time: Option, //修改时间 26 | } 27 | 28 | impl From for Notice { 29 | fn from(item: NoticeReq) -> Self { 30 | let mut model = Notice { 31 | id: item.id, //公告ID 32 | notice_title: item.notice_title, //公告标题 33 | notice_type: item.notice_type, //公告类型(1:通知,2:公告) 34 | notice_content: item.notice_content, //公告内容 35 | status: item.status, //公告状态(0:关闭,1:正常 ) 36 | remark: item.remark, //备注 37 | create_time: None, //创建时间 38 | update_time: None, //修改时间 39 | }; 40 | if let None = item.id { 41 | model.create_time = Some(DateTime::now()); 42 | } else { 43 | model.update_time = Some(DateTime::now()); 44 | } 45 | model 46 | } 47 | } 48 | 49 | impl Into for Notice { 50 | fn into(self) -> NoticeResp { 51 | NoticeResp { 52 | id: self.id, //公告ID 53 | notice_title: self.notice_title, //公告标题 54 | notice_type: self.notice_type, //公告类型(1:通知,2:公告) 55 | notice_content: self.notice_content, //公告内容 56 | status: self.status, //公告状态(0:关闭,1:正常 ) 57 | remark: self.remark, //备注 58 | create_time: self.create_time, //创建时间 59 | update_time: self.update_time, //修改时间 60 | } 61 | } 62 | } 63 | 64 | /* 65 | *通知公告表基本操作 66 | *author:刘飞华 67 | *date:2024/12/25 10:01:11 68 | */ 69 | rbatis::crud!(Notice {}, "sys_notice"); 70 | 71 | /* 72 | *根据id查询通知公告表 73 | *author:刘飞华 74 | *date:2024/12/25 10:01:11 75 | */ 76 | impl_select!(Notice{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_notice"); 77 | 78 | /* 79 | *根据公告标题查询通知公告表 80 | *author:刘飞华 81 | *date:2024/12/25 10:01:11 82 | */ 83 | impl_select!(Notice{select_by_title(title:&str) -> Option => "`where notice_title = #{title} limit 1`"}, "sys_notice"); 84 | 85 | /* 86 | *根据条件分页查询通知公告表 87 | *author:刘飞华 88 | *date:2024/12/25 10:01:11 89 | */ 90 | impl_select_page!(Notice{select_sys_notice_list(req:&QueryNoticeListReq) =>" 91 | where 1=1 92 | if req.noticeTitle != '' && req.noticeTitle != null: 93 | ` and notice_title like concat('%', #{req.noticeTitle}, '%') ` 94 | if req.noticeType != 0: 95 | ` and notice_type = #{req.noticeType} ` 96 | if req.status != 2: 97 | ` and status = #{req.status} ` 98 | if !sql.contains('count'): 99 | ` order by create_time desc `" 100 | },"sys_notice"); 101 | 102 | impl Notice { 103 | pub async fn exists_by_title(rb: &mut RBatis, title: &str) -> Result { 104 | let sql = "SELECT COUNT(*) FROM sys_notice WHERE notice_title = ?"; 105 | let count: i64 = rb.query_decode(sql, vec![title.into()]).await?; 106 | Ok(count > 0) 107 | } 108 | 109 | pub async fn exists_by_title_except_id(rb: &mut RBatis, title: &str, id: i64) -> Result { 110 | let sql = "SELECT COUNT(*) FROM sys_notice WHERE notice_title = ? and id != ?"; 111 | let count: i64 = rb.query_decode(sql, vec![title.into(), id.into()]).await?; 112 | Ok(count > 0) 113 | } 114 | // 115 | // pub async fn exists_by_id(rb: &mut RBatis, id: &i32) -> Result { 116 | // let sql = "SELECT COUNT(*) FROM sys_notice WHERE notice_title = ?"; 117 | // let count: i64 = rb.query_decode(sql, vec![title.into()]).await?; 118 | // Ok(count > 0) 119 | // } 120 | // 121 | // pub async fn update_status_by_ids( 122 | // rb: &mut RBatis, 123 | // ids: &[i32], 124 | // status: i32, 125 | // ) -> Result<(), Error> { 126 | // let sql = "SELECT COUNT(*) FROM sys_notice WHERE notice_title = ?"; 127 | // let count: i64 = rb.query_decode(sql, vec![title.into()]).await?; 128 | // Ok(count > 0) 129 | // } 130 | } 131 | -------------------------------------------------------------------------------- /src/handler/system/sys_dict_data_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_dict_data_model::DictData; 8 | use crate::vo::system::sys_dict_data_vo::*; 9 | use crate::RB; 10 | use rbatis::plugin::page::PageRequest; 11 | use rbatis::rbdc::DateTime; 12 | use rbs::value; 13 | use salvo::prelude::*; 14 | use salvo::{Request, Response}; 15 | 16 | /* 17 | *添加字典数据 18 | *author:刘飞华 19 | *date:2025/01/08 13:51:14 20 | */ 21 | #[handler] 22 | pub async fn add_sys_dict_data(req: &mut Request, res: &mut Response) -> AppResult { 23 | let mut item = req.parse_json::().await?; 24 | 25 | log::info!("add sys_dict_data params: {:?}", &item); 26 | 27 | let rb = &mut RB.clone(); 28 | if DictData::select_by_dict_label(rb, &item.dict_type, &item.dict_label).await?.is_some() { 29 | return Err(AppError::BusinessError("字典标签已存在")); 30 | } 31 | 32 | if DictData::select_by_dict_value(rb, &item.dict_type, &item.dict_value).await?.is_some() { 33 | return Err(AppError::BusinessError("字典键值已存在")); 34 | } 35 | 36 | item.id = None; 37 | DictData::insert(rb, &DictData::from(item)).await.map(|_| ok_result(res))? 38 | } 39 | 40 | /* 41 | *删除字典数据 42 | *author:刘飞华 43 | *date:2025/01/08 13:51:14 44 | */ 45 | #[handler] 46 | pub async fn delete_sys_dict_data(req: &mut Request, res: &mut Response) -> AppResult { 47 | let item = req.parse_json::().await?; 48 | log::info!("delete sys_dict_data params: {:?}", &item); 49 | 50 | let rb = &mut RB.clone(); 51 | 52 | DictData::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result(res))? 53 | } 54 | 55 | /* 56 | *更新字典数据 57 | *author:刘飞华 58 | *date:2025/01/08 13:51:14 59 | */ 60 | #[handler] 61 | pub async fn update_sys_dict_data(req: &mut Request, res: &mut Response) -> AppResult { 62 | let item = req.parse_json::().await?; 63 | log::info!("update sys_dict_data params: {:?}", &item); 64 | 65 | let rb = &mut RB.clone(); 66 | 67 | let id = item.id; 68 | if id.is_none() { 69 | return Err(AppError::BusinessError("主键不能为空")); 70 | } 71 | if DictData::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 72 | return Err(AppError::BusinessError("字典数据不存在")); 73 | } 74 | 75 | if let Some(x) = DictData::select_by_dict_label(rb, &item.dict_type, &item.dict_label).await? { 76 | if x.id != id { 77 | return Err(AppError::BusinessError("字典标签已存在")); 78 | } 79 | } 80 | 81 | if let Some(x) = DictData::select_by_dict_value(rb, &item.dict_type, &item.dict_value).await? { 82 | if x.id != id { 83 | return Err(AppError::BusinessError("字典键值已存在")); 84 | } 85 | } 86 | 87 | DictData::update_by_map(rb, &DictData::from(item), value! {"id": &id}).await.map(|_| ok_result(res))? 88 | } 89 | 90 | /* 91 | *更新字典数据状态 92 | *author:刘飞华 93 | *date:2025/01/08 13:51:14 94 | */ 95 | #[handler] 96 | pub async fn update_sys_dict_data_status(req: &mut Request, res: &mut Response) -> AppResult { 97 | let item = req.parse_json::().await?; 98 | log::info!("update sys_dict_data_status params: {:?}", &item); 99 | 100 | let update_sql = format!( 101 | "update sys_dict_data set status = ? ,update_time = ? where id in ({})", 102 | item.ids.iter().map(|_| "?").collect::>().join(", ") 103 | ); 104 | 105 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 106 | param.extend(item.ids.iter().map(|&id| value!(id))); 107 | 108 | RB.clone().exec(&update_sql, param).await.map(|_| ok_result(res))? 109 | } 110 | 111 | /* 112 | *查询字典数据详情 113 | *author:刘飞华 114 | *date:2025/01/08 13:51:14 115 | */ 116 | #[handler] 117 | pub async fn query_sys_dict_data_detail(req: &mut Request, res: &mut Response) -> AppResult { 118 | let item = req.parse_json::().await?; 119 | log::info!("query sys_dict_data_detail params: {:?}", &item); 120 | 121 | DictData::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 122 | || Err(AppError::BusinessError("字典数据不存在")), 123 | |x| { 124 | let data: DictDataResp = x.into(); 125 | ok_result_data(res, data) 126 | }, 127 | ) 128 | } 129 | 130 | /* 131 | *查询字典数据列表 132 | *author:刘飞华 133 | *date:2025/01/08 13:51:14 134 | */ 135 | #[handler] 136 | pub async fn query_sys_dict_data_list(req: &mut Request, res: &mut Response) -> AppResult { 137 | let req = req.parse_json::().await?; 138 | log::info!("query sys_dict_data_list params: {:?}", &req); 139 | 140 | let rb = &mut RB.clone(); 141 | let item = &req; 142 | 143 | DictData::select_dict_data_list(rb, &PageRequest::from(item), item) 144 | .await 145 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 146 | } 147 | -------------------------------------------------------------------------------- /src/handler/system/sys_post_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_post_model::Post; 8 | use crate::model::system::sys_user_post_model::count_user_post_by_id; 9 | use crate::vo::system::sys_post_vo::*; 10 | use crate::RB; 11 | use rbatis::plugin::page::PageRequest; 12 | use rbatis::rbdc::DateTime; 13 | use rbs::value; 14 | use salvo::prelude::*; 15 | use salvo::{Request, Response}; 16 | /* 17 | *添加岗位信息 18 | *author:刘飞华 19 | *date:2025/01/08 13:51:14 20 | */ 21 | #[handler] 22 | pub async fn add_sys_post(req: &mut Request, res: &mut Response) -> AppResult { 23 | let mut item = req.parse_json::().await?; 24 | log::info!("add sys_post params: {:?}", &item); 25 | 26 | let rb = &mut RB.clone(); 27 | 28 | if Post::select_by_name(rb, &item.post_name, None).await?.is_some() { 29 | return Err(AppError::BusinessError("岗位名称已存在")); 30 | } 31 | 32 | if Post::select_by_code(rb, &item.post_code, None).await?.is_some() { 33 | return Err(AppError::BusinessError("岗位编码已存在")); 34 | } 35 | 36 | item.id = None; 37 | Post::insert(rb, &Post::from(item)).await.map(|_| ok_result(res))? 38 | } 39 | 40 | /* 41 | *删除岗位信息 42 | *author:刘飞华 43 | *date:2025/01/08 13:51:14 44 | */ 45 | #[handler] 46 | pub async fn delete_sys_post(req: &mut Request, res: &mut Response) -> AppResult { 47 | let item = req.parse_json::().await?; 48 | log::info!("delete sys_post params: {:?}", &item); 49 | 50 | let ids = item.ids.clone(); 51 | let rb = &mut RB.clone(); 52 | for id in ids { 53 | match Post::select_by_id(rb, &id).await? { 54 | None => return Err(AppError::BusinessError("不能删除")), 55 | Some(_) => { 56 | if count_user_post_by_id(rb, id).await? > 0 { 57 | return Err(AppError::BusinessError("已分配,不能删除")); 58 | } 59 | } 60 | }; 61 | } 62 | 63 | Post::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result(res))? 64 | } 65 | 66 | /* 67 | *更新岗位信息 68 | *author:刘飞华 69 | *date:2025/01/08 13:51:14 70 | */ 71 | #[handler] 72 | pub async fn update_sys_post(req: &mut Request, res: &mut Response) -> AppResult { 73 | let item = req.parse_json::().await?; 74 | log::info!("update sys_post params: {:?}", &item); 75 | 76 | let rb = &mut RB.clone(); 77 | 78 | let id = item.id; 79 | 80 | if id.is_none() { 81 | return Err(AppError::BusinessError("主键不能为空")); 82 | } 83 | if Post::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 84 | return Err(AppError::BusinessError("岗位不存在")); 85 | } 86 | 87 | if Post::select_by_name(rb, &item.post_name, id).await?.is_some() { 88 | return Err(AppError::BusinessError("岗位名称已存在")); 89 | } 90 | 91 | if Post::select_by_code(rb, &item.post_code, id).await?.is_some() { 92 | return Err(AppError::BusinessError("岗位编码已存在")); 93 | } 94 | 95 | Post::update_by_map(rb, &Post::from(item), value! {"id": &id}).await.map(|_| ok_result(res))? 96 | } 97 | 98 | /* 99 | *更新岗位信息状态 100 | *author:刘飞华 101 | *date:2025/01/08 13:51:14 102 | */ 103 | #[handler] 104 | pub async fn update_sys_post_status(req: &mut Request, res: &mut Response) -> AppResult { 105 | let item = req.parse_json::().await?; 106 | log::info!("update sys_post_status params: {:?}", &item); 107 | 108 | let update_sql = format!( 109 | "update sys_post set status = ? ,update_time = ? where id in ({})", 110 | item.ids.iter().map(|_| "?").collect::>().join(", ") 111 | ); 112 | 113 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 114 | param.extend(item.ids.iter().map(|&id| value!(id))); 115 | 116 | RB.clone().exec(&update_sql, param).await.map(|_| ok_result(res))? 117 | } 118 | 119 | /* 120 | *查询岗位信息详情 121 | *author:刘飞华 122 | *date:2025/01/08 13:51:14 123 | */ 124 | #[handler] 125 | pub async fn query_sys_post_detail(req: &mut Request, res: &mut Response) -> AppResult { 126 | let item = req.parse_json::().await?; 127 | log::info!("query sys_post_detail params: {:?}", &item); 128 | 129 | Post::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 130 | || Err(AppError::BusinessError("岗位不存在")), 131 | |x| { 132 | let data: PostResp = x.into(); 133 | ok_result_data(res, data) 134 | }, 135 | ) 136 | } 137 | 138 | /* 139 | *查询岗位信息列表 140 | *author:刘飞华 141 | *date:2025/01/08 13:51:14 142 | */ 143 | #[handler] 144 | pub async fn query_sys_post_list(req: &mut Request, res: &mut Response) -> AppResult { 145 | let req = req.parse_json::().await?; 146 | log::info!("query sys_post_list params: {:?}", &req); 147 | 148 | let rb = &mut RB.clone(); 149 | let item = &req; 150 | 151 | Post::select_post_list(rb, &PageRequest::from(item), item) 152 | .await 153 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 154 | } 155 | -------------------------------------------------------------------------------- /src/handler/system/sys_dict_type_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_dict_data_model::{count_dict_data_by_type, update_dict_data_type}; 8 | use crate::model::system::sys_dict_type_model::DictType; 9 | use crate::vo::system::sys_dict_type_vo::*; 10 | use crate::RB; 11 | use rbatis::plugin::page::PageRequest; 12 | use rbatis::rbdc::DateTime; 13 | use rbs::value; 14 | use salvo::prelude::*; 15 | use salvo::{Request, Response}; 16 | 17 | /* 18 | *添加字典类型 19 | *author:刘飞华 20 | *date:2025/01/08 13:51:14 21 | */ 22 | #[handler] 23 | pub async fn add_sys_dict_type(req: &mut Request, res: &mut Response) -> AppResult { 24 | let mut item = req.parse_json::().await?; 25 | log::info!("add sys_dict_type params: {:?}", &item); 26 | 27 | let rb = &mut RB.clone(); 28 | if DictType::select_by_dict_type(rb, &item.dict_type).await?.is_some() { 29 | return Err(AppError::BusinessError("字典类型已存在")); 30 | } 31 | 32 | item.id = None; 33 | DictType::insert(rb, &DictType::from(item)).await.map(|_| ok_result(res))? 34 | } 35 | 36 | /* 37 | *删除字典类型 38 | *author:刘飞华 39 | *date:2025/01/08 13:51:14 40 | */ 41 | #[handler] 42 | pub async fn delete_sys_dict_type(req: &mut Request, res: &mut Response) -> AppResult { 43 | let item = req.parse_json::().await?; 44 | log::info!("delete sys_dict_type params: {:?}", &item); 45 | 46 | let rb = &mut RB.clone(); 47 | let ids = item.ids.clone(); 48 | for id in ids { 49 | let p = match DictType::select_by_id(rb, &id).await? { 50 | None => return Err(AppError::BusinessError("字典类型不存在,不能删除")), 51 | Some(x) => x, 52 | }; 53 | 54 | let res_count = count_dict_data_by_type(rb, &p.dict_type).await?; 55 | if res_count > 0 { 56 | return Err(AppError::BusinessError("已分配,不能删除")); 57 | } 58 | } 59 | 60 | DictType::delete_by_map(rb, value! {"id": &item.ids}).await.map(|_| ok_result(res))? 61 | } 62 | 63 | /* 64 | *更新字典类型 65 | *author:刘飞华 66 | *date:2025/01/08 13:51:14 67 | */ 68 | #[handler] 69 | pub async fn update_sys_dict_type(req: &mut Request, res: &mut Response) -> AppResult { 70 | let item = req.parse_json::().await?; 71 | log::info!("update sys_dict_type params: {:?}", &item); 72 | 73 | let rb = &mut RB.clone(); 74 | let id = item.id; 75 | 76 | if id.is_none() { 77 | return Err(AppError::BusinessError("主键不能为空")); 78 | } 79 | if DictType::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 80 | return Err(AppError::BusinessError("字典类型不存在")); 81 | } 82 | 83 | if let Some(x) = DictType::select_by_dict_type(rb, &item.dict_type).await? { 84 | if x.id != id { 85 | return Err(AppError::BusinessError("字典类型已存在")); 86 | } 87 | 88 | let dict_type = x.dict_type; 89 | update_dict_data_type(rb, &*item.dict_type, &dict_type).await?; 90 | } 91 | 92 | DictType::update_by_map(rb, &DictType::from(item), value! {"id": &id}).await.map(|_| ok_result(res))? 93 | } 94 | 95 | /* 96 | *更新字典类型状态 97 | *author:刘飞华 98 | *date:2025/01/08 13:51:14 99 | */ 100 | #[handler] 101 | pub async fn update_sys_dict_type_status(req: &mut Request, res: &mut Response) -> AppResult { 102 | let item = req.parse_json::().await?; 103 | 104 | let rb = &mut RB.clone(); 105 | log::info!("update sys_dict_type_status params: {:?}", &item); 106 | 107 | let update_sql = format!( 108 | "update sys_dict_type set status = ? ,update_time = ? where id in ({})", 109 | item.ids.iter().map(|_| "?").collect::>().join(", ") 110 | ); 111 | 112 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 113 | param.extend(item.ids.iter().map(|&id| value!(id))); 114 | 115 | rb.exec(&update_sql, param).await.map(|_| ok_result(res))? 116 | } 117 | 118 | /* 119 | *查询字典类型详情 120 | *author:刘飞华 121 | *date:2025/01/08 13:51:14 122 | */ 123 | #[handler] 124 | pub async fn query_sys_dict_type_detail(req: &mut Request, res: &mut Response) -> AppResult { 125 | let item = req.parse_json::().await?; 126 | 127 | log::info!("query sys_dict_type_detail params: {:?}", &item); 128 | 129 | DictType::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 130 | || Err(AppError::BusinessError("字典类型不存在")), 131 | |x| { 132 | let data: DictTypeResp = x.into(); 133 | ok_result_data(res, data) 134 | }, 135 | ) 136 | } 137 | 138 | /* 139 | *查询字典类型列表 140 | *author:刘飞华 141 | *date:2025/01/08 13:51:14 142 | */ 143 | #[handler] 144 | pub async fn query_sys_dict_type_list(req: &mut Request, res: &mut Response) -> AppResult { 145 | let req = req.parse_json::().await?; 146 | log::info!("query sys_dict_type_list params: {:?}", &req); 147 | 148 | let rb = &mut RB.clone(); 149 | let item = &req; 150 | 151 | DictType::select_dict_type_list(rb, &PageRequest::from(item), item) 152 | .await 153 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 154 | } 155 | -------------------------------------------------------------------------------- /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; 6 | use crate::vo::system::sys_dict_data_vo::DictDataResp; 7 | use crate::vo::system::sys_dict_data_vo::QueryDictDataListReq; 8 | use rbatis::rbdc::datetime::DateTime; 9 | use rbatis::RBatis; 10 | use serde::{Deserialize, Serialize}; 11 | /* 12 | *字典数据表 13 | *author:刘飞华 14 | *date:2024/12/25 10:01:11 15 | */ 16 | #[derive(Clone, Debug, Serialize, Deserialize)] 17 | pub struct DictData { 18 | pub id: Option, //字典编码 19 | pub dict_sort: i32, //字典排序 20 | pub dict_label: String, //字典标签 21 | pub dict_value: String, //字典键值 22 | pub dict_type: String, //字典类型 23 | pub css_class: String, //样式属性(其他样式扩展) 24 | pub list_class: String, //表格回显样式 25 | pub is_default: String, //是否默认(Y是 N否) 26 | pub status: i8, //状态(0:停用,1:正常) 27 | pub remark: Option, //备注 28 | pub create_time: Option, //创建时间 29 | pub update_time: Option, //修改时间 30 | } 31 | 32 | /* 33 | *字典数据表基本操作 34 | *author:刘飞华 35 | *date:2024/12/25 10:01:11 36 | */ 37 | rbatis::crud!(DictData {}, "sys_dict_data"); 38 | 39 | impl From for DictData { 40 | fn from(item: DictDataReq) -> Self { 41 | let mut model = DictData { 42 | id: item.id, //字典编码 43 | dict_sort: item.dict_sort, //字典排序 44 | dict_label: item.dict_label, //字典标签 45 | dict_value: item.dict_value, //字典键值 46 | dict_type: item.dict_type, //字典类型 47 | css_class: item.css_class, //样式属性(其他样式扩展) 48 | list_class: item.list_class, //格回显样式 49 | is_default: item.is_default, //是否默认(Y是 N否) 50 | status: item.status, //状态(0:停用,1:正常) 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 DictData { 65 | fn into(self) -> DictDataResp { 66 | DictDataResp { 67 | id: self.id, //字典编码 68 | dict_sort: self.dict_sort, //字典排序 69 | dict_label: self.dict_label, //字典标签 70 | dict_value: self.dict_value, //字典键值 71 | dict_type: self.dict_type, //字典类型 72 | css_class: self.css_class, //样式属性(其他样式扩展) 73 | list_class: self.list_class, //格回显样式 74 | is_default: self.is_default, //是否默认(Y是 N否) 75 | status: self.status, //状态(0:停用,1:正常) 76 | remark: self.remark, //备注 77 | create_time: self.create_time, //创建时间 78 | update_time: self.update_time, //修改时间 79 | } 80 | } 81 | } 82 | 83 | /* 84 | *根据id查询字典数据表 85 | *author:刘飞华 86 | *date:2024/12/25 10:01:11 87 | */ 88 | impl_select!(DictData{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_dict_data"); 89 | 90 | /* 91 | *根据dict_type和dict_label查询字典数据表 92 | *author:刘飞华 93 | *date:2024/12/25 10:01:11 94 | */ 95 | 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"); 96 | 97 | /* 98 | *根据dict_type和dict_value查询字典数据表 99 | *author:刘飞华 100 | *date:2024/12/25 10:01:11 101 | */ 102 | 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"); 103 | 104 | /* 105 | *分页查询字典数据表 106 | *author:刘飞华 107 | *date:2024/12/25 10:01:11 108 | */ 109 | impl_select_page!(DictData{select_page() =>" 110 | if !sql.contains('count'): 111 | order by create_time desc" 112 | },"sys_dict_data"); 113 | 114 | /* 115 | *根据条件分页查询字典数据表 116 | *author:刘飞华 117 | *date:2024/12/25 10:01:11 118 | */ 119 | impl_select_page!(DictData{select_dict_data_list(req:&QueryDictDataListReq) =>" 120 | where 1=1 121 | if req.dictLabel != null && req.dictLabel != '': 122 | ` and dict_label like concat('%', #{req.dictLabel}, '%') ` 123 | if req.dictType != null && req.dictType != '': 124 | ` and dict_type like concat('%', #{req.dictType}, '%') ` 125 | if req.status != 2: 126 | ` and status = #{req.status} ` 127 | if !sql.contains('count'): 128 | ` order by create_time desc" 129 | },"sys_dict_data"); 130 | 131 | /* 132 | *同步修改字典类型 133 | *author:刘飞华 134 | *date:2024/12/25 10:01:11 135 | */ 136 | #[sql("update sys_dict_data set dict_type = ? where dict_type = ?")] 137 | pub async fn update_dict_data_type(rb: &RBatis, new_dict_type: &str, old_dict_type: &str) -> Option { 138 | impled!() 139 | } 140 | 141 | /* 142 | *查询字典数据 143 | *author:刘飞华 144 | *date:2024/12/25 10:01:11 145 | */ 146 | #[sql("select count(1) from sys_dict_data where dict_type= ?")] 147 | pub async fn count_dict_data_by_type(rb: &RBatis, dict_type: &str) -> rbatis::Result { 148 | impled!() 149 | } 150 | -------------------------------------------------------------------------------- /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; 6 | use crate::vo::system::sys_dept_vo::DeptResp; 7 | use crate::vo::system::sys_dept_vo::QueryDeptListReq; 8 | use rbatis::rbdc::datetime::DateTime; 9 | use rbatis::RBatis; 10 | use serde::{Deserialize, Serialize}; 11 | /* 12 | *部门 13 | *author:刘飞华 14 | *date:2024/12/25 10:01:11 15 | */ 16 | #[derive(Clone, Debug, Serialize, Deserialize)] 17 | pub struct Dept { 18 | pub id: Option, //部门id 19 | pub parent_id: i64, //父部门id 20 | pub ancestors: Option, //祖级列 21 | pub dept_name: String, //部门名称 22 | pub sort: i32, //显示顺序 23 | pub leader: String, //负责人 24 | pub phone: String, //联系电话 25 | pub email: String, //邮箱 26 | pub status: i8, //部状态(0:停用,1:正常) 27 | pub del_flag: Option, //删除标志(0代删除 1代存在) 28 | pub create_time: Option, //创建时间 29 | pub update_time: Option, //修改时间 30 | } 31 | 32 | /* 33 | *部门基本操作 34 | *author:刘飞华 35 | *date:2024/12/25 10:01:11 36 | */ 37 | rbatis::crud!(Dept {}, "sys_dept"); 38 | 39 | impl From for Dept { 40 | fn from(item: DeptReq) -> Self { 41 | let mut model = Dept { 42 | id: item.id, //部门id 43 | parent_id: item.parent_id, //父部门id 44 | ancestors: item.ancestors, //祖级列表 45 | dept_name: item.dept_name, //部门名称 46 | sort: item.sort, //显示顺序 47 | leader: item.leader, //负责人 48 | phone: item.phone, //联系电话 49 | email: item.email, //邮箱 50 | status: item.status, //部状态(0:停用,1:正常) 51 | del_flag: None, //删除标志(0代表删除 1代表存在) 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 Dept { 65 | fn into(self) -> DeptResp { 66 | DeptResp { 67 | id: self.id, //部门id 68 | key: self.id.unwrap().to_string(), //部门id 69 | parent_id: self.parent_id, //父部门id 70 | ancestors: self.ancestors, //祖级列表 71 | dept_name: self.dept_name.clone(), //部门名称 72 | title: self.dept_name, //部门名称 73 | sort: self.sort, //显示顺序 74 | leader: self.leader, //负责人 75 | phone: self.phone, //联系电话 76 | email: self.email, //邮箱 77 | status: self.status, //部状态(0:停用,1:正常) 78 | create_time: self.create_time, //创建时间 79 | update_time: self.update_time, //修改时间 80 | } 81 | } 82 | } 83 | /* 84 | *根据id查询部门 85 | *author:刘飞华 86 | *date:2024/12/25 10:01:11 87 | */ 88 | impl_select!(Dept{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_dept"); 89 | 90 | /* 91 | *根据部门名称查询部门 92 | *author:刘飞华 93 | *date:2024/12/25 10:01:11 94 | */ 95 | 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"); 96 | 97 | /* 98 | *分页查询部门 99 | *author:刘飞华 100 | *date:2024/12/25 10:01:11 101 | */ 102 | impl_select_page!(Dept{select_page() =>" 103 | if !sql.contains('count'): 104 | order by create_time desc" 105 | },"sys_dept"); 106 | 107 | /* 108 | *根据条件分页查询部门 109 | *author:刘飞华 110 | *date:2024/12/25 10:01:11 111 | */ 112 | impl_select!(Dept{select_page_dept_list(req:&QueryDeptListReq) =>" 113 | where 1=1 114 | if req.deptName != null && req.deptName != '': 115 | ` and dept_name like concat('%', #{req.deptName}, '%') ` 116 | if req.status != 2: 117 | ` and status = #{req.status} ` 118 | if !sql.contains('count'): 119 | ` order by sort" 120 | },"sys_dept"); 121 | 122 | /* 123 | *根据部门id查询是否有下级部门 124 | *author:刘飞华 125 | *date:2024/12/12 14:41:44 126 | */ 127 | #[sql("select count(*) from sys_dept where status = 1 and del_flag = '1' and find_in_set(?, ancestors)")] 128 | pub async fn select_normal_children_dept_by_id(rb: &RBatis, id: &i64) -> rbatis::Result { 129 | impled!() 130 | } 131 | 132 | /* 133 | *根据父部门id查询下级部门数量 134 | *author:刘飞华 135 | *date:2024/12/12 14:41:44 136 | */ 137 | #[sql("select count(1) from sys_dept where del_flag = '1' and parent_id = ?")] 138 | pub async fn select_dept_count(rb: &RBatis, id: &i64) -> rbatis::Result { 139 | impled!() 140 | } 141 | 142 | /* 143 | *查询部门是否存在用户 144 | *author:刘飞华 145 | *date:2024/12/12 14:41:44 146 | */ 147 | #[sql("select count(1) from sys_user where dept_id = ? and del_flag = '1'")] 148 | pub async fn check_dept_exist_user(rb: &RBatis, id: &i64) -> rbatis::Result { 149 | impled!() 150 | } 151 | 152 | /* 153 | * 描述:根据部门id查询是所有下级部门 154 | * author:刘飞华 155 | * date:2025/1/6 11:29 156 | */ 157 | #[sql("select * from sys_dept where find_in_set(?, ancestors)")] 158 | pub async fn select_children_dept_by_id(rb: &RBatis, id: &i64) -> rbatis::Result> { 159 | impled!() 160 | } 161 | -------------------------------------------------------------------------------- /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 rbatis::rbdc::DateTime; 7 | use rbatis::PageRequest; 8 | use serde::{Deserialize, Serialize}; 9 | 10 | use crate::vo::system::sys_role_vo::RoleResp; 11 | /* 12 | 删除用户信息请求参数 13 | */ 14 | #[derive(Debug, Serialize, Deserialize)] 15 | pub struct DeleteUserReq { 16 | pub ids: Vec, 17 | } 18 | 19 | /* 20 | 更新用户信息请求参数 21 | */ 22 | #[derive(Debug, Serialize, Deserialize)] 23 | #[serde(rename_all = "camelCase")] 24 | pub struct UserReq { 25 | pub id: Option, //主键 26 | pub mobile: String, //手机 27 | pub user_name: String, //用户账号 28 | pub nick_name: String, //用户昵称 29 | pub password: Option, //用户密码 30 | pub email: String, //用户邮箱 31 | #[serde(default = "default_avatar")] 32 | pub avatar: Option, //头像路径 33 | pub status: i8, //状态(1:正常,0:禁用) 34 | pub dept_id: i64, //部门ID 35 | pub remark: Option, //备注 36 | pub post_ids: Vec, //岗位ids 37 | } 38 | fn default_avatar() -> Option { 39 | Some("https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png".to_string()) 40 | } 41 | /* 42 | 更新用户信息状态请求参数 43 | */ 44 | #[derive(Debug, Serialize, Deserialize)] 45 | pub struct UpdateUserStatusReq { 46 | pub ids: Vec, 47 | pub status: i8, 48 | } 49 | 50 | /* 51 | 查询用户信息详情请求参数 52 | */ 53 | #[derive(Debug, Serialize, Deserialize)] 54 | pub struct QueryUserDetailReq { 55 | pub id: i64, 56 | } 57 | 58 | /* 59 | 查询用户信息列表请求参数 60 | */ 61 | #[derive(Debug, Serialize, Deserialize)] 62 | #[serde(rename_all = "camelCase")] 63 | pub struct QueryUserListReq { 64 | pub page_no: u64, 65 | pub page_size: u64, 66 | pub mobile: Option, //手机 67 | pub user_name: Option, //姓名 68 | #[serde(default = "default_status")] 69 | pub status: Option, //状态(1:正常,0:禁用) 70 | pub dept_id: Option, //部门ID 71 | } 72 | fn default_status() -> Option { 73 | Some(2) 74 | } 75 | impl From<&QueryUserListReq> for PageRequest { 76 | fn from(value: &QueryUserListReq) -> Self { 77 | PageRequest::new(value.page_no, value.page_size) 78 | } 79 | } 80 | /* 81 | 查询用户信息列表响应参数 82 | */ 83 | #[derive(Debug, Serialize, Deserialize)] 84 | #[serde(rename_all = "camelCase")] 85 | pub struct UserResp { 86 | pub id: Option, //主键 87 | pub mobile: String, //手机 88 | pub user_name: String, //用户账号 89 | pub nick_name: String, //用户昵称 90 | pub user_type: String, //用户类型(00系统用户) 91 | pub email: String, //用户邮箱 92 | pub avatar: Option, //头像路径 93 | pub status: i8, //状态(1:正常,0:禁用) 94 | pub dept_id: i64, //部门ID 95 | pub login_ip: String, //最后登录IP 96 | #[serde(serialize_with = "serialize_datetime")] 97 | pub login_date: Option, //最后登录时间 98 | pub login_browser: String, //浏览器类型 99 | pub login_os: String, //操作系统 100 | #[serde(serialize_with = "serialize_datetime")] 101 | pub pwd_update_date: Option, //密码最后更新时间 102 | pub remark: Option, //备注 103 | #[serde(serialize_with = "serialize_datetime")] 104 | pub create_time: Option, //创建时间 105 | #[serde(serialize_with = "serialize_datetime")] 106 | pub update_time: Option, //修改时间 107 | pub dept_info: Option, //部门详细信息 108 | pub post_ids: Option>, //岗位ids 109 | } 110 | /* 111 | 登录请求参数 112 | */ 113 | #[derive(Debug, Serialize,Deserialize)] 114 | pub struct UserLoginReq { 115 | pub account: String, //手机 116 | pub password: String, //密码 117 | } 118 | 119 | /* 120 | 查询用户菜单响应参数 121 | */ 122 | #[derive(Debug, Serialize)] 123 | #[serde(rename_all = "camelCase")] 124 | pub struct QueryUserMenuResp { 125 | pub sys_menu: Vec, 126 | pub btn_menu: Vec, 127 | pub avatar: String, 128 | pub name: String, 129 | } 130 | 131 | /* 132 | 用户菜单参数 133 | */ 134 | #[derive(Debug, Serialize, Clone)] 135 | #[serde(rename_all = "camelCase")] 136 | pub struct MenuList { 137 | pub id: Option, 138 | pub parent_id: Option, 139 | pub name: String, 140 | pub path: String, 141 | pub api_url: String, 142 | pub menu_type: i8, 143 | pub icon: String, 144 | } 145 | 146 | /* 147 | 查询用户关联角色请求参数 148 | */ 149 | #[derive(Debug, Deserialize)] 150 | #[serde(rename_all = "camelCase")] 151 | pub struct QueryUserRoleReq { 152 | pub user_id: i64, 153 | } 154 | 155 | /* 156 | 用户关联角色响应参数 157 | */ 158 | #[derive(Debug, Serialize)] 159 | #[serde(rename_all = "camelCase")] 160 | pub struct QueryUserRoleResp { 161 | pub sys_role_list: Vec, 162 | pub user_role_ids: Vec, 163 | } 164 | 165 | /* 166 | 更新用户关联角色请求参数 167 | */ 168 | #[derive(Debug, Deserialize)] 169 | #[serde(rename_all = "camelCase")] 170 | pub struct UpdateUserRoleReq { 171 | pub user_id: i64, //用户主键 172 | pub role_ids: Vec, //角色主键 173 | } 174 | 175 | /* 176 | 重置密码 177 | */ 178 | #[derive(Debug, Deserialize)] 179 | pub struct ResetUserPwdReq { 180 | pub id: i64, //用户主键 181 | pub password: String, //用户密码 182 | } 183 | 184 | /* 185 | 重置密码 186 | */ 187 | #[derive(Debug, Deserialize)] 188 | #[serde(rename_all = "camelCase")] 189 | pub struct UpdateUserPwdReq { 190 | pub pwd: String, //用户密码 191 | pub re_pwd: String, //用户密码 192 | } 193 | -------------------------------------------------------------------------------- /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; 6 | use crate::vo::system::sys_menu_vo::MenuResp; 7 | use rbatis::rbdc::datetime::DateTime; 8 | use rbatis::RBatis; 9 | use serde::{Deserialize, Serialize}; 10 | /* 11 | *菜单信息 12 | *author:刘飞华 13 | *date:2024/12/12 14:41:44 14 | */ 15 | #[derive(Clone, Debug, Serialize, Deserialize)] 16 | pub struct Menu { 17 | pub id: Option, //主键 18 | pub menu_name: String, //菜单名称 19 | pub menu_type: i8, //菜单类型(1:目录 2:菜单 3:按钮) 20 | pub visible: i8, //菜单状态(0:隐藏, 显示:1) 21 | pub status: i8, //状态(1:正常,0:禁用) 22 | pub sort: i32, //排序 23 | pub parent_id: Option, //父ID 24 | pub menu_url: Option, //路由路径 25 | pub api_url: Option, //接口URL 26 | pub menu_icon: Option, //菜单图标 27 | pub remark: Option, //备注 28 | pub create_time: Option, //创建时间 29 | pub update_time: Option, //修改时间 30 | } 31 | impl From for Menu { 32 | fn from(item: MenuReq) -> Self { 33 | let mut model = Menu { 34 | id: item.id, //主键 35 | menu_name: item.menu_name, //菜单名称 36 | menu_type: item.menu_type, //菜单类型(1:目录 2:菜单 3:按钮) 37 | visible: item.visible, //菜单状态(0:隐藏, 显示:1) 38 | status: item.status, //状态(1:正常,0:禁用) 39 | sort: item.sort, //排序 40 | parent_id: item.parent_id, //父ID 41 | menu_url: item.menu_url, //路由路径 42 | api_url: item.api_url, //接口URL 43 | menu_icon: item.menu_icon, //菜单图标 44 | remark: item.remark, //备注 45 | create_time: None, //创建时间 46 | update_time: None, //修改时间 47 | }; 48 | if let None = item.id { 49 | model.create_time = Some(DateTime::now()); 50 | } else { 51 | model.update_time = Some(DateTime::now()); 52 | } 53 | model 54 | } 55 | } 56 | 57 | impl Into for Menu { 58 | fn into(self) -> MenuResp { 59 | MenuResp { 60 | id: self.id, //主键 61 | menu_name: self.menu_name, //菜单名称 62 | menu_type: self.menu_type, //菜单类型(1:目录 2:菜单 3:按钮) 63 | visible: self.visible, //菜单状态(0:隐藏, 显示:1) 64 | status: self.status, //状态(1:正常,0:禁用) 65 | sort: self.sort, //排序 66 | parent_id: self.parent_id, //父ID 67 | menu_url: self.menu_url, //路由路径 68 | api_url: self.api_url, //接口URL 69 | menu_icon: self.menu_icon, //菜单图标 70 | remark: self.remark, //备注 71 | create_time: self.create_time, //创建时间 72 | update_time: self.update_time, //修改时间 73 | } 74 | } 75 | } 76 | /* 77 | *菜单信息基本操作 78 | *author:刘飞华 79 | *date:2024/12/12 14:41:44 80 | */ 81 | rbatis::crud!(Menu {}, "sys_menu"); 82 | 83 | /* 84 | *根据id查询菜单信息 85 | *author:刘飞华 86 | *date:2024/12/12 14:41:44 87 | */ 88 | impl_select!(Menu{select_by_id(id:&i64) -> Option => "`where id = #{id} limit 1`"}, "sys_menu"); 89 | 90 | /* 91 | *检查菜单名称的唯一性 92 | *author:刘飞华 93 | *date:2024/12/12 14:41:44 94 | */ 95 | impl_select!(Menu{check_menu_name_unique(menu_name:&str, id: Option) -> Option => " 96 | where menu_name = #{menu_name} 97 | if id != null: 98 | ` and id != #{id} ` 99 | ` limit 1` " 100 | }, "sys_menu"); 101 | 102 | /* 103 | *检查菜单路径的唯一性 104 | *author:刘飞华 105 | *date:2025/01/04 22:24:01 106 | */ 107 | impl_select!(Menu{check_menu_url_unique(menu_url:&str, id: Option) -> Option => " 108 | where menu_url = #{menu_url} 109 | if id != null: 110 | ` and id != #{id} ` 111 | ` limit 1` " 112 | }, "sys_menu"); 113 | 114 | /* 115 | *根据ids查询菜单信息 116 | *author:刘飞华 117 | *date:2024/12/12 14:41:44 118 | */ 119 | impl_select!(Menu{select_by_ids(ids:&[i64]) -> Vec => "`where status = 1 and id in ${ids.sql()} order by sort asc`"}, "sys_menu"); 120 | 121 | /* 122 | *查询菜单数量 123 | *author:刘飞华 124 | *date:2024/12/25 10:01:11 125 | */ 126 | #[sql("select count(1) from sys_menu where parent_id= ?")] 127 | pub async fn select_count_menu_by_parent_id(rb: &RBatis, parent_id: &i64) -> rbatis::Result { 128 | impled!() 129 | } 130 | 131 | /* 132 | *查询菜单信息(排除按钮) 133 | *author:刘飞华 134 | *date:2025/01/04 22:24:01 135 | */ 136 | impl_select!(Menu{select_menu_list() -> Vec => "`where menu_type != 3 and status = 1`"}, "sys_menu"); 137 | 138 | /* 139 | *查询菜单列表 140 | *author:刘飞华 141 | *date:2024/12/25 10:01:11 142 | */ 143 | impl_select!(Menu{query_sys_menu_list(menu_name:Option, parent_id: Option, status:Option) =>" 144 | where menu_type != 3 145 | if menu_name != '' && menu_name != null: 146 | ` and menu_name like concat('%', #{menu_name}, '%') ` 147 | if parent_id != 0 && parent_id != null: 148 | ` and parent_id = #{parent_id} ` 149 | if status != 2 && status != null: 150 | ` and status = #{status} ` 151 | if !sql.contains('count'): 152 | ` order by sort asc `" 153 | },"sys_menu"); 154 | 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"); -------------------------------------------------------------------------------- /src/handler/system/sys_menu_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data, ok_result_page}; 7 | use crate::model::system::sys_menu_model::{select_count_menu_by_parent_id, Menu}; 8 | use crate::model::system::sys_role_menu_model::select_count_menu_by_menu_id; 9 | use crate::vo::system::sys_menu_vo::*; 10 | use crate::RB; 11 | use rbatis::rbdc::DateTime; 12 | use rbatis::PageRequest; 13 | use rbs::value; 14 | use salvo::prelude::*; 15 | use salvo::{Request, Response}; 16 | 17 | /* 18 | *添加菜单信息 19 | *author:刘飞华 20 | *date:2025/01/08 13:51:14 21 | */ 22 | #[handler] 23 | pub async fn add_sys_menu(req: &mut Request, res: &mut Response) -> AppResult { 24 | let mut item = req.parse_json::().await?; 25 | log::info!("add sys_menu params: {:?}", &item); 26 | 27 | let rb = &mut RB.clone(); 28 | if Menu::check_menu_name_unique(rb, &item.menu_name, None).await?.is_some() { 29 | return Err(AppError::BusinessError("菜单名称已存在")); 30 | } 31 | 32 | if let Some(url) = item.menu_url.clone() { 33 | if url != "".to_string() { 34 | if Menu::check_menu_url_unique(rb, url.as_str(), None).await?.is_some() { 35 | return Err(AppError::BusinessError("路由路径已存在")); 36 | } 37 | } 38 | } 39 | 40 | item.id = None; 41 | Menu::insert(rb, &Menu::from(item)).await.map(|_| ok_result(res))? 42 | } 43 | 44 | /* 45 | *删除菜单信息 46 | *author:刘飞华 47 | *date:2025/01/08 13:51:14 48 | */ 49 | #[handler] 50 | pub async fn delete_sys_menu(req: &mut Request, res: &mut Response) -> AppResult { 51 | let item = req.parse_json::().await?; 52 | log::info!("delete sys_menu params: {:?}", &item); 53 | 54 | let rb = &mut RB.clone(); 55 | 56 | let ids = item.ids; 57 | for x in ids.clone() { 58 | if select_count_menu_by_parent_id(rb, &x).await? > 0 { 59 | return Err(AppError::BusinessError("存在子菜单,不允许删除")); 60 | } 61 | 62 | if select_count_menu_by_menu_id(rb, &x).await? > 0 { 63 | return Err(AppError::BusinessError("菜单已分配,不允许删除")); 64 | } 65 | } 66 | 67 | Menu::delete_by_map(rb, value! {"id": &ids}).await.map(|_| ok_result(res))? 68 | } 69 | 70 | /* 71 | *更新菜单信息 72 | *author:刘飞华 73 | *date:2025/01/08 13:51:14 74 | */ 75 | #[handler] 76 | pub async fn update_sys_menu(req: &mut Request, res: &mut Response) -> AppResult { 77 | let item = req.parse_json::().await?; 78 | log::info!("update sys_menu params: {:?}", &item); 79 | 80 | let rb = &mut RB.clone(); 81 | 82 | let id = item.id; 83 | 84 | if id.is_none() { 85 | return Err(AppError::BusinessError("主键不能为空")); 86 | } 87 | 88 | if Menu::select_by_id(rb, &id.unwrap_or_default()).await?.is_none() { 89 | return Err(AppError::BusinessError("菜单信息不存在")); 90 | } 91 | 92 | if Menu::check_menu_name_unique(rb, &item.menu_name, id).await?.is_some() { 93 | return Err(AppError::BusinessError("菜单名称已存在")); 94 | } 95 | 96 | if let Some(url) = item.menu_url.clone() { 97 | if url != "".to_string() { 98 | if Menu::check_menu_url_unique(rb, &url, id).await?.is_some() { 99 | return Err(AppError::BusinessError("路由路径已存在")); 100 | } 101 | } 102 | } 103 | 104 | Menu::update_by_map(rb, &Menu::from(item), value! {"id": &id}).await.map(|_| ok_result(res))? 105 | } 106 | 107 | /* 108 | *更新菜单信息状态 109 | *author:刘飞华 110 | *date:2025/01/08 13:51:14 111 | */ 112 | #[handler] 113 | pub async fn update_sys_menu_status(req: &mut Request, res: &mut Response) -> AppResult { 114 | let item = req.parse_json::().await?; 115 | 116 | log::info!("update sys_menu_status params: {:?}", &item); 117 | 118 | let update_sql = format!( 119 | "update sys_menu set status = ? ,update_time = ? where id in ({})", 120 | item.ids.iter().map(|_| "?").collect::>().join(", ") 121 | ); 122 | 123 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 124 | param.extend(item.ids.iter().map(|&id| value!(id))); 125 | 126 | RB.clone().exec(&update_sql, param).await.map(|_| ok_result(res))? 127 | } 128 | 129 | /* 130 | *查询菜单信息详情 131 | *author:刘飞华 132 | *date:2025/01/08 13:51:14 133 | */ 134 | #[handler] 135 | pub async fn query_sys_menu_detail(req: &mut Request, res: &mut Response) -> AppResult { 136 | let item = req.parse_json::().await?; 137 | log::info!("query sys_menu_detail params: {:?}", &item); 138 | 139 | Menu::select_by_id(&mut RB.clone(), &item.id).await?.map_or_else( 140 | || Err(AppError::BusinessError("菜单信息不存在")), 141 | |x| { 142 | let data: MenuResp = x.into(); 143 | ok_result_data(res, data) 144 | }, 145 | ) 146 | } 147 | 148 | /* 149 | *查询菜单信息列表 150 | *author:刘飞华 151 | *date:2025/01/08 13:51:14 152 | */ 153 | #[handler] 154 | pub async fn query_sys_menu_list(req: &mut Request, res: &mut Response) -> AppResult { 155 | let item = req.parse_json::().await?; 156 | log::info!("query sys_menu_list params: {:?}", &item); 157 | 158 | let rb = &mut RB.clone(); 159 | 160 | let menu_name = item.menu_name; 161 | let parent_id = item.parent_id; 162 | let status = item.status; 163 | 164 | Menu::query_sys_menu_list(rb, menu_name, parent_id, status) 165 | .await 166 | .map(|x| ok_result_data(res, x.into_iter().map(|x| x.into()).collect::>()))? 167 | } 168 | 169 | /* 170 | *查询菜单信息(排除按钮) 171 | *author:刘飞华 172 | *date:2025/01/08 13:51:14 173 | */ 174 | #[handler] 175 | pub async fn query_sys_menu_list_simple(res: &mut Response) -> AppResult { 176 | Menu::select_menu_list(&mut RB.clone()) 177 | .await 178 | .map(|x| ok_result_data(res, x.into_iter().map(|x| MenuSimpleResp::from(x)).collect::>()))? 179 | } 180 | 181 | /* 182 | *查询菜单信息列表 183 | *author:刘飞华 184 | *date:2025/01/08 13:51:14 185 | */ 186 | #[handler] 187 | pub async fn query_sys_menu_resource_list(req: &mut Request, res: &mut Response) -> AppResult { 188 | let item = req.parse_json::().await?; 189 | log::info!("query sys_menu_list params: {:?}", &item); 190 | 191 | let rb = &mut RB.clone(); 192 | 193 | let menu_name = item.menu_name; 194 | let parent_id = item.parent_id; 195 | let status = item.status; 196 | 197 | let page = &PageRequest::new(item.page_no, item.page_size); 198 | 199 | Menu::query_sys_menu_resource_list(rb, page, menu_name, parent_id, status) 200 | .await 201 | .map(|x| ok_result_page(res, x.records.into_iter().map(|x| x.into()).collect::>(), x.total))? 202 | } 203 | -------------------------------------------------------------------------------- /src/handler/system/sys_dept_handler.rs: -------------------------------------------------------------------------------- 1 | // Code generated by https://github.com/feihua/code_cli 2 | // author:刘飞华 3 | // date:2025/01/08 13:51:14 4 | 5 | use crate::common::error::{AppError, AppResult}; 6 | use crate::common::result::{ok_result, ok_result_data}; 7 | 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}; 8 | use crate::vo::system::sys_dept_vo::*; 9 | use crate::RB; 10 | use rbatis::rbatis_codegen::ops::AsProxy; 11 | use rbatis::rbdc::DateTime; 12 | use rbs::value; 13 | use salvo::prelude::*; 14 | use salvo::{Request, Response}; 15 | /* 16 | *添加部门 17 | *author:刘飞华 18 | *date:2025/01/08 13:51:14 19 | */ 20 | #[handler] 21 | pub async fn add_sys_dept(req: &mut Request, res: &mut Response) -> AppResult { 22 | let item = req.parse_json::().await?; 23 | log::info!("add sys_dept params: {:?}", &item); 24 | 25 | let rb = &mut RB.clone(); 26 | 27 | if Dept::select_by_dept_name(rb, &item.dept_name, item.parent_id).await?.is_some() { 28 | return Err(AppError::BusinessError("部门名称已存在")); 29 | } 30 | 31 | match Dept::select_by_id(rb, &item.parent_id).await? { 32 | None => Err(AppError::BusinessError("添加失败,上级部门不存在")), 33 | Some(dept) => { 34 | if dept.status == 0 { 35 | return Err(AppError::BusinessError("部门停用,不允许添加")); 36 | } 37 | let ancestors = format!("{},{}", dept.ancestors.unwrap_or_default(), &item.parent_id); 38 | // item.ancestors = Some(ancestors); 39 | let mut sys_dept = Dept::from(item); 40 | sys_dept.ancestors = Some(ancestors); 41 | Dept::insert(rb, &sys_dept).await.map(|_| ok_result(res))? 42 | } 43 | } 44 | } 45 | 46 | /* 47 | *删除部门 48 | *author:刘飞华 49 | *date:2025/01/08 13:51:14 50 | */ 51 | #[handler] 52 | pub async fn delete_sys_dept(req: &mut Request, res: &mut Response) -> AppResult { 53 | let item = req.parse_json::().await?; 54 | log::info!("delete sys_dept params: {:?}", &item); 55 | 56 | let rb = &mut RB.clone(); 57 | if select_dept_count(rb, &item.id).await? > 0 { 58 | return Err(AppError::BusinessError("存在下级部门,不允许删除")); 59 | } 60 | 61 | if check_dept_exist_user(rb, &item.id).await? > 0 { 62 | return Err(AppError::BusinessError("部门存在用户,不允许删除")); 63 | } 64 | 65 | Dept::delete_by_map(rb, value! {"id": &item.id}).await.map(|_| ok_result(res))? 66 | } 67 | 68 | /* 69 | *更新部门 70 | *author:刘飞华 71 | *date:2025/01/08 13:51:14 72 | */ 73 | #[handler] 74 | pub async fn update_sys_dept(req: &mut Request, res: &mut Response) -> AppResult { 75 | let rb = &mut RB.clone(); 76 | let mut item = req.parse_json::().await?; 77 | log::info!("update sys_dept params: {:?}", &item); 78 | 79 | let id = item.id; 80 | if id.is_none() { 81 | return Err(AppError::BusinessError("主键不能为空")); 82 | } 83 | if Some(item.parent_id) == id { 84 | return Err(AppError::BusinessError("上级部门不能是自己")); 85 | } 86 | 87 | let old_ancestors = match Dept::select_by_id(rb, &id.unwrap_or_default()).await? { 88 | None => return Err(AppError::BusinessError("部门不存在")), 89 | Some(dept) => dept.ancestors.unwrap_or_default(), 90 | }; 91 | 92 | let ancestors = match Dept::select_by_id(rb, &item.parent_id).await? { 93 | None => return Err(AppError::BusinessError("上级部门不存在")), 94 | Some(dept) => { 95 | format!("{},{}", dept.ancestors.unwrap_or_default(), &item.parent_id) 96 | } 97 | }; 98 | 99 | if let Some(dept) = Dept::select_by_dept_name(rb, &item.dept_name, item.parent_id).await? { 100 | if dept.id != id { 101 | return Err(AppError::BusinessError("部门名称已存在")); 102 | } 103 | } 104 | 105 | if select_normal_children_dept_by_id(rb, &id.unwrap_or_default()).await? > 0 && item.status == 0 { 106 | return Err(AppError::BusinessError("该部门包含未停用的子部门")); 107 | } 108 | 109 | for mut x in select_children_dept_by_id(rb, &id.unwrap_or_default()).await? { 110 | x.ancestors = Some(x.ancestors.unwrap_or_default().replace(old_ancestors.as_str(), ancestors.as_str())); 111 | Dept::update_by_map(rb, &x, value! {"id": &x.id}).await?; 112 | } 113 | 114 | if item.status == 1 && ancestors != "0" { 115 | let ids = ancestors.split(",").map(|s| s.i64()).collect::>(); 116 | 117 | let update_sql = format!( 118 | "update sys_dept set status = ? ,update_time = ? where id in ({})", 119 | ids.iter().map(|_| "?").collect::>().join(", ") 120 | ); 121 | 122 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 123 | param.extend(ids.iter().map(|&id| value!(id))); 124 | 125 | rb.exec(&update_sql, param).await?; 126 | } 127 | item.ancestors = Some(ancestors.clone()); 128 | 129 | Dept::update_by_map(rb, &Dept::from(item), value! {"id": &id}).await.map(|_| ok_result(res))? 130 | } 131 | 132 | /* 133 | *更新部门状态 134 | *author:刘飞华 135 | *date:2025/01/08 13:51:14 136 | */ 137 | #[handler] 138 | pub async fn update_sys_dept_status(req: &mut Request, res: &mut Response) -> AppResult { 139 | let item = req.parse_json::().await?; 140 | log::info!("update sys_dept_status params: {:?}", &item); 141 | 142 | let rb = &mut RB.clone(); 143 | if item.status == 1 { 144 | for id in item.ids.clone() { 145 | if let Some(x) = Dept::select_by_id(rb, &id).await? { 146 | let ancestors = x.ancestors.unwrap_or_default(); 147 | let ids = ancestors.split(",").map(|s| s.i64()).collect::>(); 148 | 149 | let update_sql = format!( 150 | "update sys_dept set status = ? ,update_time = ? where id in ({})", 151 | ids.iter().map(|_| "?").collect::>().join(", ") 152 | ); 153 | 154 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 155 | param.extend(ids.iter().map(|&id| value!(id))); 156 | rb.exec(&update_sql, param).await?; 157 | } 158 | } 159 | } 160 | 161 | let update_sql = format!( 162 | "update sys_dept set status = ? ,update_time = ? where id in ({})", 163 | item.ids.iter().map(|_| "?").collect::>().join(", ") 164 | ); 165 | 166 | let mut param = vec![value!(item.status), value!(DateTime::now())]; 167 | param.extend(item.ids.iter().map(|&id| value!(id))); 168 | rb.exec(&update_sql, param).await.map(|_| ok_result(res))? 169 | } 170 | 171 | /* 172 | *查询部门详情 173 | *author:刘飞华 174 | *date:2025/01/08 13:51:14 175 | */ 176 | #[handler] 177 | pub async fn query_sys_dept_detail(req: &mut Request, res: &mut Response) -> AppResult { 178 | let item = req.parse_json::().await?; 179 | log::info!("query sys_dept_detail params: {:?}", &item); 180 | 181 | match Dept::select_by_id(&mut RB.clone(), &item.id).await? { 182 | Some(x) => { 183 | let data: DeptResp = x.into(); 184 | ok_result_data(res, data) 185 | } 186 | None => Err(AppError::BusinessError("部门不存在")), 187 | } 188 | } 189 | 190 | /* 191 | *查询部门列表 192 | *author:刘飞华 193 | *date:2025/01/08 13:51:14 194 | */ 195 | #[handler] 196 | pub async fn query_sys_dept_list(req: &mut Request, res: &mut Response) -> AppResult { 197 | let item = req.parse_json::().await?; 198 | log::info!("query sys_dept_list params: {:?}", &item); 199 | 200 | let rb = &mut RB.clone(); 201 | 202 | Dept::select_page_dept_list(rb, &item) 203 | .await 204 | .map(|x| ok_result_data(res, x.into_iter().map(|x| x.into()).collect::>()))? 205 | } 206 | -------------------------------------------------------------------------------- /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; 5 | use crate::vo::system::sys_user_vo::UserReq; 6 | use crate::vo::system::sys_user_vo::UserResp; 7 | use rbatis::executor::Executor; 8 | use rbatis::rbdc::datetime::DateTime; 9 | use rbatis::rbdc::Error; 10 | use serde::{Deserialize, Serialize}; 11 | /* 12 | *用户信息 13 | *author:刘飞华 14 | *date:2024/12/12 14:41:44 15 | */ 16 | #[derive(Clone, Debug, Serialize, Deserialize)] 17 | pub struct User { 18 | pub id: Option, //主键 19 | pub mobile: String, //手机 20 | pub user_name: String, //用户账号 21 | pub nick_name: String, //用户昵称 22 | pub user_type: Option, //用户类型(00系统用户) 23 | pub email: String, //用户邮箱 24 | pub avatar: Option, //头像路径 25 | pub password: String, //密码 26 | pub status: i8, //状态(1:正常,0:禁用) 27 | pub dept_id: i64, //部门ID 28 | pub login_ip: String, //最后登录IP 29 | pub login_date: Option, //最后登录时间 30 | pub login_browser: String, //浏览器类型 31 | pub login_os: String, //操作系统 32 | pub pwd_update_date: Option, //密码最后更新时间 33 | pub remark: Option, //备注 34 | pub del_flag: i8, //删除标志(0代表删除 1代表存在) 35 | pub create_time: Option, //创建时间 36 | pub update_time: Option, //修改时间 37 | } 38 | impl From for User { 39 | fn from(item: UserReq) -> Self { 40 | let mut model = User { 41 | id: None, //主键 42 | mobile: item.mobile, //手机 43 | user_name: item.user_name, //用户账号 44 | nick_name: item.nick_name, //用户昵称 45 | user_type: Some("01".to_string()), //用户类型(00系统用户) 46 | email: item.email, //用户邮箱 47 | avatar: item.avatar, //头像路径 48 | password: item.password.unwrap_or_default(), //密码 49 | status: item.status, //状态(1:正常,0:禁用) 50 | dept_id: item.dept_id, //部门ID 51 | login_ip: "".to_string(), //最后登录IP 52 | login_date: None, //最后登录时间 53 | login_browser: "".to_string(), //浏览器类型 54 | login_os: "".to_string(), //操作系统 55 | pwd_update_date: None, //密码最后更新时间 56 | remark: item.remark, //备注 57 | del_flag: 1, //删除标志(0代表删除 1代表存在) 58 | create_time: None, //创建时间 59 | update_time: None, //修改时间 60 | }; 61 | if let None = item.id { 62 | model.create_time = Some(DateTime::now()); 63 | } else { 64 | model.update_time = Some(DateTime::now()); 65 | } 66 | model 67 | } 68 | } 69 | 70 | impl Into for User { 71 | fn into(self) -> UserResp { 72 | UserResp { 73 | id: self.id, //主键 74 | mobile: self.mobile, //手机 75 | user_name: self.user_name, //姓名 76 | nick_name: self.nick_name, //用户昵称 77 | user_type: self.user_type.unwrap_or_default(), //用户类型(00系统用户) 78 | email: self.email, //用户邮箱 79 | avatar: self.avatar, //头像路径 80 | status: self.status, //状态(1:正常,0:禁用) 81 | dept_id: self.dept_id, //部门ID 82 | login_ip: self.login_ip, //最后登录IP 83 | login_date: self.login_date, //最后登录时间 84 | login_browser: self.login_browser, //浏览器类型 85 | login_os: self.login_os, //操作系统 86 | pwd_update_date: self.pwd_update_date, //密码最后更新时间 87 | remark: self.remark, //备注 88 | create_time: self.create_time, //创建时间 89 | update_time: self.update_time, //修改时间 90 | dept_info: None, 91 | post_ids: None, 92 | } 93 | } 94 | } 95 | /* 96 | *用户信息基本操作 97 | *author:刘飞华 98 | *date:2024/12/12 14:41:44 99 | */ 100 | rbatis::crud!(User {}, "sys_user"); 101 | 102 | /* 103 | *根据id查询用户信息 104 | *author:刘飞华 105 | *date:2024/12/12 14:41:44 106 | */ 107 | impl_select!(User{select_by_id(id:i64) -> Option => "`where id = #{id} limit 1`"}, "sys_user"); 108 | 109 | /* 110 | *根据account查询用户信息 111 | *author:刘飞华 112 | *date:2025/09/26 13:42:44 113 | */ 114 | impl_select!(User{select_by_account(account:&str) -> Option => "`where user_name = #{account} or mobile = #{account} or email = #{account} limit 1`"},"sys_user"); 115 | 116 | /* 117 | *根据mobile查询用户信息 118 | *author:刘飞华 119 | *date:2024/12/12 14:41:44 120 | */ 121 | impl_select!(User{select_by_mobile(mobile:&str) -> Option => "`where mobile = #{mobile} limit 1`"},"sys_user"); 122 | 123 | /* 124 | *根据user_name查询用户信息 125 | *author:刘飞华 126 | *date:2024/12/12 14:41:44 127 | */ 128 | impl_select!(User{select_by_user_name(user_name:&str) -> Option => "`where user_name = #{user_name} limit 1`"}, "sys_user"); 129 | 130 | /* 131 | *根据email查询用户信息 132 | *author:刘飞华 133 | *date:2024/12/12 14:41:44 134 | */ 135 | impl_select!(User{select_by_email(email:&str) -> Option => "`where email = #{email} limit 1`"}, "sys_user"); 136 | 137 | /* 138 | *分页查询用户信息 139 | *author:刘飞华 140 | *date:2024/12/12 14:41:44 141 | */ 142 | impl_select_page!(User{select_page() =>" 143 | if !sql.contains('count'): 144 | order by create_time desc" 145 | },"sys_user"); 146 | 147 | /* 148 | *根据条件分页查询用户信息 149 | *author:刘飞华 150 | *date:2024/12/12 14:41:44 151 | */ 152 | impl_select_page!(User{select_sys_user_list(req:&QueryUserListReq) =>" 153 | where 1=1 154 | if req.mobile != null && req.mobile != '': 155 | ` and mobile like concat('%', #{req.mobile}, '%') ` 156 | if req.userName != null && req.userName != '': 157 | ` and user_name like concat('%', #{req.userName}, '%') ` 158 | if req.status != 2: 159 | ` and status = #{req.status} ` 160 | if req.deptId != 0: 161 | ` and (dept_id = #{req.deptId} OR dept_id IN (SELECT id FROM sys_dept WHERE find_in_set(#{req.deptId}, ancestors))) ` 162 | if !sql.contains('count'): 163 | ` order by create_time desc `"},"sys_user"); 164 | 165 | /* 166 | *根据条件分页查询已配用户角色列表 167 | *author:刘飞华 168 | *date:2024/12/12 14:41:44 169 | */ 170 | #[py_sql( 171 | "`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} ` 172 | if mobile != '': 173 | ` and u.mobile = #{mobile} ` 174 | if user_name != '': 175 | ` and u.user_name = #{user_name} ` 176 | limit #{page_no},#{page_size}` " 177 | )] 178 | async fn select_allocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str, page_no: u64, page_size: u64) -> Result, Error> { 179 | impled!() 180 | } 181 | 182 | /* 183 | * 描述:根据条件分页查询已配用户角色数量 184 | * author:刘飞华 185 | * date:2025/1/6 16:13 186 | */ 187 | #[py_sql( 188 | "`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} ` 189 | if mobile != '': 190 | ` and u.mobile = #{mobile} ` 191 | if user_name != '': 192 | ` and u.user_name = #{user_name} `" 193 | )] 194 | async fn count_allocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str) -> Result { 195 | impled!() 196 | } 197 | 198 | /* 199 | * 描述:根据条件分页查询未分配用户角色列表 200 | * author:刘飞华 201 | * date:2025/1/6 16:17 202 | */ 203 | #[py_sql( 204 | "`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) ` 205 | if mobile != '': 206 | ` and u.mobile = #{mobile} ` 207 | if user_name != '': 208 | ` and u.user_name = #{user_name} ` 209 | limit #{page_no},#{page_size}` " 210 | )] 211 | 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> { 212 | impled!() 213 | } 214 | 215 | /* 216 | * 描述:根据条件分页查询未分配用户角色数量 217 | * author:刘飞华 218 | * date:2025/1/6 16:17 219 | */ 220 | #[py_sql( 221 | "`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) ` 222 | if mobile != '': 223 | ` and u.mobile = #{mobile} ` 224 | if user_name != '': 225 | ` and u.user_name = #{user_name} `" 226 | )] 227 | pub async fn count_unallocated_list(rb: &dyn Executor, role_id: i64, user_name: &str, mobile: &str) -> rbatis::Result { 228 | impled!() 229 | } 230 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------