├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── application-dev.yml ├── application-prod.yml ├── application.yml ├── permit-lib ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── ruoyi-rust.sql ├── src ├── config │ ├── cache_variables.rs │ ├── config.rs │ ├── global_variables.rs │ ├── log.rs │ └── mod.rs ├── controller │ ├── img_controller.rs │ ├── mod.rs │ ├── monitor │ │ ├── mod.rs │ │ ├── server_controller.rs │ │ ├── sys_logininfor_controller.rs │ │ └── sys_user_online_controller.rs │ ├── sys_auth_controller.rs │ ├── sys_config_controller.rs │ ├── sys_dept_controller.rs │ ├── sys_dict_data_controller.rs │ ├── sys_dict_type_controller.rs │ ├── sys_menu_controller.rs │ ├── sys_notice_controller.rs │ ├── sys_post_controller.rs │ ├── sys_profile_controller.rs │ ├── sys_role_controller.rs │ └── sys_user_controller.rs ├── domain │ ├── dto │ │ ├── auth.rs │ │ ├── config.rs │ │ ├── dept.rs │ │ ├── dict_data.rs │ │ ├── dict_type.rs │ │ ├── logininfor.rs │ │ ├── menu.rs │ │ ├── mod.rs │ │ ├── notice.rs │ │ ├── post.rs │ │ ├── role.rs │ │ ├── role_menu.rs │ │ ├── sign_in.rs │ │ └── user.rs │ ├── mapper │ │ ├── mod.rs │ │ ├── sys_config.rs │ │ ├── sys_dept.rs │ │ ├── sys_dict_data.rs │ │ ├── sys_dict_type.rs │ │ ├── sys_logininfor.rs │ │ ├── sys_menu.rs │ │ ├── sys_notice.rs │ │ ├── sys_post.rs │ │ ├── sys_role.rs │ │ ├── sys_role_menu.rs │ │ ├── sys_trash.rs │ │ ├── sys_user.rs │ │ └── sys_user_role.rs │ ├── mod.rs │ ├── table │ │ ├── enums.rs │ │ ├── mod.rs │ │ ├── sms.rs │ │ └── tables.rs │ └── vo │ │ ├── config.rs │ │ ├── dept.rs │ │ ├── dict_data.rs │ │ ├── dict_type.rs │ │ ├── jwt.rs │ │ ├── menu.rs │ │ ├── mod.rs │ │ ├── monitor.rs │ │ ├── notice.rs │ │ ├── post.rs │ │ ├── role.rs │ │ ├── router.rs │ │ ├── sign_in.rs │ │ ├── sys_logininfor.rs │ │ └── user.rs ├── error.rs ├── http_server.rs ├── lib.rs ├── main.rs ├── middleware │ ├── auth.rs │ ├── auth_actix.rs │ └── mod.rs ├── service │ ├── cache_service.rs │ ├── mem_service.rs │ ├── mod.rs │ ├── redis_service.rs │ ├── sys_config_service.rs │ ├── sys_dept_service.rs │ ├── sys_dict_data_service.rs │ ├── sys_dict_type_service.rs │ ├── sys_logininfor_service.rs │ ├── sys_menu_service.rs │ ├── sys_notice_service.rs │ ├── sys_post_service.rs │ ├── sys_role_menu_service.rs │ ├── sys_role_service.rs │ ├── sys_sms_service.rs │ ├── sys_trash_service.rs │ ├── sys_user_role_service.rs │ └── sys_user_service.rs ├── token_auth │ └── mod.rs ├── util │ ├── base64.rs │ ├── bencher.rs │ ├── hardware.rs │ ├── mod.rs │ ├── options.rs │ ├── password_encoder.rs │ ├── string.rs │ └── web_utils.rs └── web_data.rs └── user_agent.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | 11 | 12 | #Added by cargo 13 | 14 | /target 15 | 16 | 17 | #log 18 | *.log 19 | /idea 20 | /.idea 21 | /.fleet 22 | 23 | 24 | /.vscode 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ruoyi_rust" 3 | version = "0.1.0" 4 | authors = ["wizount <2537660666@qq.com>"] 5 | edition = "2021" 6 | 7 | 8 | [[bin]] 9 | name = "ruoyi_rust_prod" 10 | path = "src/main.rs" 11 | 12 | [[bin]] 13 | name = "ruoyi_rust_dev" 14 | path = "src/main.rs" 15 | 16 | #[build-dependencies] 17 | #tauri-build = { version = "1.2.0", features = [] } 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | [dependencies] 21 | 22 | rbs = { version = "4" } 23 | rbatis = { version = "4" } 24 | rbdc-mysql = { version = "4" } 25 | 26 | #rbatis orm dep must use async-std(Because actix-web relies on tokio0.2) 27 | tokio = { version = "1", features = ["full"] } 28 | #serde and jso 29 | serde = { version = "1.0", features = ["derive"] } 30 | serde_json = "1.0" 31 | serde_yaml = "0.9" 32 | merge-yaml-hash = "0.2" 33 | #log 34 | log = "0.4" 35 | #fast_log enable features = ["lz4", "zip", "gzip"], and edit src/config/log.rs fn choose_packer(),edit application.yml add log_pack_compress: "zip" 36 | fast_log = { version = "1.5", features = [] } 37 | #web server 38 | actix = '0.13.0' 39 | actix-web = { version = "4", default-features = false, features = ["macros"] } 40 | actix-http = "3" 41 | actix-multipart = "0.4" 42 | actix-files = "0.6.2" 43 | #actix-web-actors = "4.1.0" 44 | #redis 45 | redis = { version = "0.22.3", features = ["tokio-comp"] } 46 | #jwt 47 | jsonwebtoken = "8" 48 | #snowflake 49 | rustflake = "0.1" 50 | # enc passowrd, like springboot secutiry 51 | bcrypt = "0.13" 52 | #captcha 53 | captcha = "0.0.9" 54 | 55 | actix-easy-multipart = "3.0" 56 | 57 | #image code 58 | image = "0.24" 59 | tokio-util = { version = "0.7.4", features = ["codec"] } 60 | tokio-stream = { version = "0.1.3", features = ["sync"] } 61 | #lazy 62 | once_cell = "1.9" 63 | futures-util = "0.3.21" 64 | parking_lot = "0.12.1" 65 | 66 | uuid = { version = "1.2.2", default-features = false, features = ["v4", "fast-rng", "macro-diagnostics"] } 67 | 68 | url = "2.2" # 69 | chrono = "0.4.23" 70 | user-agent-parser = "0.3.4" 71 | 72 | base64 = "0.20.0" 73 | 74 | actix-web-validator = "5.0.1" #加入验证框架 75 | validator = { version = "0.16", features = ["derive"] }#加入验证框架 76 | 77 | permit-lib = { path = "./permit-lib" } 78 | 79 | 80 | 81 | 82 | # 系统信息 83 | sysinfo = "0.28.2" 84 | #ByteSize is an utility that easily makes bytes size representation and helps its arithmetic operations. 85 | bytesize = "1.2.0" 86 | 87 | 88 | [profile.release] 89 | lto = true 90 | opt-level = 'z' 91 | codegen-units = 1 92 | 93 | 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 wizount 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 用rust语言实现[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)后台程序 2 | 3 | # 未实现部分 4 | * 系统监控:定时任务、数据监控、缓存监控 5 | * 系统工具:代码生成、系统接口 6 | * 大部分的查询 -------------------------------------------------------------------------------- /application-dev.yml: -------------------------------------------------------------------------------- 1 | #相对应若依平台的VITE_APP_BASE_API 2 | base_api: "/dev-api" 3 | 4 | 5 | #接口白名单(免登陆直接访问) 6 | white_list_api: 7 | - "/" 8 | - "/dev-api/login" 9 | - "/dev-api/logout" 10 | - "/dev-api/captchaImage" -------------------------------------------------------------------------------- /application-prod.yml: -------------------------------------------------------------------------------- 1 | #debug模式,true输出控制台日志,false只输出文件日志.生产环境需要关闭debug,rbatis也需移除"debug_mode" 2 | #debug: false 3 | #server 地址 4 | server_url: "0.0.0.0:80" 5 | 6 | #相对应若依平台的VITE_APP_BASE_API 7 | base_api: "/prod-api" 8 | 9 | #接口白名单(免登陆直接访问) 10 | white_list_api: 11 | - "/" 12 | - "/prod-api/login" 13 | - "/prod-api/logout" 14 | - "/prod-api/captchaImage" -------------------------------------------------------------------------------- /application.yml: -------------------------------------------------------------------------------- 1 | #debug模式,true输出控制台日志,false只输出文件日志.生产环境需要关闭debug,rbatis也需移除"debug_mode" 2 | debug: true 3 | #名称 4 | server_name: "ruoyi_rust" 5 | #server 地址 6 | server_url: "0.0.0.0:8080" 7 | #相对应若依平台的VITE_APP_BASE_API 8 | base_api: "/" 9 | #缓存介质类型,单选("mem","redis") 10 | cache_type: "redis" 11 | 12 | #web后台 redis地址(cache_type为“redis”时有效) 13 | web_redis_url: "redis://127.0.0.1:6379" #这是db0 14 | 15 | #后台用数据库地址 16 | database_url: "mysql://root:123456@localhost/ruoyi-rust" 17 | #逻辑删除-字段 18 | logic_column: "del" 19 | #逻辑删除-未删除标志 20 | logic_un_deleted: 0 21 | #逻辑删除-已删除标志 22 | logic_deleted: 1 23 | #日志文件存放目录 24 | log_dir: "target/logs/" 25 | #日志分割尺寸-单位KB,MB,GB 26 | log_temp_size: "100MB" 27 | #日志打包格式可选“”(空-保留.log)“gzip”(gz压缩包)“zip”(zip压缩包)“lz4”(lz4压缩包(非常快)) 28 | #需要打开以下选项 29 | #toml文件里面,fast_log 添加 fast_log = { version = "1.5",features = ["lz4", "zip", "gzip"]} 30 | #src/config/log.rs 解除 fn choose_packer()下的注释 31 | #application.yml 添加 log_pack_compress: "zip" 32 | log_pack_compress: "" 33 | #日志滚动保留-保留全部: All,按时间保留(秒): KeepTime(i64),按版本保留: KeepNum(i64) 34 | log_rolling_type: "KeepNum(20)" 35 | #日志等级: off,info,error,warn,debug,trace 36 | log_level: "debug" 37 | #Log channel length, null for unbounded queue, non-null for bounded queue (better performance) 38 | log_chan_len: 100000 39 | #JwtToken秘钥 40 | jwt_secret: "ruoyi_rust_vue3" 41 | #短信通道缓存(系统先把数据放入队列sms:send:,第三方短信sdk或程序需要从队列获取并发送,获得回执存入sms:resp:*),格式为sms:send:手机号和sms:resp:手机号 42 | sms_cache_send_key_prefix: "sms:send:" 43 | #登陆失败拒绝策略,重试次数限制(0次无限制) 44 | login_fail_retry: 3 45 | #登陆失败重试次数限制超过后提示的等待时间(秒) 46 | login_fail_retry_wait_sec: 30 47 | #接口白名单(免登陆直接访问) 48 | white_list_api: 49 | - "/" 50 | - "/login" 51 | - "/logout" 52 | - "/captchaImage" 53 | #是否启用验证码 54 | captcha_enabled: false 55 | #验证码过期时间(分钟) 56 | captcha_expired_min: 2 57 | #Token过期时间(分钟) 58 | token_expired_min: 60 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /permit-lib/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "permit-lib" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "proc-macro2", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "proc-macro2" 16 | version = "1.0.50" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" 19 | dependencies = [ 20 | "unicode-ident", 21 | ] 22 | 23 | [[package]] 24 | name = "quote" 25 | version = "1.0.23" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 28 | dependencies = [ 29 | "proc-macro2", 30 | ] 31 | 32 | [[package]] 33 | name = "syn" 34 | version = "1.0.107" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "unicode-ident", 41 | ] 42 | 43 | [[package]] 44 | name = "unicode-ident" 45 | version = "1.0.6" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 48 | -------------------------------------------------------------------------------- /permit-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "permit-lib" 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 | [lib] 9 | proc-macro = true 10 | path = "src/lib.rs" 11 | 12 | [dependencies] 13 | syn = { version = "1.0", features = ["full", "derive", "extra-traits"] } 14 | quote = "1.0" 15 | proc-macro2 = "1.0" 16 | 17 | -------------------------------------------------------------------------------- /permit-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{TokenStream}; 2 | use syn::{parse_macro_input, ItemFn}; 3 | use quote::{quote}; 4 | 5 | #[proc_macro_attribute] 6 | pub fn has_permit(attr: TokenStream, item: TokenStream) -> TokenStream { 7 | let func = parse_macro_input!(item as ItemFn); // 我们传入的是一个函数,所以要用到ItemFn 8 | let func_vis = &func.vis; // pub 9 | let func_block = &func.block;//.stmts.iter().map(|r|r.to_token_stream().to_string()).collect::>().join("\n"); // 函数主体实现部分{} 10 | 11 | let func_decl = &func.sig; // 函数申明 12 | let func_name = &func_decl.ident; // 函数名 13 | let func_asyncness = &func_decl.asyncness; // 函数名 14 | let func_generics = &func_decl.generics; // 函数泛型 15 | let func_inputs = &func_decl.inputs; // 函数输入参数 16 | let func_output = &func_decl.output; // 函数返回 17 | 18 | // 提取参数,参数可能是多个 19 | // let params: Vec<_> = func_inputs.iter().map(|i| { 20 | // println!("{:?}",i); 21 | // match i { 22 | // // 提取形参的pattern 23 | // // https://docs.rs/syn/1.0.1/syn/struct.PatType.html 24 | // FnArg::Typed(ref val) => { 25 | // ( ( &val).pat.to_token_stream().to_string(), 26 | // ( &val).ty.to_token_stream().to_string()) 27 | // } // pat没有办法移出val,只能借用,或者val.pat.clone() 28 | // _ => unreachable!("it's not gonna happen."), 29 | // } 30 | // }).collect(); 31 | // println!("{:?}",func_inputs); 32 | //println!("{:?}", params); 33 | // 解析attr 34 | //let attr = parse_macro_input!(attr as AttributeArgs); 35 | // let i= attr.get(0); 36 | // for n in attr{ 37 | // match n. { 38 | // Meta(m)=>{m;}, 39 | // Lit(l)=>{l;} 40 | // } 41 | // } 42 | let s = attr.to_string(); 43 | //println!("aaaa{}", attr.to_string()); 44 | 45 | // // 提取attr的ident 46 | // let attr_ident = match attr.get(0).as_ref().unwrap() { 47 | // NestedMeta::Meta(Meta::Path(ref attr_ident)) => attr_ident.clone(), 48 | // _ => unreachable!("it not gonna happen."), 49 | // }; 50 | 51 | let expanded = quote! { // 重新构建函数执行 52 | #func_vis #func_asyncness fn #func_name #func_generics(req_in_permit:HttpRequest,#func_inputs) #func_output{ 53 | match crate::token_auth::check_permit(req_in_permit, #s).await {//fixme 判断参数中是否存在httpRequest,以后再说 54 | None => #func_block 55 | Some(res) => { return res.resp_json(); } 56 | } 57 | 58 | } 59 | }; 60 | // Typed(PatType { attrs: [], pat: Ident(PatIdent { attrs: [], by_ref: None, mutability: None, ident: Ident { ident: "req", span: #0 bytes(18869..18872) }, subpat: None }), colon_token: Colon, ty: Path(TypePath { qself: None, path: Path { leading_colon: None, segments: [PathSegment { ident: Ident { ident: "HttpRequest", span: #0 bytes(18874..18885) }, arguments: None }] } }) }) 61 | 62 | //println!("{}", expanded.to_string()); 63 | expanded.into() 64 | } 65 | -------------------------------------------------------------------------------- /src/config/cache_variables.rs: -------------------------------------------------------------------------------- 1 | pub const REDIS_UUID_CAPTCHA: &'static str = "login_captcha:"; 2 | pub const LOGIN_TOKEN_KEY: &'static str = "login_tokens:"; -------------------------------------------------------------------------------- /src/config/config.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use merge_yaml_hash::{MergeYamlHash}; 3 | 4 | /// Config 5 | #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] 6 | pub struct ApplicationConfig { 7 | pub debug: bool, 8 | pub server_url: String, 9 | pub base_api: String, 10 | pub web_redis_url: String, 11 | pub database_url: String, 12 | pub logic_column: String, 13 | pub logic_un_deleted: i64, 14 | pub logic_deleted: i64, 15 | pub log_dir: String, 16 | pub log_temp_size: String, 17 | pub log_pack_compress: String, 18 | pub log_rolling_type: String, 19 | pub log_chan_len: Option, 20 | pub log_level: String, 21 | pub sms_cache_send_key_prefix: String, 22 | pub jwt_secret: String, 23 | pub white_list_api: Vec, 24 | pub cache_type: String, 25 | pub login_fail_retry: u64, 26 | pub login_fail_retry_wait_sec: u64, 27 | pub captcha_enabled: bool, 28 | pub captcha_expired_min: u64, 29 | pub token_expired_min: u64 30 | } 31 | 32 | impl Default for ApplicationConfig { 33 | fn default() -> Self { 34 | 35 | let k = "profile="; 36 | let profile = std::env::args_os() 37 | .find(|v| v.to_str().unwrap_or_default().starts_with(k)) 38 | .map(|v| v.to_str().unwrap_or_default().trim_start_matches(k).to_string()); 39 | let profile = match profile{ 40 | Some(o) => Some(o), 41 | None => { 42 | std::env::var("profile").ok() 43 | } 44 | }; 45 | let profile = match profile{ 46 | Some(o) => Some(o), 47 | None => { 48 | std::env::var_os("profile").map(|x|x.into_string().unwrap_or_default()) 49 | } 50 | }; 51 | let profile = profile.unwrap_or_else(|| "prod".to_string()); 52 | let profile = profile.trim().to_string(); 53 | println!("loading profile: {}", profile); 54 | let yml_data = include_str!("../../application.yml"); 55 | 56 | let mut hash = MergeYamlHash::new(); 57 | 58 | hash.merge(yml_data); 59 | if profile.len() > 0 { 60 | match fs::read_to_string(format!("application-{profile}.yml")) { 61 | Ok(s) => { hash.merge(&s); } 62 | Err(_) => { println!("Can't loading application-{profile}.yml"); } 63 | } 64 | } 65 | //load config 66 | let result: ApplicationConfig = 67 | serde_yaml::from_str(&*hash.to_string()).expect("load config file fail"); 68 | if result.debug { 69 | //println!("[ruoyi_rust] load config:{:?}", result); 70 | println!("[ruoyi_rust] ///////////////////// Start On Debug Mode ////////////////////////////"); 71 | } else { 72 | println!("[ruoyi_rust] ///////////////////// Start On Release Mode ////////////////////////////"); 73 | } 74 | result 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/config/global_variables.rs: -------------------------------------------------------------------------------- 1 | pub const STATUS_NORMAL: char = '0'; 2 | pub const STATUS_FORBIDDEN: char = '1'; 3 | pub const DEL_FLAG_NORMAL: char = '0'; 4 | pub const DEL_FLAG_REMOVED: char = '2'; 5 | 6 | 7 | pub const CHAR_TRUE: char = '0'; 8 | pub const CHAR_FALSE: char = '1'; 9 | 10 | pub const LOGIN_SUC: char = '0'; 11 | pub const LOGIN_FAIL: char = '1'; 12 | 13 | /** 菜单类型(目录) */ 14 | pub const TYPE_DIR: char = 'M'; 15 | 16 | /** 菜单类型(菜单) */ 17 | pub const TYPE_MENU: char = 'C'; 18 | 19 | /** 菜单类型(按钮) */ 20 | pub const TYPE_BUTTON: char = 'F'; 21 | 22 | /** Layout组件标识 */ 23 | pub const LAYOUT: &'static str = "Layout"; 24 | 25 | /** ParentView组件标识 */ 26 | pub const PARENT_VIEW: &'static str = "ParentView"; 27 | 28 | /** InnerLink组件标识 */ 29 | pub const INNER_LINK: &'static str = "InnerLink"; 30 | 31 | /** 代表所有权限 */ 32 | pub const ALL_PERMISSIONS: &'static str = "*:*:*"; 33 | 34 | pub const ADMIN_NAME: &'static str = "admin"; -------------------------------------------------------------------------------- /src/config/log.rs: -------------------------------------------------------------------------------- 1 | use crate::service::CONTEXT; 2 | use fast_log::config::Config; 3 | use fast_log::consts::LogSize; 4 | use fast_log::plugin::file_split::{FileSplitAppender, Packer, RollingType}; 5 | use std::time::Duration; 6 | 7 | pub fn init_log() { 8 | //create log dir 9 | let _ = std::fs::create_dir_all(&CONTEXT.config.log_dir); 10 | //init fast log 11 | let mut cfg = Config::new() 12 | .level(str_to_log_level(&CONTEXT.config.log_level)) 13 | .custom(FileSplitAppender::new( 14 | &CONTEXT.config.log_dir, 15 | str_to_temp_size(&CONTEXT.config.log_temp_size), 16 | str_to_rolling(&CONTEXT.config.log_rolling_type), 17 | choose_packer(&CONTEXT.config.log_pack_compress), 18 | ).unwrap()); 19 | if CONTEXT.config.debug { 20 | cfg = cfg.console(); 21 | } 22 | cfg = cfg.chan_len(CONTEXT.config.log_chan_len); 23 | let _ = fast_log::init(cfg); 24 | if CONTEXT.config.debug == false { 25 | println!("[abs_admin] release_mode is up! [file_log] open,[console_log] disabled!"); 26 | } 27 | } 28 | 29 | fn choose_packer(packer: &str) -> Box { 30 | match packer { 31 | // "lz4" => Box::new(fast_log::plugin::packer::LZ4Packer {}), 32 | // "zip" => Box::new(fast_log::plugin::packer::ZipPacker {}), 33 | // "gzip" => Box::new(fast_log::plugin::packer::GZipPacker {}), 34 | _ => Box::new(fast_log::plugin::packer::LogPacker {}), 35 | } 36 | } 37 | 38 | fn str_to_temp_size(arg: &str) -> LogSize { 39 | match arg { 40 | arg if arg.ends_with("MB") => { 41 | let end = arg.find("MB").unwrap(); 42 | let num = arg[0..end].to_string(); 43 | LogSize::MB(num.parse::().unwrap()) 44 | } 45 | arg if arg.ends_with("KB") => { 46 | let end = arg.find("KB").unwrap(); 47 | let num = arg[0..end].to_string(); 48 | LogSize::KB(num.parse::().unwrap()) 49 | } 50 | arg if arg.ends_with("GB") => { 51 | let end = arg.find("GB").unwrap(); 52 | let num = arg[0..end].to_string(); 53 | LogSize::GB(num.parse::().unwrap()) 54 | } 55 | _ => LogSize::MB(100), 56 | } 57 | } 58 | 59 | fn str_to_rolling(arg: &str) -> RollingType { 60 | match arg { 61 | arg if arg.starts_with("KeepNum(") => { 62 | let end = arg.find(")").unwrap(); 63 | let num = arg["KeepNum(".len()..end].to_string(); 64 | RollingType::KeepNum(num.parse::().unwrap()) 65 | } 66 | arg if arg.starts_with("KeepTime(") => { 67 | let end = arg.find(")").unwrap(); 68 | let num = arg["KeepTime(".len()..end].to_string(); 69 | RollingType::KeepTime(Duration::from_secs(num.parse::().unwrap())) 70 | } 71 | _ => RollingType::All, 72 | } 73 | } 74 | 75 | fn str_to_log_level(arg: &str) -> log::LevelFilter { 76 | return match arg { 77 | "off" => log::LevelFilter::Off, 78 | "warn" => log::LevelFilter::Warn, 79 | "error" => log::LevelFilter::Error, 80 | "trace" => log::LevelFilter::Trace, 81 | "info" => log::LevelFilter::Info, 82 | "debug" => log::LevelFilter::Debug, 83 | _ => log::LevelFilter::Info, 84 | }; 85 | } -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod log; 3 | pub mod cache_variables; 4 | pub mod global_variables; -------------------------------------------------------------------------------- /src/controller/img_controller.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use actix_web::{Responder}; 4 | use captcha::Captcha; 5 | use captcha::filters::{Dots, Noise, Wave}; 6 | use uuid::Uuid; 7 | 8 | use crate::config::cache_variables::REDIS_UUID_CAPTCHA; 9 | use crate::domain::vo::{RespJson, RespVO}; 10 | use crate::service::CONTEXT; 11 | use crate::util::base64::encode; 12 | 13 | pub async fn captcha() -> impl Responder { 14 | let mut json = RespJson::success(); 15 | json.insert("captchaEnabled".to_string(), CONTEXT.config.captcha_enabled.into()); 16 | 17 | if CONTEXT.config.captcha_enabled { 18 | let id = Uuid::new_v4(); 19 | let mut captcha = Captcha::new(); 20 | captcha 21 | .add_chars(4) 22 | .apply_filter(Noise::new(0.1)) 23 | .apply_filter(Wave::new(1.0, 10.0).horizontal()) 24 | // .apply_filter(Wave::new(2.0, 20.0).vertical()) 25 | .view(160, 60) 26 | .apply_filter(Dots::new(4)); 27 | let png = captcha.as_png().unwrap(); 28 | let captcha_str = captcha.chars_as_string().to_lowercase(); 29 | if CONTEXT.config.debug { 30 | log::info!( 31 | "uuid:{},captcha:{}", 32 | &id.to_string(), 33 | &captcha_str 34 | ); 35 | } 36 | 37 | let result = CONTEXT 38 | .cache_service 39 | .set_string_ex( 40 | &format!("{}{}", REDIS_UUID_CAPTCHA, &id.to_string()), 41 | captcha_str.as_str(), 42 | Some(Duration::from_secs( 43 | CONTEXT.config.captcha_expired_min * 60 44 | )), 45 | ) 46 | .await; 47 | //println!("{:?}", result); 48 | if CONTEXT.config.debug == false { 49 | //release mode, return the error 50 | if result.is_err() { 51 | return RespVO::from_result(&result).resp_json(); 52 | } 53 | } 54 | json.insert("uuid".to_string(), id.to_string().into()); 55 | json.insert("img".to_string(), encode(&png, png.len()).into()); 56 | // img_vo.uuid = id.to_string(); 57 | // img_vo.img = encode(&png, png.len()); 58 | } 59 | json.resp_json() 60 | } 61 | -------------------------------------------------------------------------------- /src/controller/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod img_controller; 2 | pub mod sys_auth_controller; 3 | pub mod sys_dict_type_controller; 4 | pub mod sys_dict_data_controller; 5 | pub mod sys_menu_controller; 6 | pub mod sys_role_controller; 7 | pub mod sys_user_controller; 8 | pub mod sys_config_controller; 9 | pub mod sys_dept_controller; 10 | pub mod sys_post_controller; 11 | pub mod monitor; 12 | pub mod sys_profile_controller; 13 | pub mod sys_notice_controller; 14 | -------------------------------------------------------------------------------- /src/controller/monitor/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sys_user_online_controller; 2 | pub mod sys_logininfor_controller; 3 | pub mod server_controller; 4 | -------------------------------------------------------------------------------- /src/controller/monitor/server_controller.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{ get, Responder, HttpRequest}; 2 | 3 | use crate::domain::vo::{RespVO}; 4 | use permit_lib::has_permit; 5 | use crate::util::hardware::get_server_info; 6 | 7 | #[get("/server")] 8 | #[has_permit("monitor:server:list")] 9 | pub async fn server_info() -> impl Responder { 10 | RespVO::from_result(&Ok(get_server_info())).resp_json() 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/controller/monitor/sys_logininfor_controller.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{delete, get, Responder, web,HttpRequest}; 2 | 3 | use crate::domain::dto::LogininforPageDTO; 4 | use crate::domain::vo::{PageVO, RespVO}; 5 | use crate::service::CONTEXT; 6 | use permit_lib::has_permit; 7 | 8 | #[get("/logininfor/list")] 9 | #[has_permit("monitor:logininfor:query")] 10 | pub async fn page(page: web::Query) -> impl Responder { 11 | let data = CONTEXT.sys_logininfor_service.page(&page.0).await; 12 | PageVO::from_result(&data).resp_json() 13 | } 14 | 15 | 16 | #[delete("/logininfor/{info_id}")] 17 | #[has_permit("monitor:logininfor:remove")] 18 | pub async fn remove(info_id: web::Path) -> impl Responder { 19 | let info_id = info_id.into_inner(); 20 | let data = CONTEXT 21 | .sys_logininfor_service 22 | .remove(&info_id) 23 | .await.unwrap(); 24 | RespVO::::judge(data, "".to_string(), "删除失败!".to_string()).resp_json() 25 | } 26 | -------------------------------------------------------------------------------- /src/controller/monitor/sys_user_online_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::vo:: {RespJson, RespVO, SysUserOnlineVO, UserCache}; 2 | use crate::service::CONTEXT; 3 | use actix_web::{get, delete, web, Responder}; 4 | use crate::config::cache_variables::LOGIN_TOKEN_KEY; 5 | use permit_lib::has_permit; 6 | use actix_web::HttpRequest; 7 | use crate::error::Error; 8 | 9 | #[get("/online/list")] 10 | #[has_permit("monitor:online:list")] 11 | pub async fn page() -> impl Responder { 12 | let keys = CONTEXT.cache_service.keys(&format!("{}*", LOGIN_TOKEN_KEY)).await.unwrap(); 13 | 14 | let mut user_online_list = vec![]; 15 | 16 | for k in keys { 17 | let c: Result = CONTEXT.cache_service.get_json(&k).await; 18 | match c { 19 | Ok(u) => { 20 | let user_online = SysUserOnlineVO { 21 | token_id: Some(u.token_key.trim_start_matches(LOGIN_TOKEN_KEY).to_string()), 22 | dept_name: None, 23 | user_name: Some(u.user_name), 24 | ipaddr: None, 25 | login_location: None, 26 | phonenumber: None, 27 | browser: None, 28 | os: None, 29 | login_time: Some(u.login_time), 30 | }; 31 | user_online_list.push(user_online); 32 | } 33 | Err(_) => {} 34 | } 35 | } 36 | let mut res = RespJson::success(); 37 | res.insert("rows".to_string(), serde_json::json!(user_online_list)); 38 | res.insert("total".to_string(), serde_json::json!(user_online_list.len())); 39 | res.resp_json() 40 | } 41 | 42 | #[delete("/online/{token_id}")] 43 | #[has_permit("system:online:force_logout")] 44 | pub async fn force_logout(token_id: web::Path) -> impl Responder { 45 | let res = CONTEXT.cache_service.del(&format!("{}{}", LOGIN_TOKEN_KEY, token_id.into_inner())).await.unwrap(); 46 | if res { 47 | RespVO::::from_success_info("强制成功!").resp_json() 48 | } else { 49 | RespVO::::from_success_info("强制失败!").resp_json() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/controller/sys_auth_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::vo::{JWTToken, RespJson, RespVO}; 2 | use crate::service::CONTEXT; 3 | use actix_web::{HttpRequest, Responder, web}; 4 | use permit_lib::has_permit; 5 | use crate::config::cache_variables::LOGIN_TOKEN_KEY; 6 | use crate::config::global_variables::LOGIN_SUC; 7 | use crate::domain::dto::SignInDTO; 8 | use crate::token_auth::{get_token}; 9 | use crate::web_data::get_user_name; 10 | 11 | // ///Check whether the token_auth and path are valid and accessible 12 | // pub async fn check(arg: web::Json) -> impl Responder { 13 | // let r = CONTEXT.sys_auth_service.check_auth(arg.0).await; 14 | // RespVO::from_result(&r).resp_json() 15 | // } 16 | 17 | pub async fn login(req: HttpRequest, arg: web::Json) -> impl Responder { 18 | let token = CONTEXT.sys_user_service.login(&arg.0, &req).await; 19 | if token.is_err() { 20 | return RespVO::from_error_result(500, &token).resp_json(); 21 | } 22 | let token = token.unwrap(); 23 | let mut res = RespJson::success(); 24 | res.insert("token".to_string(), token.into()); 25 | return res.resp_json(); 26 | } 27 | 28 | pub async fn logout(req: HttpRequest) -> impl Responder { 29 | let token = get_token(&req); 30 | if !token.is_empty() { 31 | let claims = JWTToken::verify(&CONTEXT.config.jwt_secret, &token); 32 | if claims.is_ok() { 33 | let login_user_key = claims.unwrap().login_user_key; 34 | CONTEXT.sys_logininfor_service.add_async(&crate::util::web_utils::build_logininfor(&req, get_user_name(), LOGIN_SUC, "退出成功".to_string())).await; 35 | CONTEXT.cache_service.del(&format!("{}{}", LOGIN_TOKEN_KEY, login_user_key)).await; 36 | } 37 | } 38 | RespVO::::from_success_info("退出成功!").resp_json() 39 | } 40 | 41 | #[has_permit("")] 42 | pub async fn info(req: HttpRequest) -> impl Responder { 43 | // match crate::token_auth::check_permit(req, "").await { 44 | // None => {} 45 | // Some(res) => { return res.resp_json(); } 46 | // } 47 | 48 | let user_cache = CONTEXT.sys_user_service.get_user_cache(&req).await; 49 | // let user_data = CONTEXT 50 | // .sys_user_service 51 | // .get_user_info_by_token(&user_cache) 52 | // .await; 53 | if user_cache.is_err() { 54 | return RespVO::::from_error_info(401, "请重新!").resp_json(); 55 | } 56 | let user_cache = user_cache.unwrap(); 57 | let mut res = RespJson::success(); 58 | res.insert("permissions".to_string(), serde_json::json!(&user_cache.permissions)); 59 | res.insert("user".to_string(), serde_json::json!(&user_cache.user)); 60 | res.insert("roles".to_string(), serde_json::json!(rbatis::make_table_field_vec!(&user_cache.roles , role_key))); 61 | res.resp_json() 62 | } 63 | -------------------------------------------------------------------------------- /src/controller/sys_config_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{ConfigAddDTO, ConfigUpdateDTO, ConfigPageDTO}; 2 | use crate::domain::table::SysConfig; 3 | use crate::domain::vo::{PageVO, RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, delete, web, Responder}; 6 | use permit_lib::has_permit; 7 | use actix_web::HttpRequest; 8 | 9 | #[get("/config/list")] 10 | #[has_permit("system:config:query")] 11 | pub async fn page(page: web::Query) -> impl Responder { 12 | let data = CONTEXT.sys_config_service.page(&page.0).await; 13 | PageVO::from_result(&data).resp_json() 14 | } 15 | 16 | 17 | #[get("/config/{config_id}")] 18 | #[has_permit("system:config:query")] 19 | pub async fn detail(config_id: web::Path) -> impl Responder { 20 | let config_id = config_id.into_inner(); 21 | let config_vo = CONTEXT.sys_config_service.detail(&config_id).await; 22 | RespVO::from_result(&config_vo).resp_json() 23 | } 24 | 25 | 26 | #[post("/config")] 27 | #[has_permit("system:config:add")] 28 | pub async fn add(arg: web::Json) -> impl Responder { 29 | let mut data = SysConfig::from(arg.0); 30 | data.create_by = Some(crate::web_data::get_user_name()); 31 | let res = SysConfig::from(data); 32 | let data = CONTEXT.sys_config_service.add(&res).await; 33 | RespVO::from_result(&data).resp_json() 34 | } 35 | 36 | #[put("/config")] 37 | #[has_permit("system:config:edit")] 38 | pub async fn update(arg: web::Json) -> impl Responder { 39 | let mut data = SysConfig::from(arg.0); 40 | data.update_by = Some(crate::web_data::get_user_name()); 41 | let data = CONTEXT.sys_config_service.update(data).await; 42 | RespVO::from_result(&data).resp_json() 43 | } 44 | 45 | #[delete("/config/{config_id}")] 46 | #[has_permit("system:config:remove")] 47 | pub async fn remove(config_id: web::Path) -> impl Responder { 48 | let config_id = config_id.into_inner(); 49 | let data = CONTEXT 50 | .sys_config_service 51 | .remove(&config_id) 52 | .await; 53 | RespVO::::judge(data.unwrap(), "".to_string(), "删除失败!".to_string()).resp_json() 54 | } 55 | -------------------------------------------------------------------------------- /src/controller/sys_dept_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{DeptAddDTO, DeptUpdateDTO, DeptQueryDTO}; 2 | use crate::domain::table::SysDept; 3 | use crate::domain::vo::{ RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, delete, web, Responder}; 6 | use permit_lib::has_permit; 7 | use actix_web::HttpRequest; 8 | use crate::config::global_variables::STATUS_NORMAL; 9 | 10 | #[get("/dept/list")] 11 | #[has_permit("system:dept:query")] 12 | pub async fn page(query: web::Query) -> impl Responder { 13 | let data = CONTEXT.sys_dept_service.all(&query).await; 14 | RespVO::from_result(&data).resp_json() 15 | } 16 | 17 | 18 | #[get("/dept/{dept_id}")] 19 | #[has_permit("system:dept:query")] 20 | pub async fn detail(dept_id: web::Path) -> impl Responder { 21 | let dept_id = dept_id.into_inner(); 22 | let dept_vo = CONTEXT.sys_dept_service.detail(&dept_id).await; 23 | RespVO::from_result(&dept_vo).resp_json() 24 | } 25 | 26 | 27 | #[post("/dept")] 28 | #[has_permit("system:dept:add")] 29 | pub async fn add(arg: web::Json) -> impl Responder { 30 | let mut data = SysDept::from(arg.0); 31 | data.create_by = Some(crate::web_data::get_user_name()); 32 | if data.status.is_none() { 33 | data.status = Some(STATUS_NORMAL); 34 | } 35 | let data = CONTEXT.sys_dept_service.add(&data).await.unwrap(); 36 | RespVO::::judge(data, "".to_string(), "添加失败!".to_string()).resp_json() 37 | } 38 | 39 | #[put("/dept")] 40 | #[has_permit("system:dept:edit")] 41 | pub async fn update(arg: web::Json) -> impl Responder { 42 | let mut data = SysDept::from(arg.0); 43 | data.update_by = Some(crate::web_data::get_user_name()); 44 | let data = CONTEXT.sys_dept_service.update(data).await.unwrap(); 45 | RespVO::::judge(data, "".to_string(), "更新失败!".to_string()).resp_json() 46 | } 47 | 48 | #[delete("/dept/{dept_id}")] 49 | #[has_permit("system:dept:remove")] 50 | pub async fn remove(dept_id: web::Path) -> impl Responder { 51 | let dept_id = dept_id.into_inner(); 52 | let data = CONTEXT 53 | .sys_dept_service 54 | .remove(&dept_id) 55 | .await.unwrap(); 56 | RespVO::::judge(data, "".to_string(), "删除失败!".to_string()).resp_json() 57 | } 58 | -------------------------------------------------------------------------------- /src/controller/sys_dict_data_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{DictDataAddDTO, DictDataUpdateDTO, DictDataPageDTO}; 2 | use crate::domain::table::SysDictData; 3 | use crate::domain::vo::{PageVO, RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, delete, web, Responder}; 6 | use crate::config::global_variables::STATUS_NORMAL; 7 | use permit_lib::has_permit; 8 | use actix_web::HttpRequest; 9 | 10 | #[get("/dict/data/list")] 11 | #[has_permit("system:dict:query")] 12 | pub async fn page(page: web::Query) -> impl Responder { 13 | let data = CONTEXT.sys_dict_data_service.page(&page.0).await; 14 | PageVO::from_result(&data).resp_json() 15 | } 16 | 17 | 18 | #[get("/dict/data/{dict_id}")] 19 | #[has_permit("system:dict:query")] 20 | pub async fn detail(dict_id: web::Path) -> impl Responder { 21 | let dict_id = dict_id.into_inner(); 22 | let dict_data_vo = CONTEXT.sys_dict_data_service.detail(&dict_id).await; 23 | RespVO::from_result(&dict_data_vo).resp_json() 24 | } 25 | 26 | #[post("/dict/data")] 27 | #[has_permit("system:dict:add")] 28 | pub async fn add(arg: web::Json) -> impl Responder { 29 | let mut data = SysDictData::from(arg.0); 30 | data.create_by = Some(crate::web_data::get_user_name()); 31 | if data.status.is_none() { 32 | data.status = Some(STATUS_NORMAL); 33 | } 34 | let data = CONTEXT.sys_dict_data_service.add(&data).await.unwrap(); 35 | RespVO::::judge(data, "".to_string(), "添加失败!".to_string()).resp_json() 36 | } 37 | 38 | #[put("/dict/data")] 39 | #[has_permit("system:dict:edit")] 40 | pub async fn update(arg: web::Json) -> impl Responder { 41 | let mut data = SysDictData::from(arg.0); 42 | data.update_by = Some(crate::web_data::get_user_name()); 43 | let data = CONTEXT.sys_dict_data_service.update(data).await.unwrap(); 44 | RespVO::::judge(data, "".to_string(), "更新失败!".to_string()).resp_json() 45 | } 46 | 47 | #[delete("/dict/data/{dict_code}")] 48 | #[has_permit("system:dict:remove")] 49 | pub async fn remove(dict_id: web::Path) -> impl Responder { 50 | let dict_id = dict_id.into_inner(); 51 | let data = CONTEXT 52 | .sys_dict_data_service 53 | .remove(&dict_id) 54 | .await.unwrap(); 55 | RespVO::::judge(data, "".to_string(), "删除失败!".to_string()).resp_json() 56 | } 57 | 58 | 59 | #[get("/dict/data/type/{dict_type}")] 60 | #[has_permit("")] 61 | pub async fn get_by_dict_type(dict_type: web::Path) -> impl Responder { 62 | let dict_type = dict_type.into_inner(); 63 | let dict_data_vo = CONTEXT.sys_dict_data_service.get_by_dict_type(&dict_type).await; 64 | RespVO::from_result(&dict_data_vo).resp_json() 65 | } -------------------------------------------------------------------------------- /src/controller/sys_dict_type_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{DictTypeAddDTO, DictTypeUpdateDTO, DictTypePageDTO}; 2 | use crate::domain::table::SysDictType; 3 | use crate::domain::vo::{PageVO, RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, web, Responder, HttpRequest, delete}; 6 | use crate::config::global_variables::STATUS_NORMAL; 7 | use permit_lib::has_permit; 8 | 9 | #[get("/dict/type/list")] 10 | #[has_permit("system:dict:query")] 11 | pub async fn page(page: web::Query) -> impl Responder { 12 | let data = CONTEXT.sys_dict_type_service.page(&page.0).await; 13 | PageVO::from_result(&data).resp_json() 14 | } 15 | 16 | 17 | #[get("/dict/type/optionselect")] 18 | #[has_permit("system:dict:query")] 19 | pub async fn optionselect() -> impl Responder { 20 | let data = CONTEXT.sys_dict_type_service.finds_all().await; 21 | RespVO::from_result(&data).resp_json() 22 | } 23 | 24 | #[get("/dict/type/{dict_id}")] 25 | #[has_permit("system:dict:query")] 26 | pub async fn detail(dict_id: web::Path) -> impl Responder { 27 | let dict_id = dict_id.into_inner(); 28 | let dict_type_vo = CONTEXT.sys_dict_type_service.detail(&dict_id).await; 29 | RespVO::from_result(&dict_type_vo).resp_json() 30 | } 31 | 32 | #[post("/dict/type")] 33 | #[has_permit("system:dict:add")] 34 | pub async fn add(arg: web::Json) -> impl Responder { 35 | let mut data = SysDictType::from(arg.0); 36 | data.create_by = Some(crate::web_data::get_user_name()); 37 | if data.dict_name.is_none() { 38 | return RespVO::::from_error_info(500, "字典名字不能为空!").resp_json(); 39 | } 40 | if data.status.is_none() { 41 | data.status = Some(STATUS_NORMAL); 42 | } 43 | let data = CONTEXT.sys_dict_type_service.add(&data).await; 44 | RespVO::from_result(&data).resp_json() 45 | } 46 | 47 | #[put("/dict/type")] 48 | #[has_permit("system:dict:edit")] 49 | pub async fn update(arg: web::Json) -> impl Responder { 50 | let mut data = SysDictType::from(arg.0); 51 | data.update_by = Some(crate::web_data::get_user_name()); 52 | let data = CONTEXT.sys_dict_type_service.update(data).await; 53 | RespVO::from_result(&data).resp_json() 54 | } 55 | 56 | #[delete("/dict/type/{dict_id}")] 57 | #[has_permit("system:dict:remove")] 58 | pub async fn remove(dict_id: web::Path) -> impl Responder { 59 | let dict_id = dict_id.into_inner(); 60 | let data = CONTEXT.sys_dict_type_service 61 | .remove(&dict_id).await; 62 | RespVO::from_result(&data).resp_json() 63 | } 64 | -------------------------------------------------------------------------------- /src/controller/sys_menu_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{MenuAddDTO, MenuPageDTO, MenuUpdateDTO}; 2 | use crate::domain::table::{SysMenu}; 3 | use crate::domain::vo::{RespJson, RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, delete, web, Responder, HttpRequest}; 6 | use crate::config::global_variables::ADMIN_NAME; 7 | use permit_lib::has_permit; 8 | 9 | #[get("/menu/list")] 10 | #[has_permit("system:menu:query")] 11 | pub async fn query_menu(page: web::Query) -> impl Responder { 12 | let data = CONTEXT.sys_menu_service.query_menu(&page.0).await; 13 | RespVO::from_result(&data).resp_json() 14 | } 15 | 16 | 17 | //菜单栏生成 18 | #[has_permit("")] 19 | pub async fn routers(req: HttpRequest) -> impl Responder { 20 | let user_cache = CONTEXT.sys_user_service.get_user_cache(&req).await; 21 | let data = CONTEXT.sys_menu_service.get_routers(&user_cache.unwrap()).await; 22 | RespVO::from_result(&data).resp_json() 23 | } 24 | 25 | #[get("/menu/{menu_id}")] 26 | #[has_permit("system:menu:query")] 27 | pub async fn detail(menu_id: web::Path) -> impl Responder { 28 | let menu_id = menu_id.into_inner(); 29 | let menu_vo = CONTEXT.sys_menu_service.detail(&menu_id).await; 30 | RespVO::from_result(&menu_vo).resp_json() 31 | } 32 | 33 | #[post("/menu")] 34 | #[has_permit("system:menu:add")] 35 | pub async fn add(arg: actix_web_validator::Json) -> impl Responder { 36 | let mut data = SysMenu::from(arg.0); 37 | data.create_by = Some(crate::web_data::get_user_name()); 38 | if data.path.is_none() { 39 | data.path = Some("".to_string()); 40 | } 41 | let data = CONTEXT.sys_menu_service.add(&data).await; 42 | CONTEXT.sys_menu_service.update_cache().await; 43 | RespVO::from_result(&data).resp_json() 44 | } 45 | 46 | #[put("/menu")] 47 | #[has_permit("system:menu:edit")] 48 | pub async fn update(arg: web::Json) -> impl Responder { 49 | let mut menu = SysMenu::from(arg.0); 50 | menu.update_by = Some(crate::web_data::get_user_name()); 51 | 52 | let cnt = CONTEXT.sys_menu_service.update(&menu).await; 53 | 54 | CONTEXT.sys_menu_service.update_cache().await; 55 | RespVO::from_result(&cnt).resp_json() 56 | } 57 | 58 | #[delete("/menu/{menu_id}")] 59 | #[has_permit("system:menu:remove")] 60 | pub async fn remove(menu_id: web::Path) -> impl Responder { 61 | let menu_id = menu_id.into_inner(); 62 | let data = CONTEXT.sys_menu_service 63 | .remove(&menu_id).await; 64 | CONTEXT.sys_menu_service.update_cache().await; 65 | RespVO::from_result(&data).resp_json() 66 | } 67 | 68 | #[get("/menu/treeselect")] 69 | #[has_permit("system:menu:query")] 70 | pub async fn treeselect(req: HttpRequest) -> impl Responder { 71 | let user_cache = CONTEXT.sys_user_service.get_user_cache(&req).await.unwrap(); 72 | let menus = if user_cache.user_name == ADMIN_NAME { 73 | CONTEXT.sys_menu_service.all().await 74 | } else { 75 | CONTEXT.sys_menu_service.get_menu_list_by_user_id(&user_cache.id).await 76 | }; 77 | let menu_tree = CONTEXT.sys_menu_service.build_menu_tree(&menus.unwrap()).unwrap(); 78 | let menu_select = CONTEXT.sys_menu_service.tree_select(menu_tree); 79 | RespVO::from_result(&menu_select).resp_json() 80 | } 81 | 82 | #[get("/menu/roleMenuTreeselect/{role_id}")] 83 | #[has_permit("system:menu:query")] 84 | pub async fn role_menu_treeselect(req: HttpRequest, role_id: web::Path) -> impl Responder { 85 | let role_id = role_id.into_inner(); 86 | 87 | let user_cache = CONTEXT.sys_user_service.get_user_cache(&req).await.unwrap(); 88 | let menus = if user_cache.user_name == ADMIN_NAME { 89 | CONTEXT.sys_menu_service.all().await 90 | } else { 91 | CONTEXT.sys_menu_service.get_menu_list_by_user_id(&user_cache.id).await 92 | }; 93 | let menu_tree = CONTEXT.sys_menu_service.build_menu_tree(&menus.unwrap()).unwrap(); 94 | let menu_select = CONTEXT.sys_menu_service.tree_select(menu_tree); 95 | 96 | let checked_keys = CONTEXT.sys_role_menu_service.select_by_role_id(&role_id).await.unwrap().into_iter().map(|m| m.menu_id.unwrap()).collect::>(); 97 | 98 | let mut res = RespJson::success(); 99 | res.insert("checkedKeys".to_string(), serde_json::json!(checked_keys)); 100 | res.insert("menus".to_string(), serde_json::json!(menu_select.unwrap())); 101 | res.resp_json() 102 | } -------------------------------------------------------------------------------- /src/controller/sys_notice_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{NoticeAddDTO, NoticeUpdateDTO, NoticePageDTO}; 2 | use crate::domain::table::SysNotice; 3 | use crate::domain::vo::{PageVO, RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, delete, web, Responder}; 6 | use permit_lib::has_permit; 7 | use actix_web::HttpRequest; 8 | 9 | #[get("/notice/list")] 10 | #[has_permit("system:notice:query")] 11 | pub async fn page(page: web::Query) -> impl Responder { 12 | let data = CONTEXT.sys_notice_service.page(&page.0).await; 13 | PageVO::from_result(&data).resp_json() 14 | } 15 | 16 | 17 | #[get("/notice/{notice_id}")] 18 | #[has_permit("system:notice:query")] 19 | pub async fn detail(notice_id: web::Path) -> impl Responder { 20 | let notice_id = notice_id.into_inner(); 21 | let notice_vo = CONTEXT.sys_notice_service.detail(¬ice_id).await; 22 | RespVO::from_result(¬ice_vo).resp_json() 23 | } 24 | 25 | 26 | #[post("/notice")] 27 | #[has_permit("system:notice:add")] 28 | pub async fn add(arg: web::Json) -> impl Responder { 29 | let mut data = SysNotice::from(arg.0); 30 | data.create_by = Some(crate::web_data::get_user_name()); 31 | let res = SysNotice::from(data); 32 | let data = CONTEXT.sys_notice_service.add(&res).await; 33 | RespVO::from_result(&data).resp_json() 34 | } 35 | 36 | #[put("/notice")] 37 | #[has_permit("system:notice:edit")] 38 | pub async fn update(arg: web::Json) -> impl Responder { 39 | let mut data = SysNotice::from(arg.0); 40 | data.update_by = Some(crate::web_data::get_user_name()); 41 | let data = CONTEXT.sys_notice_service.update(data).await; 42 | RespVO::from_result(&data).resp_json() 43 | } 44 | 45 | #[delete("/notice/{notice_id}")] 46 | #[has_permit("system:notice:remove")] 47 | pub async fn remove(notice_id: web::Path) -> impl Responder { 48 | let notice_id = notice_id.into_inner(); 49 | let data = CONTEXT 50 | .sys_notice_service 51 | .remove(¬ice_id) 52 | .await; 53 | RespVO::::judge(data.unwrap(), "".to_string(), "删除失败!".to_string()).resp_json() 54 | } 55 | -------------------------------------------------------------------------------- /src/controller/sys_post_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{PostAddDTO, PostUpdateDTO, PostPageDTO}; 2 | use crate::domain::table::SysPost; 3 | use crate::domain::vo::{PageVO, RespVO}; 4 | use crate::service::CONTEXT; 5 | use actix_web::{get, post, put, delete, web,HttpRequest, Responder}; 6 | use crate::config::global_variables::STATUS_NORMAL; 7 | use permit_lib::has_permit; 8 | 9 | #[get("/post/list")] 10 | #[has_permit("system:post:query")] 11 | pub async fn page(query: web::Query) -> impl Responder { 12 | let data = CONTEXT.sys_post_service.page(&query).await; 13 | PageVO::from_result(&data).resp_json() 14 | } 15 | 16 | 17 | #[get("/post/{dict_id}")] 18 | #[has_permit("system:post:query")] 19 | pub async fn detail(dict_id: web::Path) -> impl Responder { 20 | let dict_id = dict_id.into_inner(); 21 | let post_vo = CONTEXT.sys_post_service.detail(&dict_id).await; 22 | RespVO::from_result(&post_vo).resp_json() 23 | } 24 | 25 | 26 | #[post("/post")] 27 | #[has_permit("system:post:add")] 28 | pub async fn add(arg: web::Json) -> impl Responder { 29 | let mut data = SysPost::from(arg.0); 30 | data.create_by = Some(crate::web_data::get_user_name()); 31 | if data.status.is_none() { 32 | data.status = Some(STATUS_NORMAL); 33 | } 34 | let data = CONTEXT.sys_post_service.add(&data).await.unwrap(); 35 | RespVO::::judge(data, "".to_string(), "添加失败!".to_string()).resp_json() 36 | } 37 | 38 | #[put("/post")] 39 | #[has_permit("system:post:edit")] 40 | pub async fn update(arg: web::Json) -> impl Responder { 41 | let mut data = SysPost::from(arg.0); 42 | data.update_by = Some(crate::web_data::get_user_name()); 43 | let data = CONTEXT.sys_post_service.update(data).await.unwrap(); 44 | RespVO::::judge(data, "".to_string(), "更新失败!".to_string()).resp_json() 45 | } 46 | 47 | #[delete("/post/{post_id}")] 48 | #[has_permit("system:post:remove")] 49 | pub async fn remove(dict_id: web::Path) -> impl Responder { 50 | let dict_id = dict_id.into_inner(); 51 | let data = CONTEXT 52 | .sys_post_service 53 | .remove(&dict_id) 54 | .await.unwrap(); 55 | RespVO::::judge(data, "".to_string(), "删除失败!".to_string()).resp_json() 56 | } 57 | -------------------------------------------------------------------------------- /src/controller/sys_profile_controller.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use actix_web::{get, HttpRequest, put, Responder, web}; 3 | 4 | use crate::domain::vo::{RespJson, RespVO}; 5 | use crate::service::CONTEXT; 6 | use permit_lib::has_permit; 7 | use crate::domain::dto::{PasswordUpdateDTO, UserUpdateDTO}; 8 | use crate::util::password_encoder::PasswordEncoder; 9 | 10 | /* 11 | * 用户自身的操作 12 | */ 13 | 14 | //的用户信息 15 | #[get("/user/profile")] 16 | #[has_permit("")] 17 | pub async fn profile(req: HttpRequest) -> impl Responder { 18 | let user_cahce = CONTEXT.sys_user_service.get_user_cache(&req).await.unwrap(); 19 | let mut res = RespJson::success_info("操作成功"); 20 | 21 | res.insert("data".to_string(), serde_json::json!(user_cahce.user.unwrap())); 22 | //todo 职位 23 | // res.insert("posts".to_string(), serde_json::json!(CONTEXT.sys_post_service.finds_all().await.unwrap())); 24 | res.insert("roleGroup".to_string(), serde_json::json!(user_cahce.roles.clone().into_iter().map(|r|r.role_name.unwrap()).collect::>().join(","))); 25 | return res.resp_json(); 26 | } 27 | 28 | //用户自行修改用户信息 29 | #[put("/user/profile")] 30 | #[has_permit("")] 31 | pub async fn update_profile(req: HttpRequest, mut arg: web::Json) -> impl Responder { 32 | let mut user_cache = CONTEXT.sys_user_service.get_user_cache(&req).await.unwrap(); 33 | let clone = arg.0.clone(); 34 | arg.0.user_id = user_cache.id.clone().into(); 35 | let vo = CONTEXT.sys_user_service.update(arg.0).await.unwrap(); 36 | if vo > 0 { 37 | let mut user = user_cache.user.clone().unwrap(); 38 | user.phonenumber = clone.phonenumber; 39 | user.email = clone.email; 40 | user_cache.user = user.into(); 41 | CONTEXT.cache_service.set_string_ex( 42 | &user_cache.token_key, 43 | &user_cache.to_string(), 44 | Some(Duration::from_secs( 45 | CONTEXT.config.token_expired_min * 60 46 | )), 47 | ).await; 48 | } 49 | return RespVO::from_result(&Ok(vo)).resp_json(); 50 | } 51 | 52 | //用户自行修改密码 53 | #[put("/user/profile/updatePwd")] 54 | #[has_permit("")] 55 | pub async fn update_pwd(req: HttpRequest, arg: web::Query) -> impl Responder { 56 | let user_cache = CONTEXT.sys_user_service.get_user_cache(&req).await.unwrap(); 57 | let user_id = user_cache.id.clone(); 58 | let user = CONTEXT.sys_user_service.find(&user_id).await.unwrap().unwrap(); 59 | let new_password = &arg.new_password; 60 | let old_password = &arg.old_password; 61 | if new_password.eq(old_password) { 62 | return RespVO::::from_error_info(500, "新密码不能与旧密码相同").resp_json(); 63 | } 64 | 65 | 66 | if !PasswordEncoder::verify(user.password.as_ref().unwrap(), &old_password.clone().unwrap()) { 67 | return RespVO::::from_error_info(500, "修改密码失败,旧密码错误").resp_json(); 68 | } 69 | let vo = CONTEXT.sys_user_service.update_password( 70 | &PasswordEncoder::encode(new_password.as_ref().unwrap()), &user_id).await; 71 | return RespVO::from_result(&vo).resp_json(); 72 | } -------------------------------------------------------------------------------- /src/controller/sys_role_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{RoleAddDTO, RoleUpdateDTO, RolePageDTO, RoleAuthUserPageDTO, UserRoleDTO, UsersRoleDTO}; 2 | use crate::domain::vo::{PageVO, RespJson, RespVO}; 3 | use crate::service::CONTEXT; 4 | use actix_web::{get, post, put, web, Responder, HttpRequest, delete}; 5 | use crate::domain::table::{SysRole, SysUserRole}; 6 | use permit_lib::has_permit; 7 | 8 | #[get("/role/list")] 9 | #[has_permit("system:role:query")] 10 | pub async fn page(arg: web::Query) -> impl Responder { 11 | let vo = CONTEXT.sys_role_service.page(&arg.0).await; 12 | return PageVO::from_result(&vo).resp_json(); 13 | } 14 | 15 | #[get("/role/{role_id}")] 16 | #[has_permit("system:role:query")] 17 | pub async fn detail(role_id: web::Path) -> impl Responder { 18 | let role_id = role_id.into_inner(); 19 | let role_vo = CONTEXT.sys_role_service.detail(&role_id).await; 20 | RespVO::from_result(&role_vo).resp_json() 21 | } 22 | 23 | #[post("/role")] 24 | #[has_permit("system:role:add")] 25 | pub async fn add(arg: web::Json) -> impl Responder { 26 | let menu_ids = arg.0.menu_ids.clone().unwrap(); 27 | let mut data = SysRole::from(arg.0); 28 | 29 | data.create_by = Some(crate::web_data::get_user_name()); 30 | let vo = CONTEXT.sys_role_service.add(data, menu_ids).await; 31 | return RespVO::from_result(&vo).resp_json(); 32 | } 33 | 34 | #[put("/role")] 35 | #[has_permit("system:role:edit")] 36 | pub async fn update(arg: web::Json) -> impl Responder { 37 | let menu_ids = arg.0.menu_ids.clone().unwrap(); 38 | let mut data = SysRole::from(arg.0); 39 | data.update_by = Some(crate::web_data::get_user_name()); 40 | let vo = CONTEXT.sys_role_service.update(data, menu_ids).await; 41 | return RespVO::from_result(&vo).resp_json(); 42 | } 43 | 44 | #[delete("/role/{role_id}")] 45 | #[has_permit("system:role:remove")] 46 | pub async fn remove(role_id: web::Path) -> impl Responder { 47 | let role_id = role_id.into_inner(); 48 | let vo = CONTEXT.sys_role_service.remove(&role_id).await; 49 | return RespVO::from_result(&Ok(1)).resp_json(); 50 | } 51 | 52 | //已分配此角色的用户 53 | #[get("/role/authUser/allocatedList")] 54 | #[has_permit("system:role:query")] 55 | pub async fn auth_user_list(arg: web::Query) -> impl Responder { 56 | let vo = CONTEXT.sys_role_service.auth_user_list_page(&arg.0).await.unwrap(); 57 | let mut res = RespJson::success(); 58 | res.insert("rows".to_string(), serde_json::json!(vo)); 59 | res.resp_json() 60 | } 61 | 62 | //未分配此角色的用户 63 | #[get("/role/authUser/unallocatedList")] 64 | #[has_permit("system:role:query")] 65 | pub async fn unallocated_user_list(arg: web::Query) -> impl Responder { 66 | let vo = CONTEXT.sys_role_service.unallocated_user_list_page(&arg.0).await.unwrap(); 67 | let mut res = RespJson::success(); 68 | res.insert("rows".to_string(), serde_json::json!(vo)); 69 | res.resp_json() 70 | } 71 | 72 | //取消对某个用户授权 73 | #[put("/role/authUser/cancel")] 74 | #[has_permit("system:role:query")] 75 | pub async fn cancel_user(arg: web::Json) -> impl Responder { 76 | let row = CONTEXT.sys_user_role_service.remove(&SysUserRole::from(arg.0)).await.unwrap(); 77 | RespVO::::judge(row, "取消授权成功。".to_string(), "取消授权失败!".to_string()).resp_json() 78 | } 79 | 80 | //对多个用户进行授权 81 | #[put("/role/authUser/selectAll")] 82 | #[has_permit("system:role:query")] 83 | pub async fn auth_user_all(arg: web::Query) -> impl Responder { 84 | let user_ids: Vec = arg.user_ids.split(",").map(|u| u.to_string()).collect(); 85 | let res = CONTEXT.sys_user_role_service.add_users_role(&arg.0.role_id, &user_ids).await; 86 | RespVO::::judge(res.unwrap(), "批量授权成功。".to_string(), "批量授权失败!".to_string()).resp_json() 87 | } 88 | //对多个用户进行授权 89 | #[put("/role/authUser/cancelAll")] 90 | #[has_permit("system:role:query")] 91 | pub async fn cancel_user_all(arg: web::Query) -> impl Responder { 92 | let user_ids: Vec = arg.user_ids.split(",").map(|u| u.to_string()).collect(); 93 | let res = CONTEXT.sys_user_role_service.remove_users_role(&arg.0.role_id, &user_ids).await; 94 | RespVO::::judge(res.unwrap(), "批量取消授权成功。".to_string(), "批量取消授权失败!".to_string()).resp_json() 95 | } 96 | 97 | #[put("/role/changeStatus")] 98 | #[has_permit("system:role:edit")] 99 | pub async fn change_status(arg: web::Json) -> impl Responder { 100 | 101 | // roleService.checkRoleAllowed(role); todo 102 | // roleService.checkRoleDataScope(role.getRoleId()); todo 103 | // role.setUpdateBy(getUsername()); 104 | let mut data = SysRole::from(arg.0); 105 | data.update_by = Some(crate::web_data::get_user_name()); 106 | let res = CONTEXT.sys_role_service.update(data, vec![]).await; 107 | return RespVO::from_result(&res).resp_json(); 108 | } 109 | -------------------------------------------------------------------------------- /src/controller/sys_user_controller.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{get, HttpRequest, post, put, Responder, web}; 2 | use permit_lib::has_permit; 3 | 4 | use crate::config::global_variables::{ADMIN_NAME}; 5 | use crate::domain::dto::{ UserAddDTO, UserPageDTO, UserRoleAuthQueryDTO, UserUpdateDTO}; 6 | use crate::domain::table::SysUser; 7 | use crate::domain::vo::{ PageVO, RespJson, RespVO}; 8 | use crate::service::CONTEXT; 9 | use crate::web_data::get_user_name; 10 | 11 | #[get("/user/list")] 12 | #[has_permit("system:user:query")] 13 | pub async fn page(arg: web::Query) -> impl Responder { 14 | let vo = CONTEXT.sys_user_service.page(&arg.0).await; 15 | return PageVO::from_result(&vo).resp_json(); 16 | } 17 | 18 | //前端打开用户添加时,查询岗位和角色列表 19 | #[get("/user/")] 20 | #[has_permit("system:user:query")] 21 | pub async fn before_add(arg: web::Query) -> impl Responder { 22 | let mut res = RespJson::success_info("操作成功"); 23 | res.insert("roles".to_string(), serde_json::json!(CONTEXT.sys_role_service.finds_all().await.unwrap()));//fixme 24 | 25 | res.insert("posts".to_string(), serde_json::json!(CONTEXT.sys_post_service.finds_all().await.unwrap()));//fixme 26 | res.resp_json() 27 | } 28 | 29 | #[post("/user")] 30 | #[has_permit("system:user:add")] 31 | pub async fn add(arg: web::Json) -> impl Responder { 32 | let role_ids = arg.role_ids.clone().unwrap(); 33 | let mut data = SysUser::from(arg.0); 34 | let user_id = data.user_id.clone().unwrap(); 35 | data.create_by = Some(get_user_name()); 36 | let rows_affected = CONTEXT.sys_user_service.add(data).await.unwrap(); 37 | if rows_affected > 0 { 38 | if role_ids.len() > 0 { 39 | CONTEXT.sys_user_role_service.add_user_roles(&user_id, &role_ids); 40 | } 41 | //todo 增加post 42 | } 43 | 44 | return RespVO::from_result(&Ok(rows_affected)).resp_json(); 45 | } 46 | 47 | 48 | //用户编辑,需要查询post和role列表 49 | #[get("/user/{user_id}")] 50 | #[has_permit("system:user:query")] 51 | pub async fn detail(user_id: web::Path) -> impl Responder { 52 | let user_id = user_id.into_inner(); 53 | let user = CONTEXT.sys_user_service.detail(&user_id).await.unwrap(); 54 | 55 | let mut res = RespJson::success_info("操作成功"); 56 | 57 | let role_ids: Vec = CONTEXT.sys_role_service.finds_roles_by_user_id(&user_id).await.unwrap() 58 | .into_iter().map(|r| r.role_id.unwrap()).collect(); 59 | 60 | res.insert("data".to_string(), serde_json::json!(user)); 61 | 62 | res.insert("posts".to_string(), serde_json::json!(CONTEXT.sys_post_service.finds_all().await.unwrap())); 63 | res.insert("roleIds".to_string(), serde_json::json!(role_ids)); 64 | res.insert("roles".to_string(), serde_json::json!(CONTEXT.sys_role_service.finds_all().await.unwrap())); 65 | 66 | return res.resp_json(); 67 | } 68 | 69 | #[put("/user")] 70 | #[has_permit("system:user:edit")] 71 | pub async fn update(arg: web::Json) -> impl Responder { 72 | let vo = CONTEXT.sys_user_service.update(arg.0).await; 73 | return RespVO::from_result(&vo).resp_json(); 74 | } 75 | 76 | #[get("/user/{user_id}")] 77 | #[has_permit("system:user:remove")] 78 | pub async fn remove(user_id: web::Path) -> impl Responder { 79 | let user_id = user_id.into_inner(); 80 | let vo = CONTEXT 81 | .sys_user_service 82 | .remove(&user_id) 83 | .await; 84 | return RespVO::from_result(&vo).resp_json(); 85 | } 86 | 87 | 88 | #[get("/user/deptTree")] 89 | #[has_permit("system:user:query")] 90 | pub async fn get_dept_tree() -> impl Responder { 91 | let dept_tree = CONTEXT.sys_dept_service.get_dept_tree(&get_user_name()).await; 92 | return RespVO::from_result(&dept_tree).resp_json(); 93 | } 94 | 95 | #[put("/user/authRole")] 96 | #[has_permit("system:user:query")] 97 | pub async fn set_auth_roles(arg: web::Query) -> impl Responder { 98 | let s = arg.role_ids.clone().unwrap_or_default(); 99 | let role_ids: Vec = s.split(",").map(|s| s.to_string()).collect(); 100 | CONTEXT.sys_user_role_service.reset_through_user_id(&arg.user_id.clone().unwrap_or_default(), &role_ids).await; 101 | RespVO::::from_success_info("更新成功!").resp_json() 102 | } 103 | 104 | #[get("/user/authRole/{user_id}")] 105 | #[has_permit("system:user:query")] 106 | pub async fn get_auth_roles(user_id: web::Path) -> impl Responder { 107 | let user_id = user_id.into_inner(); 108 | let mut user = CONTEXT.sys_user_service.detail(&user_id).await.unwrap(); 109 | user.roles = Some(CONTEXT.sys_role_service.finds_roles_by_user_id(&user_id).await.unwrap()); 110 | let roles = CONTEXT.sys_role_service.finds_roles_by_user_id(&user_id).await.unwrap(); 111 | let filter_roles = match get_user_name().eq(ADMIN_NAME) { 112 | true => { 113 | roles 114 | } 115 | false => { 116 | roles.into_iter().filter(|r| r.admin).collect::>() 117 | } 118 | }; 119 | 120 | let mut res = RespJson::success_info("操作成功"); 121 | res.insert("user".to_string(), serde_json::json!(user)); 122 | res.insert("roles".to_string(), serde_json::json!(filter_roles)); 123 | return res.resp_json(); 124 | } 125 | 126 | 127 | // AjaxResult ajax = AjaxResult.success(); 128 | // SysUser user = userService.selectUserById(userId); 129 | // List roles = roleService.selectRolesByUserId(userId); 130 | // ajax.put("user", user); 131 | // ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); 132 | // return ajax; 133 | // } 134 | 135 | 136 | #[put("/user/resetPwd")] 137 | #[has_permit("system:user:resetPwd")] 138 | pub async fn reset_pwd(arg: web::Json) -> impl Responder { 139 | let user_id = arg.0.user_id.unwrap(); 140 | let user = CONTEXT.sys_user_service.find(&user_id).await.unwrap(); 141 | if user.is_none() { 142 | return RespVO::::from_error_info(500, "找不到此用户").resp_json(); 143 | } 144 | let user = user.unwrap(); 145 | if user.user_name.eq(&Some(ADMIN_NAME.to_string())) { 146 | return RespVO::::from_error_info(500, "不允许操作超级管理员用户").resp_json(); 147 | } 148 | //todo 查看datascope userService.checkUserDataScope(user.getUserId()); 149 | 150 | //user.password = Some(PasswordEncoder::encode(arg.password.as_ref().unwrap())); 151 | 152 | /// user.setUpdateBy(getUsername()); 153 | 154 | 155 | // CONTEXT.sys_user_service.update(&user_id).await.unwrap(); 156 | RespVO::::from_success_info("更新成功!").resp_json() 157 | } 158 | 159 | //更改用户当前状态 160 | #[put("/user/changeStatus")] 161 | #[has_permit("")] 162 | pub async fn change_status(req: HttpRequest, arg: web::Json) -> impl Responder { 163 | let user_id = arg.0.user_id.unwrap(); 164 | let user = CONTEXT.sys_user_service.find(&user_id).await.unwrap(); 165 | if user.is_none() { 166 | return RespVO::::from_error_info(500, "找不到此用户").resp_json(); 167 | } 168 | let user = user.unwrap(); 169 | if user.user_name.eq(&Some(ADMIN_NAME.to_string())) { 170 | return RespVO::::from_error_info(500, "不允许操作超级管理员用户").resp_json(); 171 | } 172 | let vo = CONTEXT.sys_user_service.update_status( 173 | arg.0.status.unwrap(), &user_id).await; 174 | return RespVO::from_result(&vo).resp_json(); 175 | } -------------------------------------------------------------------------------- /src/domain/dto/auth.rs: -------------------------------------------------------------------------------- 1 | /// auth dto 2 | #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] 3 | pub struct SysAuthDTO { 4 | pub access_token: String, 5 | pub path: String, 6 | } 7 | -------------------------------------------------------------------------------- /src/domain/dto/config.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::ObjectId; 2 | use crate::domain::table::SysConfig; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use rbatis::sql::PageRequest; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct ConfigPageDTO { 11 | #[serde(rename(deserialize = "pageNum"))] 12 | pub page_no: Option, 13 | pub page_size: Option, 14 | pub config_name: Option, 15 | pub config_key: Option, 16 | pub config_type: Option, 17 | pub status: Option, 18 | 19 | } 20 | 21 | impl From for PageRequest { 22 | fn from(arg: ConfigPageDTO) -> Self { 23 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 24 | } 25 | } 26 | 27 | impl From<&ConfigPageDTO> for PageRequest { 28 | fn from(arg: &ConfigPageDTO) -> Self { 29 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 30 | } 31 | } 32 | 33 | #[derive(Serialize, Deserialize, Clone, Debug)] 34 | #[serde(rename_all = "camelCase")] 35 | pub struct ConfigAddDTO { 36 | pub config_id: Option, 37 | pub config_name: Option, 38 | pub config_key: Option, 39 | pub config_value: Option, 40 | pub config_type: Option, 41 | pub remark: Option, 42 | } 43 | 44 | impl From for SysConfig { 45 | fn from(arg: ConfigAddDTO) -> Self { 46 | SysConfig { 47 | config_id: ObjectId::new().to_string().into(), 48 | config_name: arg.config_name, 49 | config_key: arg.config_key, 50 | config_value: arg.config_value, 51 | config_type: arg.config_type, 52 | create_by: None, 53 | create_time: DateTime::now().set_micro(0).into(), 54 | update_by: None, 55 | update_time: None, 56 | remark: arg.remark, 57 | } 58 | } 59 | } 60 | 61 | #[derive(Serialize, Deserialize, Clone, Debug)] 62 | #[serde(rename_all = "camelCase")] 63 | pub struct ConfigUpdateDTO { 64 | pub config_id: Option, 65 | pub config_name: Option, 66 | pub config_key: Option, 67 | pub config_value: Option, 68 | pub config_type: Option, 69 | pub remark: Option, 70 | } 71 | 72 | impl From for SysConfig { 73 | fn from(arg: ConfigUpdateDTO) -> Self { 74 | SysConfig { 75 | config_id: arg.config_id, 76 | config_name: arg.config_name, 77 | config_key: arg.config_key, 78 | config_value: arg.config_value, 79 | config_type: arg.config_type, 80 | create_by: None, 81 | create_time: None, 82 | update_by: None, 83 | update_time: DateTime::now().set_micro(0).into(), 84 | remark: arg.remark, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/domain/dto/dept.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::ObjectId; 2 | use crate::domain::table::SysDept; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use serde::{Deserialize, Serialize}; 5 | use crate::config::global_variables::{DEL_FLAG_NORMAL, STATUS_NORMAL}; 6 | 7 | /// dept query DTO 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct DeptQueryDTO { 11 | pub dept_name: Option, 12 | pub status: Option, 13 | } 14 | 15 | 16 | #[derive(Serialize, Deserialize, Clone, Debug)] 17 | #[serde(rename_all = "camelCase")] 18 | pub struct DeptAddDTO { 19 | pub dept_id: Option, 20 | pub parent_id: Option, 21 | pub ancestors: Option, 22 | pub dept_name: Option, 23 | pub order_num: Option, 24 | pub leader: Option, 25 | pub phone: Option, 26 | pub email: Option, 27 | } 28 | 29 | impl From for SysDept { 30 | fn from(arg: DeptAddDTO) -> Self { 31 | SysDept { 32 | dept_id: ObjectId::new().to_string().into(), 33 | parent_id: arg.parent_id, 34 | ancestors: arg.ancestors, 35 | dept_name: arg.dept_name, 36 | order_num: arg.order_num, 37 | leader: arg.leader, 38 | phone: arg.phone, 39 | email: arg.email, 40 | status: Some(STATUS_NORMAL), 41 | del_flag: Some(DEL_FLAG_NORMAL), 42 | create_by: None, 43 | create_time: DateTime::now().set_micro(0).into(), 44 | update_by: None, 45 | update_time: None, 46 | 47 | } 48 | } 49 | } 50 | 51 | #[derive(Serialize, Deserialize, Clone, Debug)] 52 | #[serde(rename_all = "camelCase")] 53 | pub struct DeptUpdateDTO { 54 | pub dept_id: Option, 55 | pub parent_id: Option, 56 | pub ancestors: Option, 57 | pub dept_name: Option, 58 | pub order_num: Option, 59 | pub leader: Option, 60 | pub phone: Option, 61 | pub email: Option, 62 | pub status: Option, 63 | pub del_flag: Option, 64 | } 65 | 66 | impl From for SysDept { 67 | fn from(arg: DeptUpdateDTO) -> Self { 68 | SysDept { 69 | dept_id:arg.dept_id, 70 | parent_id: arg.parent_id, 71 | ancestors: arg.ancestors, 72 | dept_name: arg.dept_name, 73 | order_num: arg.order_num, 74 | leader: arg.leader, 75 | phone: arg.phone, 76 | email: arg.email, 77 | status: arg.status, 78 | del_flag: arg.del_flag, 79 | create_by: None, 80 | create_time: None, 81 | update_by: None, 82 | update_time: DateTime::now().set_micro(0).into(), 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/domain/dto/dict_data.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::{ObjectId}; 2 | use crate::domain::table::SysDictData; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use rbatis::sql::PageRequest; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct DictDataPageDTO { 11 | #[serde(rename(deserialize = "pageNum"))] 12 | pub page_no: Option, 13 | pub page_size: Option, 14 | pub dict_type: Option, 15 | pub dict_label: Option, 16 | pub status: Option, 17 | } 18 | 19 | impl From for PageRequest { 20 | fn from(arg: DictDataPageDTO) -> Self { 21 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 22 | } 23 | } 24 | 25 | impl From<&DictDataPageDTO> for PageRequest { 26 | fn from(arg: &DictDataPageDTO) -> Self { 27 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 28 | } 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Clone, Debug)] 32 | #[serde(rename_all = "camelCase")] 33 | pub struct DictDataAddDTO { 34 | pub dict_sort: Option, 35 | pub dict_label: Option, 36 | pub dict_value: Option, 37 | pub dict_type: Option, 38 | pub css_class: Option, 39 | pub list_class: Option, 40 | pub is_default: Option, 41 | pub status: Option, 42 | pub create_by: Option, 43 | pub create_time: Option, 44 | pub remark: Option, 45 | } 46 | impl From for SysDictData { 47 | fn from(arg: DictDataAddDTO) -> Self { 48 | SysDictData { 49 | dict_code:ObjectId::new().to_string().into(), 50 | dict_sort: arg.dict_sort, 51 | dict_label: arg.dict_label, 52 | dict_value: arg.dict_value, 53 | dict_type: arg.dict_type, 54 | css_class: arg.css_class, 55 | list_class: arg.list_class, 56 | is_default: arg.is_default, 57 | status: arg.status, 58 | create_by: arg.create_by, 59 | create_time: arg.create_time, 60 | update_by: None, 61 | update_time: None, 62 | remark: arg.remark, 63 | } 64 | } 65 | } 66 | 67 | #[derive(Serialize, Deserialize, Clone, Debug)] 68 | #[serde(rename_all = "camelCase")] 69 | pub struct DictDataUpdateDTO { 70 | pub dict_code: Option, 71 | pub dict_sort: Option, 72 | pub dict_label: Option, 73 | pub dict_value: Option, 74 | pub dict_type: Option, 75 | pub css_class: Option, 76 | pub list_class: Option, 77 | pub is_default: Option, 78 | pub status: Option, 79 | pub update_by: Option, 80 | pub update_time: Option, 81 | pub remark: Option, 82 | } 83 | 84 | impl From for SysDictData { 85 | fn from(arg: DictDataUpdateDTO) -> Self { 86 | SysDictData { 87 | dict_code: arg.dict_code, 88 | dict_sort: arg.dict_sort, 89 | dict_label: arg.dict_label, 90 | dict_value: arg.dict_value, 91 | dict_type: arg.dict_type, 92 | css_class: arg.css_class, 93 | list_class: arg.list_class, 94 | is_default: arg.is_default, 95 | status: arg.status, 96 | create_by: None, 97 | create_time: None, 98 | update_by: arg.update_by, 99 | update_time: arg.update_time, 100 | remark: arg.remark, 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/domain/dto/dict_type.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::ObjectId; 2 | use crate::domain::table::SysDictType; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use rbatis::sql::PageRequest; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct DictTypePageDTO { 11 | #[serde(rename(deserialize = "pageNum"))] 12 | pub page_no: Option, 13 | pub page_size: Option, 14 | pub dict_name: Option, 15 | pub dict_type: Option, 16 | pub status: Option, 17 | } 18 | 19 | impl From for PageRequest { 20 | fn from(arg: DictTypePageDTO) -> Self { 21 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 22 | } 23 | } 24 | 25 | impl From<&DictTypePageDTO> for PageRequest { 26 | fn from(arg: &DictTypePageDTO) -> Self { 27 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 28 | } 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Clone, Debug)] 32 | #[serde(rename_all = "camelCase")] 33 | pub struct DictTypeAddDTO { 34 | pub dict_name: Option, 35 | pub dict_type: Option, 36 | pub status: Option, 37 | pub remark: Option 38 | } 39 | 40 | impl From for SysDictType { 41 | fn from(arg: DictTypeAddDTO) -> Self { 42 | SysDictType { 43 | dict_id: ObjectId::new().to_string().into(), 44 | dict_name: arg.dict_name, 45 | dict_type: arg.dict_type, 46 | status: arg.status, 47 | create_by: Some(crate::web_data::get_user_name()), 48 | create_time: DateTime::now().set_micro(0).into(), 49 | update_by: None, 50 | update_time: None, 51 | remark: arg.remark 52 | } 53 | } 54 | } 55 | 56 | #[derive(Serialize, Deserialize, Clone, Debug)] 57 | #[serde(rename_all = "camelCase")] 58 | pub struct DictTypeUpdateDTO { 59 | pub dict_id: Option, 60 | pub dict_name: Option, 61 | pub dict_type: Option, 62 | pub status: Option, 63 | pub remark: Option, 64 | } 65 | 66 | impl From for SysDictType { 67 | fn from(arg: DictTypeUpdateDTO) -> Self { 68 | SysDictType { 69 | dict_id: arg.dict_id, 70 | dict_name: arg.dict_name, 71 | dict_type: arg.dict_type, 72 | status: arg.status, 73 | create_by: None, 74 | create_time: None, 75 | update_by: None, 76 | update_time: DateTime::now().set_micro(0).into(), 77 | remark: arg.remark, 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/domain/dto/logininfor.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::PageRequest; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | 5 | #[derive(Serialize, Deserialize, Clone, Debug)] 6 | pub struct LogininforPageDTO { 7 | #[serde(rename(deserialize = "pageNum"))] 8 | pub page_no: Option, 9 | #[serde(rename(deserialize = "pageSize"))] 10 | pub page_size: Option, 11 | pub state: Option, 12 | } 13 | 14 | impl From for PageRequest { 15 | fn from(arg: LogininforPageDTO) -> Self { 16 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 17 | } 18 | } 19 | 20 | impl From<&LogininforPageDTO> for PageRequest { 21 | fn from(arg: &LogininforPageDTO) -> Self { 22 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/domain/dto/menu.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::SysMenu; 2 | use rbatis::rbdc::datetime::DateTime; 3 | use serde::{Deserialize, Serialize}; 4 | use validator::{Validate}; 5 | 6 | 7 | #[derive(Serialize, Deserialize, Clone, Debug)] 8 | #[serde(rename_all = "camelCase")] 9 | pub struct MenuPageDTO { 10 | pub menu_name: Option, 11 | pub status: Option 12 | } 13 | 14 | 15 | 16 | #[derive(Serialize, Deserialize, Validate, Clone, Debug)] 17 | #[serde(rename_all = "camelCase")] 18 | pub struct MenuAddDTO { 19 | #[validate(length(min = 2, max = 10, message = "菜单名称长度在2到10之间"))] 20 | #[validate(required(message = "菜单"))] 21 | pub menu_name: Option, 22 | //父id(可空) 23 | pub parent_id: Option, 24 | //顺序 25 | pub order_num: Option, 26 | //前端-菜单路径 27 | pub path: Option, 28 | //组件路径 29 | pub component: Option, 30 | //组件路径 31 | pub query: Option, 32 | //是否为外链 33 | pub is_frame: Option, 34 | //是否缓存 35 | pub is_cache: Option, 36 | //菜单类型 37 | pub menu_type: Option, 38 | //菜单可见 39 | pub visible: Option, 40 | //菜单状态 41 | pub status: Option, 42 | //权限标识 43 | pub perms: Option, 44 | //图标 45 | pub icon: Option, 46 | pub remark: Option, 47 | } 48 | 49 | impl From for SysMenu { 50 | fn from(arg: MenuAddDTO) -> Self { 51 | SysMenu { 52 | menu_id: None, 53 | menu_name: arg.menu_name, 54 | parent_id: arg.parent_id, 55 | order_num: arg.order_num, 56 | path: arg.path, 57 | component: arg.component, 58 | query: arg.query, 59 | is_frame: arg.is_frame, 60 | is_cache: arg.is_cache, 61 | menu_type: arg.menu_type, 62 | visible: arg.visible, 63 | status: arg.status, 64 | perms: arg.perms, 65 | icon: arg.icon, 66 | create_by: None, 67 | create_time: DateTime::now().set_micro(0).into(), 68 | 69 | update_by: None, 70 | update_time: None, 71 | remark: arg.remark, 72 | } 73 | } 74 | } 75 | 76 | #[derive(Serialize, Deserialize, Clone, Debug)] 77 | #[serde(rename_all = "camelCase")] 78 | pub struct MenuUpdateDTO { 79 | pub menu_id: Option, 80 | pub menu_name: Option, 81 | //父id(可空) 82 | pub parent_id: Option, 83 | //顺序 84 | pub order_num: Option, 85 | //前端-菜单路径 86 | pub path: Option, 87 | //组件路径 88 | pub component: Option, 89 | //组件路径 90 | pub query: Option, 91 | //是否为外链 92 | pub is_frame: Option, 93 | //是否缓存 94 | pub is_cache: Option, 95 | //菜单类型 96 | pub menu_type: Option, 97 | //菜单可见 98 | pub visible: Option, 99 | //菜单状态 100 | pub status: Option, 101 | //权限标识 102 | pub perms: Option, 103 | //图标 104 | pub icon: Option, 105 | pub update_by: Option, 106 | pub update_time: Option, 107 | pub remark: Option, 108 | } 109 | 110 | impl From for SysMenu { 111 | fn from(arg: MenuUpdateDTO) -> Self { 112 | SysMenu { 113 | menu_id: arg.menu_id, 114 | menu_name: arg.menu_name, 115 | parent_id: arg.parent_id, 116 | order_num: arg.order_num, 117 | path: arg.path, 118 | component: arg.component, 119 | query: arg.query, 120 | is_frame: arg.is_frame, 121 | is_cache: arg.is_cache, 122 | menu_type: arg.menu_type, 123 | visible: arg.visible, 124 | status: arg.status, 125 | perms: arg.perms, 126 | icon: arg.icon, 127 | create_by: None, 128 | create_time: None, 129 | update_by: None, 130 | update_time: DateTime::now().set_micro(0).into(), 131 | remark: None, 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/domain/dto/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dict_type; 2 | pub mod dict_data; 3 | pub mod menu; 4 | pub mod role; 5 | pub mod sign_in; 6 | pub mod user; 7 | pub mod config; 8 | pub mod role_menu; 9 | pub mod logininfor; 10 | pub mod dept; 11 | pub mod post; 12 | pub mod notice; 13 | 14 | pub use dict_type::*; 15 | pub use dict_data::*; 16 | pub use menu::*; 17 | pub use role::*; 18 | pub use sign_in::*; 19 | pub use user::*; 20 | pub use config::*; 21 | pub use dept::*; 22 | pub use post::*; 23 | pub use notice::*; 24 | 25 | pub use logininfor::*; 26 | 27 | 28 | use serde::{Deserialize, Serialize}; 29 | 30 | #[derive(Serialize, Deserialize, Clone, Debug)] 31 | pub struct EmptyDTO {} 32 | 33 | /// IdDTO 34 | #[derive(Serialize, Deserialize, Clone, Debug)] 35 | pub struct IdDTO { 36 | pub id: Option, 37 | } 38 | -------------------------------------------------------------------------------- /src/domain/dto/notice.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::ObjectId; 2 | use crate::domain::table::SysNotice; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use rbatis::sql::PageRequest; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct NoticePageDTO { 11 | #[serde(rename(deserialize = "pageNum"))] 12 | pub page_no: Option, 13 | pub page_size: Option, 14 | pub notice_title: Option, 15 | pub create_by: Option, 16 | pub notice_type: Option, 17 | } 18 | 19 | impl From for PageRequest { 20 | fn from(arg: NoticePageDTO) -> Self { 21 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 22 | } 23 | } 24 | 25 | impl From<&NoticePageDTO> for PageRequest { 26 | fn from(arg: &NoticePageDTO) -> Self { 27 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 28 | } 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Clone, Debug)] 32 | #[serde(rename_all = "camelCase")] 33 | pub struct NoticeAddDTO { 34 | pub notice_title: Option, 35 | pub notice_content: Option, 36 | pub notice_type: Option, 37 | pub status: Option, 38 | pub remark: Option, 39 | } 40 | 41 | impl From for SysNotice { 42 | fn from(arg: NoticeAddDTO) -> Self { 43 | SysNotice { 44 | notice_id: ObjectId::new().to_string().into(), 45 | notice_title: arg.notice_title, 46 | notice_content: arg.notice_content, 47 | notice_type: arg.notice_type, 48 | status: arg.status, 49 | create_by: None, 50 | create_time: DateTime::now().set_micro(0).into(), 51 | update_by: None, 52 | update_time: None, 53 | remark: arg.remark, 54 | } 55 | } 56 | } 57 | 58 | #[derive(Serialize, Deserialize, Clone, Debug)] 59 | #[serde(rename_all = "camelCase")] 60 | pub struct NoticeUpdateDTO { 61 | pub notice_id: Option, 62 | pub notice_title: Option, 63 | pub notice_content: Option, 64 | pub notice_type: Option, 65 | pub status: Option, 66 | pub remark: Option, 67 | } 68 | 69 | impl From for SysNotice { 70 | fn from(arg: NoticeUpdateDTO) -> Self { 71 | SysNotice { 72 | notice_id: arg.notice_id, 73 | notice_title: arg.notice_title, 74 | notice_content: arg.notice_content, 75 | notice_type: arg.notice_type, 76 | status: arg.status, 77 | create_by: None, 78 | create_time: None, 79 | update_by: None, 80 | update_time: DateTime::now().set_micro(0).into(), 81 | remark: arg.remark, 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/domain/dto/post.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::ObjectId; 2 | use crate::domain::table::SysPost; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use rbatis::sql::PageRequest; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /// page page DTO 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct PostPageDTO { 11 | #[serde(rename(deserialize = "pageNum"))] 12 | pub page_no: Option, 13 | #[serde(rename(deserialize = "pageSize"))] 14 | pub page_size: Option, 15 | pub post_name: Option, 16 | pub post_code: Option, 17 | pub status: Option, 18 | } 19 | 20 | impl From for PageRequest { 21 | fn from(arg: PostPageDTO) -> Self { 22 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 23 | } 24 | } 25 | 26 | impl From<&PostPageDTO> for PageRequest { 27 | fn from(arg: &PostPageDTO) -> Self { 28 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 29 | } 30 | } 31 | 32 | 33 | #[derive(Serialize, Deserialize, Clone, Debug)] 34 | #[serde(rename_all = "camelCase")] 35 | pub struct PostAddDTO { 36 | pub post_code: Option, 37 | pub post_name: Option, 38 | pub post_sort: Option, 39 | pub status: Option, 40 | pub remark: Option, 41 | } 42 | 43 | impl From for SysPost { 44 | fn from(arg: PostAddDTO) -> Self { 45 | SysPost { 46 | post_id: ObjectId::new().to_string().into(), 47 | post_code: arg.post_code, 48 | post_name: arg.post_name, 49 | post_sort: arg.post_sort, 50 | status: arg.status, 51 | create_by: None, 52 | create_time: DateTime::now().set_micro(0).into(), 53 | update_by: None, 54 | update_time: None, 55 | remark: arg.remark, 56 | } 57 | } 58 | } 59 | 60 | #[derive(Serialize, Deserialize, Clone, Debug)] 61 | #[serde(rename_all = "camelCase")] 62 | pub struct PostUpdateDTO { 63 | pub post_id: Option, 64 | pub post_code: Option, 65 | pub post_name: Option, 66 | pub post_sort: Option, 67 | pub status: Option, 68 | pub remark: Option, 69 | } 70 | 71 | impl From for SysPost { 72 | fn from(arg: PostUpdateDTO) -> Self { 73 | SysPost { 74 | post_id: arg.post_id, 75 | post_code: arg.post_code, 76 | post_name: arg.post_name, 77 | post_sort: arg.post_sort, 78 | status: arg.status, 79 | create_by: None, 80 | create_time: None, 81 | update_by: None, 82 | update_time: DateTime::now().set_micro(0).into(), 83 | remark: arg.remark, 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/domain/dto/role.rs: -------------------------------------------------------------------------------- 1 | use rbatis::object_id::ObjectId; 2 | use crate::domain::table::{SysRole}; 3 | use rbatis::rbdc::datetime::DateTime; 4 | use rbatis::sql::PageRequest; 5 | use serde::{Deserialize, Serialize}; 6 | use crate::config::global_variables::{DEL_FLAG_NORMAL}; 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct RolePageDTO { 11 | #[serde(rename(deserialize = "pageNum"))] 12 | pub page_no: Option, 13 | #[serde(rename(deserialize = "pageSize"))] 14 | pub page_size: Option, 15 | pub role_name: Option, 16 | pub role_key: Option, 17 | pub status: Option, 18 | pub create_begin_time: Option,//fixme 根据前端传来数据,还未找到方法。 19 | pub create_end_time: Option, 20 | } 21 | 22 | impl From<&RolePageDTO> for PageRequest { 23 | fn from(arg: &RolePageDTO) -> Self { 24 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 25 | } 26 | } 27 | 28 | #[derive(Serialize, Deserialize, Clone, Debug)] 29 | #[serde(rename_all = "camelCase")] 30 | pub struct RoleAddDTO { 31 | pub role_name: Option, 32 | pub role_key: Option, 33 | pub role_sort: Option, 34 | pub data_scope: Option, 35 | pub menu_check_strictly: Option, 36 | pub dept_check_strictly: Option, 37 | pub menu_ids: Option>, 38 | pub status: Option, 39 | pub remark: Option, 40 | } 41 | 42 | 43 | 44 | 45 | impl From for SysRole { 46 | fn from(arg: RoleAddDTO) -> Self { 47 | SysRole { 48 | role_id: ObjectId::new().to_string().into(), 49 | role_name: arg.role_name, 50 | role_key: arg.role_key, 51 | role_sort: arg.role_sort, 52 | data_scope: arg.data_scope, 53 | menu_check_strictly: match arg.menu_check_strictly.unwrap_or(true) { 54 | true => {Some('1')} 55 | false => {Some('0')} 56 | }, 57 | dept_check_strictly: match arg.dept_check_strictly.unwrap_or(true) { 58 | true => {Some('1')} 59 | false => {Some('0')} 60 | }, 61 | status: arg.status, 62 | del_flag: Some(DEL_FLAG_NORMAL), 63 | create_by: None, 64 | create_time: DateTime::now().set_micro(0).into(), 65 | update_by: None, 66 | update_time: None, 67 | remark: arg.remark, 68 | } 69 | } 70 | } 71 | 72 | 73 | #[derive(Serialize, Deserialize, Clone, Debug)] 74 | #[serde(rename_all = "camelCase")] 75 | pub struct RoleUpdateDTO { 76 | pub role_id: Option, 77 | pub role_name: Option, 78 | pub role_key: Option, 79 | pub role_sort: Option, 80 | pub data_scope: Option, 81 | pub menu_check_strictly: Option, 82 | pub dept_check_strictly: Option, 83 | pub menu_ids: Option>, 84 | pub status: Option, 85 | pub remark: Option, 86 | } 87 | 88 | 89 | impl From for SysRole { 90 | fn from(arg: RoleUpdateDTO) -> Self { 91 | SysRole { 92 | role_id: arg.role_id, 93 | role_name: arg.role_name, 94 | role_key: arg.role_key, 95 | role_sort: arg.role_sort, 96 | data_scope: arg.data_scope, 97 | menu_check_strictly: match arg.menu_check_strictly.unwrap_or(true) { 98 | true => {Some('1')} 99 | false => {Some('0')} 100 | }, 101 | dept_check_strictly: match arg.dept_check_strictly.unwrap_or(true) { 102 | true => {Some('1')} 103 | false => {Some('0')} 104 | }, 105 | status: arg.status, 106 | del_flag: None, 107 | create_by: None, 108 | create_time: None, 109 | update_by: None, 110 | update_time: DateTime::now().set_micro(0).into(), 111 | remark: arg.remark, 112 | } 113 | } 114 | } 115 | 116 | // 117 | 118 | #[derive(Serialize, Deserialize, Clone, Debug)] 119 | #[serde(rename_all = "camelCase")] 120 | pub struct RoleAuthUserPageDTO { 121 | #[serde(rename(deserialize = "pageNum"))] 122 | pub page_no: Option, 123 | #[serde(rename(deserialize = "pageSize"))] 124 | pub page_size: Option, 125 | pub role_id: Option, 126 | pub user_name: Option, 127 | pub phonenumber: Option 128 | } 129 | 130 | impl From<&RoleAuthUserPageDTO> for PageRequest { 131 | fn from(arg: &RoleAuthUserPageDTO) -> Self { 132 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/domain/dto/role_menu.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::PageRequest; 2 | use serde::{Deserialize, Serialize}; 3 | use crate::domain::dto::{RoleAddDTO, RoleUpdateDTO}; 4 | 5 | #[derive(Serialize, Deserialize, Clone, Debug)] 6 | pub struct RoleMenuAddDTO { 7 | pub name: Option, 8 | //父id(可空) 9 | pub parent_id: Option, 10 | 11 | } 12 | 13 | impl From for RoleAddDTO { 14 | fn from(arg: RoleMenuAddDTO) -> Self { 15 | Self { 16 | role_name: arg.name, 17 | role_key: None, 18 | role_sort: None, 19 | data_scope: None, 20 | menu_check_strictly: None, 21 | dept_check_strictly: None, 22 | menu_ids: None, 23 | status: None, 24 | remark: None, 25 | } 26 | } 27 | } 28 | 29 | #[derive(Serialize, Deserialize, Clone, Debug)] 30 | pub struct SysRoleMenuUpdateDTO { 31 | pub id: Option, 32 | pub name: Option, 33 | pub parent_id: Option, 34 | //菜单id集合 35 | pub menu_ids: Vec, 36 | } 37 | 38 | impl From for RoleUpdateDTO { 39 | fn from(arg: SysRoleMenuUpdateDTO) -> Self { 40 | Self { 41 | role_id: arg.id, 42 | role_name: arg.name, 43 | role_key: None, 44 | role_sort: None, 45 | data_scope: None, 46 | menu_check_strictly: None, 47 | dept_check_strictly: None, 48 | menu_ids: None, 49 | status: None, 50 | remark: None, 51 | } 52 | } 53 | } 54 | 55 | #[derive(Serialize, Deserialize, Clone, Debug)] 56 | pub struct SysRoleMenuPageDTO { 57 | #[serde(rename(deserialize = "pageNum"))] 58 | pub page_no: Option, 59 | #[serde(rename(deserialize = "pageSize"))] 60 | pub page_size: Option, 61 | pub name: Option, 62 | } 63 | 64 | impl From<&SysRoleMenuPageDTO> for PageRequest { 65 | fn from(arg: &SysRoleMenuPageDTO) -> Self { 66 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/domain/dto/sign_in.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Clone, Debug)] 4 | pub struct SignInDTO { 5 | pub username: String, 6 | pub password: String, 7 | //验证码,可用是短信验证码,图片验证码,二维码验证码... 8 | pub code: Option, 9 | pub uuid: Option, 10 | } 11 | -------------------------------------------------------------------------------- /src/domain/dto/user.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::{SysUser, SysUserRole}; 2 | use crate::util::password_encoder::PasswordEncoder; 3 | use rbatis::object_id::ObjectId; 4 | use rbatis::rbdc::datetime::DateTime; 5 | use rbatis::sql::PageRequest; 6 | use serde::{Deserialize, Serialize}; 7 | use validator::Validate; 8 | use crate::config::global_variables::{DEL_FLAG_NORMAL, STATUS_NORMAL}; 9 | 10 | #[derive(Serialize, Deserialize, Validate, Clone, Debug)] 11 | #[serde(rename_all = "camelCase")] 12 | pub struct UserAddDTO { 13 | pub user_id: Option, 14 | pub dept_id: Option, 15 | pub user_name: Option, 16 | pub nick_name: Option, 17 | pub email: Option, 18 | pub phonenumber: Option, 19 | pub sex: Option, 20 | pub password: Option, 21 | pub remark: Option, 22 | pub role_ids: Option>, 23 | pub post_ids: Option>, 24 | } 25 | 26 | impl From for SysUser { 27 | fn from(arg: UserAddDTO) -> Self { 28 | SysUser { 29 | user_id: ObjectId::new().to_string().into(), 30 | dept_id: arg.dept_id, 31 | user_name: arg.user_name.clone(), 32 | nick_name: arg.nick_name, 33 | email: arg.email, 34 | phonenumber: arg.phonenumber, 35 | sex: arg.sex, 36 | avatar: None, 37 | password: PasswordEncoder::encode(&arg.password.unwrap_or_default()).into(), 38 | status: STATUS_NORMAL.into(), 39 | del_flag: DEL_FLAG_NORMAL.into(), 40 | login_ip: None, 41 | login_date: None, 42 | create_by: None, 43 | create_time: DateTime::now().set_micro(0).into(), 44 | update_by: None, 45 | update_time: None, 46 | remark: arg.remark, 47 | } 48 | } 49 | } 50 | 51 | #[derive(Serialize, Deserialize, Clone, Debug)] 52 | #[serde(rename_all = "camelCase")] 53 | pub struct UserUpdateDTO { 54 | pub user_id: Option, 55 | pub dept_id: Option, 56 | pub user_name: Option, 57 | pub nick_name: Option, 58 | pub email: Option, 59 | pub phonenumber: Option, 60 | pub sex: Option, 61 | pub status: Option, 62 | pub remark: Option, 63 | pub role_ids: Option>, 64 | pub post_ids: Option>, 65 | } 66 | 67 | impl From for SysUser { 68 | fn from(arg: UserUpdateDTO) -> Self { 69 | SysUser { 70 | user_id: arg.user_id, 71 | dept_id: arg.dept_id, 72 | user_name: arg.user_name, 73 | nick_name: arg.nick_name, 74 | email: arg.email, 75 | phonenumber: arg.phonenumber, 76 | sex: arg.sex, 77 | avatar: None, 78 | password: None, 79 | status: arg.status, 80 | del_flag: None, 81 | login_ip: None, 82 | login_date: None, 83 | create_by: None, 84 | create_time: None, 85 | update_by: None, 86 | update_time: DateTime::now().set_micro(0).into(), 87 | remark: arg.remark, 88 | } 89 | } 90 | } 91 | 92 | #[derive(Serialize, Deserialize, Clone, Debug)] 93 | #[serde(rename_all = "camelCase")] 94 | pub struct UserPageDTO { 95 | #[serde(rename(deserialize = "pageNum"))] 96 | pub page_no: Option, 97 | #[serde(rename(deserialize = "pageSize"))] 98 | pub page_size: Option, 99 | pub user_name: Option, 100 | pub phonenumber: Option, 101 | pub status: Option, 102 | pub dept_id: Option, 103 | } 104 | 105 | impl From<&UserPageDTO> for PageRequest { 106 | fn from(arg: &UserPageDTO) -> Self { 107 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 108 | } 109 | } 110 | 111 | impl From<&UserRolePageDTO> for UserPageDTO { 112 | fn from(arg: &UserRolePageDTO) -> Self { 113 | Self { 114 | page_no: arg.page_no.clone(), 115 | page_size: arg.page_size.clone(), 116 | user_name: arg.user_name.clone(), 117 | phonenumber: None, 118 | status: None, 119 | dept_id: None, 120 | } 121 | } 122 | } 123 | 124 | #[derive(Serialize, Deserialize, Clone, Debug)] 125 | #[serde(rename_all = "camelCase")] 126 | pub struct UserRoleDTO { 127 | pub user_id: Option, 128 | pub role_id: Option, 129 | } 130 | 131 | impl From for SysUserRole { 132 | fn from(arg: UserRoleDTO) -> Self { 133 | SysUserRole { 134 | user_id: arg.user_id, 135 | role_id: arg.role_id, 136 | } 137 | } 138 | } 139 | 140 | #[derive(Serialize, Deserialize, Clone, Debug)] 141 | #[serde(rename_all = "camelCase")] 142 | pub struct UsersRoleDTO { 143 | pub user_ids: String, 144 | pub role_id: String, 145 | } 146 | 147 | 148 | #[derive(Serialize, Deserialize, Clone, Debug)] 149 | pub struct UserRolePageDTO { 150 | #[serde(rename(deserialize = "pageNum"))] 151 | pub page_no: Option, 152 | #[serde(rename(deserialize = "pageSize"))] 153 | pub page_size: Option, 154 | pub user_name: Option, 155 | pub name: Option, 156 | 157 | } 158 | 159 | impl From<&UserRolePageDTO> for PageRequest { 160 | fn from(arg: &UserRolePageDTO) -> Self { 161 | PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)) 162 | } 163 | } 164 | 165 | #[derive(Serialize, Deserialize, Clone, Debug)] 166 | #[serde(rename_all = "camelCase")] 167 | pub struct UserRoleAuthQueryDTO { 168 | pub user_id: Option, 169 | pub role_ids: Option, 170 | } 171 | 172 | 173 | #[derive(Serialize, Deserialize, Clone, Debug)] 174 | #[serde(rename_all = "camelCase")] 175 | pub struct PasswordUpdateDTO { 176 | pub old_password: Option, 177 | pub new_password: Option 178 | } 179 | -------------------------------------------------------------------------------- /src/domain/mapper/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sys_config; 2 | pub mod sys_dict_type; 3 | pub mod sys_dict_data; 4 | pub mod sys_menu; 5 | pub mod sys_role; 6 | pub mod sys_role_menu; 7 | pub mod sys_trash; 8 | pub mod sys_user; 9 | pub mod sys_user_role; 10 | pub mod sys_logininfor; 11 | pub mod sys_dept; 12 | pub mod sys_post; 13 | pub mod sys_notice; 14 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_config.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysConfig {}); 3 | 4 | 5 | impl_select_page!(SysConfig{select_page(dto: &crate::domain::dto::ConfigPageDTO) => 6 | "`where 1=1` 7 | if dto.configName != '': 8 | ` and config_name like #{'%'+dto.configName+'%'}` 9 | if dto.configKey != '': 10 | ` and config_key like #{'%'+dto.configKey+'%'}` 11 | if dto.configType != '': 12 | ` and config_type = #{dto.configType}` 13 | if dto.status != '': 14 | ` and status = #{dto.status}` 15 | if !sql.contains('count'): 16 | ` order by create_time`"}); 17 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_dept.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysDept {}); 3 | 4 | 5 | impl_select!(SysDept{select_all_query(dto: &crate::domain::dto::DeptQueryDTO) => 6 | "`where parent_id IS NOT NULL` 7 | if dto.deptName != '': 8 | ` and dept_name like #{'%'+dto.deptName+'%'}` 9 | if dto.status != '': 10 | ` and status = #{dto.status}` 11 | if !sql.contains('count'): 12 | ` order by order_num`"}); 13 | 14 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_dict_data.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysDictData {}); 3 | 4 | 5 | impl_select_page!(SysDictData{select_page(dto: &crate::domain::dto::DictDataPageDTO) => 6 | "`where 1=1 ` 7 | if dto.dictType != '': 8 | ` and dict_type = #{dto.dictType}` 9 | if dto.dictLabel != '': 10 | ` and dict_label like #{'%'+dto.dictLabel+'%'}` 11 | if dto.status != '': 12 | ` and status = #{dto.status}` 13 | if !sql.contains('count'): 14 | ` order by dict_sort`"}); 15 | impl_select!(SysDictData{select_by_dict_type(dict_type:&String)=>"`where dict_type =#{dict_type} order by dict_sort`"}); 16 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_dict_type.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysDictType {}); 3 | 4 | impl_select_page!(SysDictType{select_page(dto: &crate::domain::dto::DictTypePageDTO) => 5 | "`where 1=1 ` 6 | if dto.dictType != '': 7 | ` and dict_type like #{'%'+dto.dictType+'%'}` 8 | if dto.dictName != '': 9 | ` and dict_name like #{'%'+dto.dictName+'%'}` 10 | if dto.status != '': 11 | ` and status = #{dto.status}` 12 | if !sql.contains('count'): 13 | ` order by create_time`"}); 14 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_logininfor.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysLogininfor {}); 3 | 4 | 5 | impl_select_page!(SysLogininfor{select_page(dto: &crate::domain::dto::LogininforPageDTO) => 6 | "`` 7 | if !sql.contains('count'): 8 | ` order by login_time desc`"}); 9 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_menu.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | //SysMenu 3 | crud!(SysMenu {});//如何去掉第一个AND 4 | impl_select!(SysMenu{query_menu(dto: &crate::domain::dto::MenuPageDTO) => 5 | "`where 1=1` 6 | if dto.menuName != '': 7 | ` and menu_name like #{'%'+dto.menuName+'%'}` 8 | if dto.status != '': 9 | ` and status = #{dto.status}` 10 | if !sql.contains('count'): 11 | ` order by order_num`"}); 12 | impl_select!(SysMenu{select_all_order_num() => 13 | "` order by order_num`"}); -------------------------------------------------------------------------------- /src/domain/mapper/sys_notice.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysNotice {}); 3 | 4 | 5 | impl_select_page!(SysNotice{select_page(dto: &crate::domain::dto::NoticePageDTO) => 6 | "`where 1=1` 7 | if dto.noticeTitle != '': 8 | ` and notice_title like #{'%'+dto.noticeTitle+'%'}` 9 | if dto.createBy != '': 10 | ` and create_by = #{dto.createBy}` 11 | if dto.noticeType != '': 12 | ` and notice_type = #{dto.noticeType}` 13 | if !sql.contains('count'): 14 | ` order by notice_title`"}); 15 | 16 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_post.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysPost {}); 3 | 4 | 5 | impl_select_page!(SysPost{select_page(dto: &crate::domain::dto::PostPageDTO) => 6 | "`where 1=1 ` 7 | if dto.postName != '': 8 | ` and post_name like #{'%'+dto.postName+'%'}` 9 | if dto.postCode != '': 10 | ` and post_code like #{'%'+dto.postCode+'%'}` 11 | if dto.status != '': 12 | ` and status = #{dto.status}` 13 | if !sql.contains('count'): 14 | ` order by post_sort`"}); -------------------------------------------------------------------------------- /src/domain/mapper/sys_role.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::RolePageDTO; 2 | use crate::domain::table::*; 3 | crud!(SysRole {}); 4 | impl_select_page!(SysRole{select_page(dto:&RolePageDTO)=> 5 | "`where del_flag = '0'` 6 | if dto.roleName != '': 7 | ` and role_name like #{'%'+dto.roleName+'%'}` 8 | if dto.roleKey != '': 9 | ` and role_key like #{'%'+dto.roleKey+'%'}` 10 | if dto.status != '': 11 | ` and status = #{dto.status}` 12 | if !sql.contains('count'): 13 | ` order by role_sort`"}); 14 | 15 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_role_menu.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysRoleMenu {}); 3 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_trash.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysTrash {}); 3 | -------------------------------------------------------------------------------- /src/domain/mapper/sys_user.rs: -------------------------------------------------------------------------------- 1 | use rbatis::executor::Executor; 2 | use crate::domain::dto::{RoleAuthUserPageDTO, UserPageDTO}; 3 | use crate::domain::table::*; 4 | use crate::rbatis::rbdc::Error; 5 | crud!(SysUser {}); 6 | 7 | impl_select_page!(SysUser{select_page(dto:&UserPageDTO)=> 8 | "`where del_flag = '0'` 9 | if dto.userName != '': 10 | ` and user_name like #{'%'+dto.userName+'%'}` 11 | if dto.phonenumber != '': 12 | ` and phonenumber like #{'%'+dto.phonenumber+'%'}` 13 | if dto.status != '': 14 | ` and status = #{dto.status}` 15 | if dto.deptId != '': 16 | ` and (dept_id = #{dto.deptId} OR dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{dto.deptId}, ancestors)))` 17 | if !sql.contains('count'): 18 | ` order by create_time`"}); 19 | 20 | 21 | 22 | #[py_sql( 23 | "`select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time` 24 | ` from sys_user u` 25 | ` left join sys_dept d on u.dept_id = d.dept_id` 26 | ` left join sys_user_role ur on u.user_id = ur.user_id` 27 | ` left join sys_role r on r.role_id = ur.role_id` 28 | ` where u.del_flag = '0' and r.role_id = #{dto.roleId}` 29 | if dto.userName != '': 30 | ` AND u.user_name like concat('%', #{dto.userName}, '%')` 31 | if dto.phonenumber != '': 32 | ` AND u.phonenumber like concat('%', #{dto.phonenumber}, '%')` 33 | ` limit #{dto.pageNo}, #{dto.pageSize}` 34 | " 35 | )] 36 | pub async fn db_auth_user_list(rb: &mut dyn Executor, dto: &RoleAuthUserPageDTO) -> Result, Error> { 37 | impled!() 38 | } 39 | 40 | 41 | #[py_sql( 42 | "`select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time` 43 | ` from sys_user u` 44 | ` left join sys_dept d on u.dept_id = d.dept_id` 45 | ` left join sys_user_role ur on u.user_id = ur.user_id` 46 | ` left join sys_role r on r.role_id = ur.role_id` 47 | ` where u.del_flag = '0' and (r.role_id != #{dto.roleId} or r.role_id IS NULL)` 48 | ` and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{dto.roleId})` 49 | if dto.userName != '': 50 | ` AND u.user_name like concat('%', #{dto.userName}, '%')` 51 | if dto.phonenumber != '': 52 | ` AND u.phonenumber like concat('%', #{dto.phonenumber}, '%')` 53 | ` limit #{dto.pageNo}, #{dto.pageSize}` 54 | " 55 | )] 56 | pub async fn db_unallocated_user_list(rb: &mut dyn Executor, dto: &RoleAuthUserPageDTO) -> Result, Error> { 57 | impled!() 58 | } -------------------------------------------------------------------------------- /src/domain/mapper/sys_user_role.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::*; 2 | crud!(SysUserRole {}); 3 | -------------------------------------------------------------------------------- /src/domain/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::service::ApplicationConfig; 2 | use rbatis::Rbatis; 3 | pub mod dto; 4 | pub mod mapper; 5 | pub mod table; 6 | pub mod vo; 7 | 8 | pub fn init_rbatis(config: &ApplicationConfig) -> Rbatis { 9 | let rbatis = Rbatis::new(); 10 | if rbatis.is_debug_mode() == false && config.debug.eq(&true) { 11 | panic!( 12 | r#"已使用release模式运行,但是仍使用debug模式!请修改 application.yml 中debug配置项为 debug: false"# 13 | ); 14 | } 15 | return rbatis; 16 | } 17 | -------------------------------------------------------------------------------- /src/domain/table/enums.rs: -------------------------------------------------------------------------------- 1 | // use serde::{Deserializer, Serializer}; 2 | // use std::fmt::{Debug, Display, Formatter}; 3 | // 4 | // #[derive(Clone)] 5 | // pub enum LoginCheck { 6 | // NoCheck, 7 | // PasswordCheck, 8 | // PasswordImgCodeCheck, 9 | // PhoneCodeCheck, 10 | // } 11 | // 12 | // impl From for &str { 13 | // fn from(arg: LoginCheck) -> Self { 14 | // match arg { 15 | // LoginCheck::NoCheck => "", 16 | // LoginCheck::PasswordCheck => "PasswordCheck", 17 | // LoginCheck::PasswordImgCodeCheck => "PasswordImgCodeCheck", 18 | // LoginCheck::PhoneCodeCheck => "PhoneCodeCheck", 19 | // } 20 | // } 21 | // } 22 | // 23 | // impl From<&str> for LoginCheck { 24 | // fn from(arg: &str) -> Self { 25 | // match arg { 26 | // "" => LoginCheck::NoCheck, 27 | // "NoCheck" => LoginCheck::NoCheck, 28 | // "PasswordCheck" => LoginCheck::PasswordCheck, 29 | // "PasswordImgCodeCheck" => LoginCheck::PasswordImgCodeCheck, 30 | // "PhoneCodeCheck" => LoginCheck::PhoneCodeCheck, 31 | // _ => LoginCheck::NoCheck, 32 | // } 33 | // } 34 | // } 35 | // 36 | // impl Debug for LoginCheck { 37 | // fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 38 | // f.write_str(<&str>::from(self.clone())) 39 | // } 40 | // } 41 | // 42 | // impl Display for LoginCheck { 43 | // fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 44 | // f.write_str(<&str>::from(self.clone())) 45 | // } 46 | // } 47 | // 48 | // impl serde::Serialize for LoginCheck { 49 | // fn serialize(&self, serializer: S) -> Result 50 | // where 51 | // S: Serializer, 52 | // { 53 | // self.to_string().serialize(serializer) 54 | // } 55 | // } 56 | // 57 | // impl<'de> serde::Deserialize<'de> for LoginCheck { 58 | // fn deserialize(deserializer: D) -> Result 59 | // where 60 | // D: Deserializer<'de>, 61 | // { 62 | // let v = String::deserialize(deserializer)?; 63 | // Ok(LoginCheck::from(v.as_str())) 64 | // } 65 | // } 66 | -------------------------------------------------------------------------------- /src/domain/table/mod.rs: -------------------------------------------------------------------------------- 1 | mod enums; 2 | mod sms; 3 | mod tables; 4 | pub use enums::*; 5 | pub use sms::*; 6 | pub use tables::*; 7 | -------------------------------------------------------------------------------- /src/domain/table/sms.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::HashMap; 3 | 4 | #[derive(Serialize, Deserialize)] 5 | pub struct Sms { 6 | pub user_name: String, 7 | pub args: HashMap, 8 | } 9 | -------------------------------------------------------------------------------- /src/domain/vo/config.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::{SysConfig}; 2 | 3 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct SysConfigVO { 6 | pub config_id: Option, 7 | pub config_name: Option, 8 | pub config_key: Option, 9 | pub config_value: Option, 10 | pub config_type: Option, 11 | pub remark: Option, 12 | } 13 | 14 | impl From for SysConfigVO { 15 | fn from(arg: SysConfig) -> Self { 16 | Self { 17 | config_id: arg.config_id, 18 | config_name: arg.config_name, 19 | config_key: arg.config_key, 20 | config_value: arg.config_value, 21 | config_type: arg.config_type, 22 | remark: arg.remark, 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/domain/vo/dept.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbdc::datetime::DateTime; 2 | use crate::domain::table::{SysDept}; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysDeptVO { 7 | pub dept_id: Option, 8 | pub parent_id: Option, 9 | pub ancestors: Option, 10 | pub dept_name: Option, 11 | pub order_num: Option, 12 | pub leader: Option, 13 | pub phone: Option, 14 | pub email: Option, 15 | pub status: Option, 16 | pub del_flag: Option, 17 | pub create_by: Option, 18 | pub create_time: Option, 19 | pub update_by: Option, 20 | pub update_time: Option, 21 | } 22 | 23 | impl From for SysDeptVO { 24 | fn from(arg: SysDept) -> Self { 25 | Self { 26 | dept_id: arg.dept_id, 27 | parent_id: arg.parent_id, 28 | ancestors: arg.ancestors, 29 | dept_name: arg.dept_name, 30 | order_num: arg.order_num, 31 | leader: arg.leader, 32 | phone: arg.phone, 33 | email: arg.email, 34 | status: arg.status, 35 | del_flag: arg.del_flag, 36 | create_by: arg.create_by, 37 | create_time: arg.create_time, 38 | update_by: arg.update_by, 39 | update_time: None, 40 | } 41 | } 42 | } 43 | 44 | 45 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 46 | #[serde(rename_all = "camelCase")] 47 | pub struct DeptTreeVO { 48 | pub id: Option, 49 | #[serde(skip_serializing)] 50 | pub parent_id: Option, 51 | pub label: Option, 52 | #[serde(skip_serializing_if = "Option::is_none")] 53 | pub children: Option>, 54 | } 55 | 56 | impl From for DeptTreeVO { 57 | fn from(arg: SysDept) -> Self { 58 | Self { 59 | id: arg.dept_id, 60 | parent_id: arg.parent_id, 61 | label: arg.dept_name, 62 | children: None, 63 | } 64 | } 65 | } 66 | impl DeptTreeVO { 67 | pub fn is_parent(&self) -> bool { 68 | self.parent_id.is_none() || self.parent_id.clone().unwrap_or_default().eq("0") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/domain/vo/dict_data.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::{SysDictData}; 2 | use rbatis::rbdc::types::datetime::DateTime; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysDictDataVO { 7 | 8 | pub dict_code: Option, 9 | pub dict_sort: Option, 10 | pub dict_label: Option, 11 | pub dict_value: Option, 12 | pub dict_type: Option, 13 | pub css_class: Option, 14 | pub list_class: Option, 15 | pub is_default: Option, 16 | pub status: Option, 17 | pub create_by: Option, 18 | pub create_time: Option, 19 | pub update_by: Option, 20 | pub update_time: Option, 21 | pub remark: Option, 22 | } 23 | 24 | impl From for SysDictDataVO { 25 | fn from(arg: SysDictData) -> Self { 26 | Self { 27 | dict_code: arg.dict_code, 28 | dict_sort: arg.dict_sort, 29 | dict_label: arg.dict_label, 30 | dict_value: arg.dict_value, 31 | dict_type: arg.dict_type, 32 | css_class: arg.css_class, 33 | list_class: arg.list_class, 34 | is_default: arg.is_default, 35 | status: arg.status, 36 | create_by: arg.create_by, 37 | create_time: arg.create_time, 38 | update_by: arg.update_by, 39 | update_time: arg.update_time, 40 | remark: arg.remark, 41 | } 42 | } 43 | } 44 | 45 | impl SysDictDataVO {} 46 | 47 | 48 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 49 | #[serde(rename_all = "camelCase")] 50 | pub struct SysDictDataSimpleVO { 51 | pub dict_label: Option, 52 | pub dict_value: Option, 53 | pub css_class: Option, 54 | pub list_class: Option, 55 | } 56 | 57 | impl From for SysDictDataSimpleVO { 58 | fn from(arg: SysDictData) -> Self { 59 | Self { 60 | dict_label: arg.dict_label, 61 | dict_value: arg.dict_value, 62 | css_class: arg.css_class, 63 | list_class: arg.list_class, 64 | } 65 | } 66 | } 67 | 68 | impl SysDictDataSimpleVO {} 69 | -------------------------------------------------------------------------------- /src/domain/vo/dict_type.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::SysDictType; 2 | use rbatis::rbdc::types::datetime::DateTime; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysDictTypeVO { 7 | pub dict_id: Option, 8 | pub dict_name: Option, 9 | pub dict_type: Option, 10 | pub status: Option, 11 | pub create_by: Option, 12 | pub create_time: Option, 13 | pub update_by: Option, 14 | pub update_time: Option, 15 | pub remark: Option, 16 | } 17 | 18 | impl From for SysDictTypeVO { 19 | fn from(arg: SysDictType) -> Self { 20 | Self { 21 | dict_id: arg.dict_id, 22 | dict_name: arg.dict_name, 23 | dict_type: arg.dict_type, 24 | status: arg.status, 25 | create_by: arg.create_by, 26 | create_time: arg.create_time, 27 | update_by: arg.update_by, 28 | update_time: arg.update_time, 29 | remark: arg.remark, 30 | } 31 | } 32 | } 33 | 34 | impl SysDictTypeVO {} 35 | -------------------------------------------------------------------------------- /src/domain/vo/jwt.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use jsonwebtoken::errors::ErrorKind; 3 | use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | /// JWT authentication Token structure 7 | /// diff: 不再保存如此多的信息,都保留在缓存 8 | #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] 9 | pub struct JWTToken { 10 | //账号id 11 | pub login_user_key: String, 12 | //过期时间 13 | pub exp: usize, 14 | } 15 | 16 | impl JWTToken { 17 | /// create token_auth 18 | /// secret: your secret string 19 | pub fn create_token(&self, secret: &str) -> Result { 20 | return match encode( 21 | &Header::default(), 22 | self, 23 | &EncodingKey::from_secret(secret.as_ref()), 24 | ) { 25 | Ok(t) => Ok(t), 26 | Err(_) => Err(Error::from("JWTToken encode fail!")), // in practice you would return the error 27 | }; 28 | } 29 | /// verify token_auth invalid 30 | /// secret: your secret string 31 | pub fn verify(secret: &str, token: &str) -> Result { 32 | let validation = Validation::default(); 33 | return match decode::( 34 | &token, 35 | &DecodingKey::from_secret(secret.as_ref()), 36 | &validation, 37 | ) { 38 | Ok(c) => Ok(c.claims), 39 | Err(err) => match *err.kind() { 40 | ErrorKind::InvalidToken => return Err(Error::from("InvalidToken")), // Example on how to handle a specific error 41 | ErrorKind::InvalidIssuer => return Err(Error::from("InvalidIssuer")), // Example on how to handle a specific error 42 | _ => return Err(Error::from("InvalidToken other errors")), 43 | }, 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/domain/vo/menu.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::SysMenu; 2 | use rbatis::rbdc::types::datetime::DateTime; 3 | use crate::config::global_variables::{CHAR_FALSE, TYPE_DIR, TYPE_MENU}; 4 | 5 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 6 | #[serde(rename_all = "camelCase")] 7 | pub struct SysMenuVO { 8 | pub menu_id: Option, 9 | pub menu_name: Option, 10 | //父id(可空) 11 | pub parent_id: Option, 12 | //顺序 13 | pub order_num: Option, 14 | //前端-菜单路径 15 | pub path: Option, 16 | //组件路径 17 | pub component: Option, 18 | //组件路径 19 | pub query: Option, 20 | //是否为外链 21 | pub is_frame: Option, 22 | //是否缓存 23 | pub is_cache: Option, 24 | //菜单类型 25 | pub menu_type: Option, 26 | //菜单可见 27 | pub visible: Option, 28 | //菜单状态 29 | pub status: Option, 30 | //权限标识 31 | pub perms: Option, 32 | //图标 33 | pub icon: Option, 34 | pub create_time: Option, 35 | pub children: Option>, 36 | } 37 | 38 | impl From for SysMenuVO { 39 | fn from(arg: SysMenu) -> Self { 40 | Self { 41 | menu_id: arg.menu_id, 42 | menu_name: arg.menu_name, 43 | parent_id: arg.parent_id, 44 | order_num: arg.order_num, 45 | path: arg.path, 46 | component: arg.component, 47 | query: arg.query, 48 | is_frame: arg.is_frame, 49 | is_cache: arg.is_cache, 50 | menu_type: arg.menu_type, 51 | visible: arg.visible, 52 | status: arg.status, 53 | perms: arg.perms, 54 | icon: arg.icon, 55 | create_time: arg.create_time, 56 | children: vec![].into(), 57 | } 58 | } 59 | } 60 | 61 | impl SysMenuVO { 62 | pub fn is_parent(&self) -> bool { 63 | self.parent_id.unwrap_or_default() == 0 64 | } 65 | pub fn is_menu_frame(&self) -> bool { 66 | self.is_parent() && self.menu_type.clone().unwrap_or_default() == TYPE_MENU && self.is_frame.clone().unwrap_or_default() == CHAR_FALSE 67 | } 68 | pub fn is_inner_link(&self) -> bool { 69 | self.is_frame.clone().unwrap_or_default() == CHAR_FALSE && (self.path.clone().unwrap_or_default().starts_with("http://") || self.path.clone().unwrap_or_default().starts_with("https://")) 70 | } 71 | pub fn is_parent_view(&self) -> bool { 72 | !self.is_parent() && self.menu_type.clone().unwrap_or_default() == TYPE_DIR 73 | } 74 | } 75 | 76 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 77 | //#[serde(rename_all = "camelCase")] 78 | pub struct MenuTreeSelectVO { 79 | pub id: Option, 80 | pub label: Option, 81 | #[serde(skip_serializing_if = "Option::is_none")] 82 | pub children: Option>, 83 | } -------------------------------------------------------------------------------- /src/domain/vo/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dict_type; 2 | pub mod dict_data; 3 | pub mod jwt; 4 | pub mod menu; 5 | pub mod router; 6 | pub mod role; 7 | pub mod sign_in; 8 | pub mod user; 9 | pub mod config; 10 | pub mod sys_logininfor; 11 | pub mod dept; 12 | pub mod post; 13 | pub mod monitor; 14 | pub mod notice; 15 | 16 | pub use dict_type::*; 17 | pub use dict_data::*; 18 | pub use jwt::*; 19 | pub use menu::*; 20 | pub use router::*; 21 | pub use role::*; 22 | pub use sign_in::*; 23 | pub use user::*; 24 | pub use config::*; 25 | pub use dept::*; 26 | pub use sys_logininfor::*; 27 | pub use post::*; 28 | pub use monitor::*; 29 | pub use notice::*; 30 | 31 | use crate::error::Error; 32 | use actix_web::HttpResponse; 33 | use rbatis::sql::Page; 34 | use serde::de::DeserializeOwned; 35 | use serde::{Deserialize, Serialize}; 36 | use serde_json::Value; 37 | 38 | pub const CODE_SUCCESS: u64 = 200; 39 | pub const CODE_FAIL: u64 = 500; 40 | 41 | /// The http interface returns the model structure, providing basic json data structures such as code, msg, and data 42 | #[derive(Debug, Serialize, Deserialize, Clone)] 43 | #[serde(rename_all = "camelCase")] 44 | pub struct RespVO { 45 | pub code: u64, 46 | #[serde(skip_serializing_if = "Option::is_none")] 47 | pub msg: Option, 48 | #[serde(skip_serializing_if = "Option::is_none")] 49 | // #[serde(rename = "camelCase")] 50 | pub data: Option, 51 | } 52 | 53 | impl RespVO 54 | where 55 | T: Serialize + DeserializeOwned + Clone, 56 | { 57 | pub fn from_result(arg: &Result) -> Self { 58 | if arg.is_ok() { 59 | Self { 60 | code: CODE_SUCCESS, 61 | msg: None, 62 | data: arg.clone().ok(), 63 | } 64 | } else { 65 | Self { 66 | code: CODE_FAIL, 67 | msg: Some(arg.clone().err().unwrap().to_string()), 68 | data: None, 69 | } 70 | } 71 | } 72 | 73 | pub fn from(arg: &T) -> Self { 74 | Self { 75 | code: CODE_SUCCESS, 76 | msg: None, 77 | data: Some(arg.clone()), 78 | } 79 | } 80 | pub fn from_success_info(msg: &str) -> Self { 81 | Self { 82 | code: CODE_SUCCESS, 83 | msg: Some(msg.to_string()), 84 | data: None, 85 | } 86 | } 87 | 88 | pub fn from_error_result(code: u64, arg: &Result) -> Self { 89 | Self { 90 | code, 91 | msg: Some(arg.clone().err().unwrap().to_string()), 92 | data: None, 93 | } 94 | } 95 | 96 | pub fn from_error_info(code: u64, info: &str) -> Self { 97 | Self { 98 | code, 99 | msg: Some(info.to_string()), 100 | data: None, 101 | } 102 | } 103 | 104 | 105 | pub fn judge(affected: u64, success_msg: String, fail_message: String) -> Self { 106 | if affected >= 1 { 107 | Self { 108 | code: CODE_SUCCESS, 109 | msg: Some(success_msg), 110 | data: None, 111 | } 112 | } else { 113 | Self { 114 | code: CODE_FAIL, 115 | msg: Some(fail_message), 116 | data: None, 117 | } 118 | } 119 | } 120 | pub fn resp_json(&self) -> HttpResponse { 121 | return HttpResponse::Ok() 122 | .insert_header(("Access-Control-Allow-Origin", "*")) 123 | .insert_header(("Cache-Control", "no-cache")) 124 | .insert_header(("Content-Type", "text/json;charset=UTF-8")) 125 | .body(self.to_string()); 126 | } 127 | } 128 | 129 | impl ToString for RespVO 130 | where 131 | T: Serialize + DeserializeOwned + Clone, 132 | { 133 | fn to_string(&self) -> String { 134 | serde_json::to_string(self).unwrap() 135 | } 136 | } 137 | 138 | /// 自定义输入,serde_json map 139 | #[derive(Debug, Serialize, Deserialize, Clone)] 140 | pub struct RespJson { 141 | inner: serde_json::map::Map, 142 | } 143 | 144 | impl RespJson 145 | { 146 | 147 | pub fn new() -> Self { 148 | Self { 149 | inner: serde_json::map::Map::new() 150 | } 151 | } 152 | pub fn success() -> Self { 153 | let mut inner = serde_json::map::Map::new(); 154 | inner.insert("code".to_string(), CODE_SUCCESS.into()); 155 | Self { 156 | inner 157 | } 158 | } 159 | pub fn success_info(msg:&str) -> Self { 160 | let mut inner = serde_json::map::Map::new(); 161 | inner.insert("code".to_string(), CODE_SUCCESS.into()); 162 | inner.insert("msg".to_string(), msg.into()); 163 | Self { 164 | inner 165 | } 166 | } 167 | //插入新的 168 | pub fn insert(&mut self, key: String, v: Value) -> &mut RespJson { 169 | self.inner.insert(key, v); 170 | self 171 | } 172 | pub fn resp_json(&self) -> HttpResponse { 173 | return HttpResponse::Ok() 174 | .insert_header(("Access-Control-Allow-Origin", "*")) 175 | .insert_header(("Cache-Control", "no-cache")) 176 | .insert_header(("Content-Type", "text/json;charset=UTF-8")) 177 | .body(self.to_string()); 178 | } 179 | } 180 | 181 | impl ToString for RespJson 182 | { 183 | fn to_string(&self) -> String { 184 | serde_json::to_string(&self.inner).unwrap() 185 | } 186 | } 187 | 188 | #[derive(Debug, Serialize, Deserialize, Clone)] 189 | #[serde(rename_all = "camelCase")] 190 | pub struct PageVO { 191 | pub code: u64, 192 | #[serde(skip_serializing_if = "Option::is_none")] 193 | pub rows: Option>, 194 | pub total: Option, 195 | pub msg:Option 196 | } 197 | 198 | impl PageVO 199 | where 200 | T: Serialize + DeserializeOwned + Clone, 201 | { 202 | pub fn from_result(arg: &Result, Error>) -> Self { 203 | if arg.is_ok() { 204 | let arg = arg.as_ref().unwrap(); 205 | Self { 206 | code: CODE_SUCCESS, 207 | rows: Some(arg.records.clone()), 208 | total: Some(arg.total), 209 | msg: None, 210 | } 211 | } else { 212 | Self { 213 | code: CODE_FAIL, 214 | rows: None, 215 | total: None, 216 | msg: Some(arg.clone().err().unwrap().to_string()), 217 | } 218 | } 219 | } 220 | pub fn resp_json(&self) -> HttpResponse { 221 | return HttpResponse::Ok() 222 | .insert_header(("Access-Control-Allow-Origin", "*")) 223 | .insert_header(("Cache-Control", "no-cache")) 224 | .insert_header(("Content-Type", "text/json;charset=UTF-8")) 225 | .body(self.to_string()); 226 | } 227 | } 228 | 229 | impl ToString for PageVO 230 | where 231 | T: Serialize + DeserializeOwned + Clone, 232 | { 233 | fn to_string(&self) -> String { 234 | serde_json::to_string(self).unwrap() 235 | } 236 | } 237 | 238 | -------------------------------------------------------------------------------- /src/domain/vo/monitor.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbdc::datetime::DateTime; 2 | use serde::{Deserialize, Serialize}; 3 | /* 4 | 服务器VO 5 | */ 6 | #[derive(Clone, Debug, Deserialize,Serialize)] 7 | #[serde(rename_all = "camelCase")] 8 | pub struct ServerVO { 9 | pub cpu: CpuVO, 10 | pub mem: Memory, 11 | pub sys: SysVO, 12 | pub sys_files: Vec, 13 | } 14 | 15 | #[derive(Clone, Debug, Deserialize,Serialize)] 16 | #[serde(rename_all = "camelCase")] 17 | pub struct CpuVO { 18 | //CPU核心数 19 | pub cpu_num: u8, 20 | //空闲率 21 | pub free: f32, 22 | //系统使用率 23 | pub sys: f32, 24 | pub total: u64, 25 | //用户使用率 26 | pub used: f32, 27 | pub wait: f32, 28 | } 29 | 30 | 31 | #[derive(Clone, Debug, Deserialize,Serialize)] 32 | #[serde(rename_all = "camelCase")] 33 | pub struct Memory { 34 | //空闲率 35 | pub free: String, 36 | //内存大小(G) 37 | pub total: String, 38 | //已用 39 | pub used: String, 40 | //用户使用率 41 | pub usage: String, 42 | } 43 | 44 | #[derive(Clone, Debug, Deserialize,Serialize)] 45 | #[serde(rename_all = "camelCase")] 46 | pub struct SysVO { 47 | //IP地址 48 | pub computer_ip: String, 49 | //计算机名称 50 | pub computer_name: String, 51 | //64位还是32位 52 | pub os_arch: String, 53 | //操作系统名称 54 | pub os_name: String, 55 | } 56 | 57 | #[derive(Clone, Debug, Deserialize,Serialize)] 58 | #[serde(rename_all = "camelCase")] 59 | pub struct DiskVO { 60 | //盘符 61 | pub dir_name: String, 62 | //剩下空间 63 | pub free: String, 64 | //磁盘格式 65 | pub sys_type_name: String, 66 | //磁盘名称 67 | pub type_name: String, 68 | //总容量 69 | pub total: String, 70 | // 71 | //使用空间 72 | pub used: String, 73 | //使用率 74 | pub usage: String, 75 | } 76 | 77 | //在线用户 78 | #[derive(Clone, Debug, Serialize, Deserialize)] 79 | #[serde(rename_all = "camelCase")] 80 | pub struct SysUserOnlineVO { 81 | pub token_id: Option, 82 | pub dept_name: Option, 83 | pub user_name: Option, 84 | pub ipaddr: Option, 85 | pub login_location: Option, 86 | pub phonenumber: Option, 87 | pub browser: Option, 88 | pub os: Option, 89 | pub login_time: Option 90 | } 91 | -------------------------------------------------------------------------------- /src/domain/vo/notice.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbdc::datetime::DateTime; 2 | use crate::domain::table::{SysNotice}; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysNoticeVO { 7 | pub notice_id: Option, 8 | pub notice_title: Option, 9 | pub notice_content: Option, 10 | pub notice_type: Option, 11 | pub create_by: Option, 12 | pub create_time: Option, 13 | pub status: Option, 14 | pub remark: Option, 15 | } 16 | 17 | impl From for SysNoticeVO { 18 | fn from(arg: SysNotice) -> Self { 19 | Self { 20 | notice_id: arg.notice_id, 21 | notice_title: arg.notice_title, 22 | notice_content: arg.notice_content, 23 | notice_type: arg.notice_type, 24 | create_by: arg.create_by, 25 | create_time: arg.create_time, 26 | status: arg.status, 27 | remark: arg.remark, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/domain/vo/post.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbdc::datetime::DateTime; 2 | use crate::domain::table::{SysPost}; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysPostVO { 7 | pub post_id: Option, 8 | pub post_code: Option, 9 | pub post_name: Option, 10 | pub post_sort: Option, 11 | pub status: Option, 12 | pub create_by: Option, 13 | pub create_time: Option, 14 | pub update_by: Option, 15 | pub update_time: Option, 16 | pub remark: Option, 17 | } 18 | 19 | impl From for SysPostVO { 20 | fn from(arg: SysPost) -> Self { 21 | Self { 22 | post_id: arg.post_id, 23 | post_code: arg.post_code, 24 | post_name: arg.post_name, 25 | post_sort: arg.post_sort, 26 | status: arg.status, 27 | create_by: arg.create_by, 28 | create_time: arg.create_time, 29 | update_by: arg.update_by, 30 | update_time: arg.update_time, 31 | remark: arg.remark 32 | } 33 | } 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/domain/vo/role.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::SysRole; 2 | use rbatis::rbdc::datetime::DateTime; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysRoleVO { 7 | pub admin:bool, 8 | pub role_id: Option, 9 | pub role_name: Option, 10 | pub role_key: Option, 11 | pub role_sort: Option, 12 | pub data_scope: Option, 13 | pub menu_check_strictly: Option, 14 | pub dept_check_strictly: Option, 15 | pub status: Option, 16 | pub del_flag: Option, 17 | pub create_by: Option, 18 | pub create_time: Option, 19 | pub update_by: Option, 20 | pub update_time: Option, 21 | pub remark: Option, 22 | pub flag:bool 23 | } 24 | 25 | impl From for SysRoleVO { 26 | fn from(arg: SysRole) -> Self { 27 | Self { 28 | admin: arg.role_id.clone().unwrap()=="1", 29 | role_id: arg.role_id, 30 | role_name: arg.role_name, 31 | role_key: arg.role_key, 32 | role_sort: arg.role_sort, 33 | data_scope: arg.data_scope, 34 | menu_check_strictly: arg.menu_check_strictly.eq(&Some('1')).into(), 35 | dept_check_strictly: arg.dept_check_strictly.eq(&Some('1')).into(), 36 | status: arg.status, 37 | del_flag: arg.del_flag, 38 | create_by: arg.create_by, 39 | create_time: arg.create_time, 40 | update_by: arg.update_by, 41 | update_time: arg.update_time, 42 | remark: arg.remark, 43 | flag: false, 44 | } 45 | } 46 | } 47 | 48 | impl SysRoleVO { 49 | pub fn from_option(arg: Option) -> Option { 50 | match arg { 51 | Some(arg) => Some(SysRoleVO { 52 | admin: arg.role_id.clone().unwrap()=="1", 53 | role_id: arg.role_id, 54 | role_name: arg.role_name, 55 | role_key: arg.role_key, 56 | role_sort: arg.role_sort, 57 | data_scope: arg.data_scope, 58 | menu_check_strictly: arg.menu_check_strictly.eq(&Some('1')).into(), 59 | dept_check_strictly: arg.dept_check_strictly.eq(&Some('1')).into(), 60 | status: arg.status, 61 | del_flag: arg.del_flag, 62 | create_by: arg.create_by, 63 | create_time: arg.create_time, 64 | update_by: arg.update_by, 65 | update_time: arg.update_time, 66 | remark: arg.remark, 67 | flag: false, 68 | }), 69 | _ => None, 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/domain/vo/router.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 2 | #[serde(rename_all = "camelCase")] 3 | pub struct MetaVO { 4 | //设置该路由在侧边栏和面包屑中展示的名字 5 | pub title: Option, 6 | //设置该路由的图标,对应路径src/assets/icons/svg 7 | pub icon: Option, 8 | //设置为true,则不会被 缓存 9 | pub no_cache: Option, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | //内链地址(http(s)://开头) 12 | pub link: Option, 13 | } 14 | 15 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 16 | #[serde(rename_all = "camelCase")] 17 | pub struct RouterVO { 18 | //路由名字 19 | pub name: Option, 20 | //路由地址 21 | pub path: Option, 22 | //是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 23 | pub hidden: Option, 24 | //重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 25 | #[serde(skip_serializing_if = "Option::is_none")] 26 | pub redirect: Option, 27 | //组件地址 28 | pub component: Option, 29 | //路由参数:如 {"id": 1, "name": "ry"} 30 | #[serde(skip_serializing_if = "Option::is_none")] 31 | pub query: Option, 32 | //当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 33 | #[serde(skip_serializing_if = "Option::is_none")] 34 | pub always_show: Option, 35 | //其他元素 36 | pub meta: Option, 37 | //子路由 38 | #[serde(skip_serializing_if = "Vec::is_empty")] 39 | pub children: Vec, 40 | } 41 | -------------------------------------------------------------------------------- /src/domain/vo/sign_in.rs: -------------------------------------------------------------------------------- 1 | use rbatis::rbdc::datetime::DateTime; 2 | use crate::domain::vo::user::SysUserVO; 3 | use serde::{Deserialize, Serialize}; 4 | use crate::domain::table::SysRole; 5 | 6 | ///的后的所有信息,保存到redis 7 | #[derive(Debug, Serialize, Deserialize, Clone)] 8 | pub struct UserCache { 9 | pub id: String, 10 | pub user_name: String, 11 | pub user: Option, 12 | pub permissions: Vec, 13 | pub menu_ids: Vec, 14 | pub roles: Vec, 15 | pub login_time: DateTime, 16 | pub token_key:String 17 | } 18 | 19 | impl ToString for UserCache { 20 | fn to_string(&self) -> String { 21 | serde_json::json!(self).to_string() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/domain/vo/sys_logininfor.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::{SysLogininfor}; 2 | use rbatis::rbdc::types::datetime::DateTime; 3 | 4 | #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct SysLogininforVO { 7 | pub info_id: Option, 8 | pub user_name: Option, 9 | pub ipaddr: Option, 10 | pub login_location: Option, 11 | pub browser: Option, 12 | pub os: Option, 13 | pub status: Option, 14 | pub msg: Option, 15 | pub login_time: Option, 16 | } 17 | 18 | impl From for SysLogininforVO { 19 | fn from(arg: SysLogininfor) -> Self { 20 | Self { 21 | info_id: arg.info_id, 22 | user_name: arg.user_name, 23 | ipaddr: arg.ipaddr, 24 | login_location: arg.login_location, 25 | browser: arg.browser, 26 | os: arg.os, 27 | status: arg.status, 28 | msg: arg.msg, 29 | login_time: arg.login_time 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/domain/vo/user.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::{SysUser}; 2 | use rbatis::rbdc::datetime::DateTime; 3 | use serde::{Deserialize, Serialize}; 4 | use crate::config::global_variables::ADMIN_NAME; 5 | use crate::domain::vo::SysRoleVO; 6 | 7 | #[derive(Clone, Debug, Serialize, Deserialize)] 8 | #[serde(rename_all = "camelCase")] 9 | pub struct SysUserVO { 10 | pub user_id: Option, 11 | pub dept_id: Option, 12 | pub user_name: Option, 13 | pub nick_name: Option, 14 | pub email: Option, 15 | pub phonenumber: Option, 16 | pub sex: Option, 17 | pub avatar: Option, 18 | pub password: Option, 19 | pub status: Option, 20 | pub del_flag: Option, 21 | pub login_ip: Option, 22 | pub login_date: Option, 23 | pub create_by: Option, 24 | pub create_time: Option, 25 | pub update_by: Option, 26 | pub update_time: Option, 27 | pub remark: Option, 28 | pub admin: bool, 29 | pub roles: Option>, 30 | } 31 | 32 | impl From for SysUserVO { 33 | fn from(arg: SysUser) -> Self { 34 | Self { 35 | user_id: arg.user_id, 36 | dept_id: arg.dept_id, 37 | user_name: arg.user_name.clone(), 38 | nick_name: arg.nick_name, 39 | email: arg.email, 40 | phonenumber: arg.phonenumber, 41 | sex: arg.sex, 42 | avatar: arg.avatar, 43 | //屏蔽密码 44 | password: None, 45 | status: arg.status, 46 | del_flag: arg.del_flag, 47 | login_ip: arg.login_ip, 48 | login_date: arg.login_date, 49 | create_by: arg.create_by, 50 | create_time: arg.create_time, 51 | update_by: arg.update_by, 52 | update_time: arg.update_time, 53 | remark: arg.remark, 54 | admin: arg.user_name.unwrap_or_default().eq(ADMIN_NAME), 55 | roles: None, 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Errorand Result types. 2 | use std::error::Error as StdError; 3 | use std::fmt::{self, Debug, Display}; 4 | use std::io; 5 | 6 | 7 | use serde::de::Visitor; 8 | use serde::ser::{Serialize, Serializer}; 9 | use serde::{Deserialize, Deserializer}; 10 | 11 | pub type Result = std::result::Result; 12 | 13 | /// A generic error that represents all the ways a method can fail inside of rexpr::core. 14 | #[derive(Debug)] 15 | #[non_exhaustive] 16 | pub enum Error { 17 | /// Default Error 18 | E(String), 19 | } 20 | 21 | impl Display for Error { 22 | // IntellijRust does not understand that [non_exhaustive] applies only for downstream crates 23 | // noinspection RsMatchCheck 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | match self { 26 | Error::E(error) => write!(f, "{}", error), 27 | } 28 | } 29 | } 30 | 31 | impl StdError for Error {} 32 | 33 | impl From for Error{ 34 | fn from(arg: redis::RedisError) -> Self { 35 | Error::E(arg.to_string()) 36 | } 37 | } 38 | 39 | impl From for Error { 40 | #[inline] 41 | fn from(err: io::Error) -> Self { 42 | Error::from(err.to_string()) 43 | } 44 | } 45 | 46 | impl From<&str> for Error { 47 | fn from(arg: &str) -> Self { 48 | return Error::E(arg.to_string()); 49 | } 50 | } 51 | 52 | impl From for Error { 53 | fn from(arg: String) -> Self { 54 | return Error::E(arg); 55 | } 56 | } 57 | 58 | impl From<&dyn std::error::Error> for Error { 59 | fn from(arg: &dyn std::error::Error) -> Self { 60 | return Error::E(arg.to_string()); 61 | } 62 | } 63 | 64 | impl From for std::io::Error { 65 | fn from(arg: Error) -> Self { 66 | arg.into() 67 | } 68 | } 69 | 70 | impl From for Error { 71 | fn from(arg: rbatis::Error) -> Self { 72 | Error::E(arg.to_string()) 73 | } 74 | } 75 | 76 | impl From for Error { 77 | fn from(arg: actix_web::error::Error) -> Self { 78 | Error::E(arg.to_string()) 79 | } 80 | } 81 | 82 | 83 | impl Clone for Error { 84 | fn clone(&self) -> Self { 85 | Error::from(self.to_string()) 86 | } 87 | 88 | fn clone_from(&mut self, source: &Self) { 89 | *self = Self::from(source.to_string()); 90 | } 91 | } 92 | 93 | // This is what #[derive(Serialize)] would generate. 94 | impl Serialize for Error { 95 | fn serialize(&self, serializer: S) -> std::result::Result 96 | where 97 | S: Serializer, 98 | { 99 | serializer.serialize_str(self.to_string().as_str()) 100 | } 101 | } 102 | 103 | struct ErrorVisitor; 104 | 105 | impl<'de> Visitor<'de> for ErrorVisitor { 106 | type Value = String; 107 | 108 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 109 | formatter.write_str("a string") 110 | } 111 | 112 | fn visit_string(self, v: String) -> std::result::Result 113 | where 114 | E: std::error::Error, 115 | { 116 | Ok(v) 117 | } 118 | 119 | fn visit_str(self, v: &str) -> std::result::Result 120 | where 121 | E: std::error::Error, 122 | { 123 | Ok(v.to_string()) 124 | } 125 | } 126 | 127 | impl<'de> Deserialize<'de> for Error { 128 | fn deserialize(deserializer: D) -> std::result::Result 129 | where 130 | D: Deserializer<'de>, 131 | { 132 | let r = deserializer.deserialize_string(ErrorVisitor)?; 133 | return Ok(Error::from(r)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] //允许未使用的变量 2 | #![allow(unused_must_use)] 3 | extern crate log; 4 | #[macro_use] 5 | extern crate rbatis; 6 | extern crate bcrypt; 7 | 8 | #[macro_use] 9 | pub mod util; 10 | pub mod config; 11 | pub mod controller; 12 | pub mod domain; 13 | pub mod error; 14 | //pub mod middleware; 15 | pub mod service; 16 | pub mod web_data; 17 | pub mod http_server; 18 | //管理token,为兼容若依管理平台重新写,不再使用中间件进行验证 19 | pub mod token_auth; -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use ruoyi_rust::service::CONTEXT; 2 | use ruoyi_rust::http_server; 3 | 4 | 5 | /// use tokio,because Rbatis specifies the runtime-tokio 6 | #[tokio::main] 7 | async fn main() -> std::io::Result<()> { 8 | //日志追加器 9 | ruoyi_rust::config::log::init_log(); 10 | 11 | 12 | //连接数据库 13 | CONTEXT.init_pool().await; 14 | 15 | CONTEXT.sys_dict_data_service.update_cache().await?; 16 | CONTEXT.sys_config_service.loading_config_cache().await?; 17 | CONTEXT.sys_menu_service.update_cache().await?; 18 | 19 | http_server::build_server(&CONTEXT.config.base_api).await 20 | } 21 | -------------------------------------------------------------------------------- /src/middleware/auth.rs: -------------------------------------------------------------------------------- 1 | use crate::config::cache_variables::LOGIN_TOKEN_KEY; 2 | use crate::config::global_variables::ADMIN_NAME; 3 | use crate::domain::vo::{JWTToken, UserCache}; 4 | use crate::service::CONTEXT; 5 | 6 | pub struct Auth; 7 | 8 | ///Whether the interface is in the whitelist 9 | pub fn is_white_list_api(path: &str) -> bool { 10 | if path.eq("/") { 11 | return true; 12 | } 13 | for x in &CONTEXT.config.white_list_api { 14 | if x.contains(path) { 15 | return true; 16 | } 17 | } 18 | return false; 19 | } 20 | 21 | ///Check whether the token_auth is valid and has not expired 22 | pub async fn checked_token(token: &str, path: &str) -> Result { 23 | //check token_auth alive 24 | let claims = JWTToken::verify(&CONTEXT.config.jwt_secret, token); 25 | match claims { 26 | Ok(c) => { 27 | let key = format!("{}{}", LOGIN_TOKEN_KEY, c.login_user_key); 28 | let user_cache: Result = CONTEXT.cache_service.get_json(&key).await; 29 | match user_cache { 30 | Ok(u) => { 31 | CONTEXT.cache_service.expire(&key, (CONTEXT.config.token_expired_min * 60) as i32).await; 32 | Ok(u) 33 | } 34 | Err(e) => { 35 | Err(e) 36 | } 37 | } 38 | } 39 | Err(e) => { 40 | return Err(crate::error::Error::from(e.to_string())); 41 | } 42 | } 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/middleware/auth_actix.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::{ready, Ready}, 3 | rc::Rc, 4 | }; 5 | 6 | use actix_http::body::BoxBody; 7 | use actix_web::{ 8 | dev::{Service, ServiceRequest, ServiceResponse, Transform}, 9 | Error, 10 | }; 11 | use futures_util::future::LocalBoxFuture; 12 | 13 | use crate::domain::vo::RespVO; 14 | use crate::middleware::auth::{check_auth, checked_token, is_white_list_api}; 15 | use crate::util::token::get_token; 16 | 17 | pub struct Auth; 18 | 19 | 20 | impl Transform for Auth 21 | where 22 | S: Service, Error=Error>, 23 | S::Future: 'static, 24 | { 25 | type Response = ServiceResponse; 26 | type Error = Error; 27 | type InitError = (); 28 | type Transform = AuthMiddleware; 29 | type Future = Ready>; 30 | 31 | fn new_transform(&self, service: S) -> Self::Future { 32 | ready(Ok(AuthMiddleware { 33 | service: Rc::new(service), 34 | })) 35 | } 36 | } 37 | 38 | pub struct AuthMiddleware { 39 | // This is special: We need this to avoid lifetime issues. 40 | service: Rc, 41 | } 42 | 43 | impl Service for AuthMiddleware 44 | where 45 | S: Service, Error=Error> + 'static, 46 | S::Future: 'static, 47 | { 48 | type Response = ServiceResponse; 49 | type Error = Error; 50 | type Future = LocalBoxFuture<'static, Result>; 51 | 52 | #[inline] 53 | fn poll_ready( 54 | &self, 55 | cx: &mut ::core::task::Context<'_>, 56 | ) -> ::core::task::Poll> { 57 | self.service.poll_ready(cx).map_err(Into::into) 58 | } 59 | 60 | fn call(&self, req: ServiceRequest) -> Self::Future { 61 | let svc = self.service.clone(); 62 | let token = get_token(req.request()); 63 | let path = req.path().to_string(); 64 | Box::pin(async move { 65 | // //debug mode not enable auth 66 | // if !CONTEXT.config.debug { 67 | if !is_white_list_api(&path) { 68 | //非白名单检查token是否有效 69 | match checked_token(&token, &path).await { 70 | Ok(data) => { 71 | match check_auth(&data, &path).await { 72 | Ok(_) => { 73 | //刷新过期时间 74 | crate::web_data::set_user_name(data.user_name); 75 | } 76 | Err(e) => { 77 | //仅提示拦截 78 | let resp: RespVO = RespVO { 79 | code: 0, 80 | msg: Some(format!("无权限访问:{}", e.to_string())), 81 | data: None, 82 | }; 83 | return Ok(req.into_response(resp.resp_json())); 84 | } 85 | } 86 | } 87 | Err(e) => { 88 | //401 http状态码会强制前端退出当前登陆状态 89 | let resp: RespVO = RespVO { 90 | code: 401, 91 | msg: Some(format!("Unauthorized for:{}", e.to_string())), 92 | data: None, 93 | }; 94 | return Ok(req.into_response(resp.resp_json()));//根据前台,需要返回http status code 200,但是code 是401 95 | } 96 | } 97 | } 98 | // } 99 | let res = svc.call(req).await?; 100 | Ok(res) 101 | }) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/middleware/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod auth; 2 | pub mod auth_actix; 3 | -------------------------------------------------------------------------------- /src/service/cache_service.rs: -------------------------------------------------------------------------------- 1 | use std::format; 2 | use crate::config::config::ApplicationConfig; 3 | use crate::error::{ Result}; 4 | use crate::service::{MemService, RedisService}; 5 | use futures_util::future::BoxFuture; 6 | use serde::de::DeserializeOwned; 7 | use serde::Serialize; 8 | use std::time::Duration; 9 | 10 | pub trait ICacheService: Sync + Send { 11 | fn set_string(&self, k: &str, v: &str) -> BoxFuture>; 12 | 13 | fn get_string(&self, k: &str) -> BoxFuture>; 14 | 15 | fn set_string_ex(&self, k: &str, v: &str, ex: Option) -> BoxFuture>; 16 | 17 | fn ttl(&self, k: &str) -> BoxFuture>; 18 | 19 | fn del(&self, k: &str) -> BoxFuture>; 20 | 21 | fn keys(&self, k: &str) -> BoxFuture>>; 22 | 23 | fn hgetall(&self, k: &str) -> BoxFuture>>; 24 | 25 | fn expire(&self, k: &str, time_sec: i32) -> BoxFuture>; 26 | //从在db插入 27 | fn hset(&self, k: &str, f: &str, v: &str) -> BoxFuture>; 28 | //切换Db 29 | fn select(&self, db: &str) -> BoxFuture>; 30 | } 31 | 32 | pub struct CacheService { 33 | pub inner: Box, 34 | } 35 | 36 | impl CacheService { 37 | pub fn new(cfg: &ApplicationConfig) -> crate::error::Result { 38 | match cfg.cache_type.as_str() { 39 | "mem" => { 40 | println!("[ruoyi_rust] cache_type: mem"); 41 | Ok(Self { 42 | inner: Box::new(MemService::default()), 43 | }) 44 | } 45 | "redis" => { 46 | println!("[ruoyi_rust] cache_type: redis"); 47 | Ok(Self { 48 | inner: Box::new(RedisService::new(&cfg.web_redis_url)), 49 | }) 50 | } 51 | e => { 52 | panic!( 53 | "[ruoyi_rust] unknown of cache_type: \"{}\",current support 'mem' or 'redis'", 54 | e 55 | ); 56 | } 57 | } 58 | } 59 | 60 | pub async fn set_string(&self, k: &str, v: &str) -> Result { 61 | self.inner.set_string(k, v).await 62 | } 63 | 64 | pub async fn get_string(&self, k: &str) -> Result { 65 | self.inner.get_string(k).await 66 | } 67 | 68 | pub async fn set_json(&self, k: &str, v: &T) -> Result 69 | where 70 | T: Serialize + Sync, 71 | { 72 | self.set_json_ex(k, v, None).await 73 | } 74 | 75 | pub async fn set_json_ex(&self, k: &str, v: &T, ex: Option) -> Result 76 | where 77 | T: Serialize + Sync, 78 | { 79 | let data = serde_json::to_string(v); 80 | if data.is_err() { 81 | return Err(crate::error::Error::from(format!( 82 | "CacheService set_json fail:{}", 83 | data.err().unwrap() 84 | ))); 85 | } 86 | let data = self.set_string_ex(k, data.unwrap().as_str(), ex).await?; 87 | Ok(data) 88 | } 89 | 90 | pub async fn get_json(&self, k: &str) -> Result 91 | where 92 | T: DeserializeOwned + Sync, 93 | { 94 | let mut r = self.get_string(k).await?; 95 | if r.is_empty() { 96 | r = "null".to_string(); 97 | } 98 | let data: serde_json::Result = serde_json::from_str(r.as_str()); 99 | if data.is_err() { 100 | return Err(crate::error::Error::from(format!( 101 | "MemCacheService GET fail:{}", 102 | data.err().unwrap() 103 | ))); 104 | } 105 | Ok(data.unwrap()) 106 | } 107 | 108 | pub async fn set_string_ex(&self, k: &str, v: &str, ex: Option) -> Result { 109 | self.inner.set_string_ex(k, v, ex).await 110 | } 111 | 112 | pub async fn ttl(&self, k: &str) -> Result { 113 | self.inner.ttl(k).await 114 | } 115 | pub async fn del(&self, k: &str) -> Result { 116 | self.inner.del(k).await 117 | } 118 | pub async fn keys(&self, k: &str) -> Result> { 119 | self.inner.keys(k).await 120 | } 121 | pub async fn expire(&self, k: &str, time_sec: i32) -> Result { 122 | self.inner.expire(k,time_sec).await 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/service/mem_service.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use crate::service::ICacheService; 3 | use futures_util::future::BoxFuture; 4 | use parking_lot::Mutex; 5 | use std::collections::hash_map::RandomState; 6 | use std::collections::HashMap; 7 | use std::ops::Sub; 8 | use std::time::{Duration, Instant}; 9 | 10 | ///Memory Cache Service 11 | pub struct MemService { 12 | pub cache: Mutex), RandomState>>, 13 | } 14 | 15 | impl MemService { 16 | pub fn recycling(&self) { 17 | let mut map_lock_guard = self.cache.lock(); 18 | let mut need_removed = vec![]; 19 | for (k, v) in map_lock_guard.iter() { 20 | if let Some((i, d)) = v.1 { 21 | if i.elapsed() >= d { 22 | //out of time 23 | need_removed.push(k.to_string()); 24 | } 25 | } 26 | } 27 | for x in need_removed { 28 | map_lock_guard.remove(&x); 29 | } 30 | } 31 | } 32 | 33 | impl Default for MemService { 34 | fn default() -> Self { 35 | Self { 36 | cache: Default::default(), 37 | } 38 | } 39 | } 40 | 41 | impl ICacheService for MemService { 42 | fn set_string(&self, k: &str, v: &str) -> BoxFuture> { 43 | self.recycling(); 44 | let k = k.to_string(); 45 | let v = v.to_string(); 46 | let mut guard = self.cache.lock(); 47 | guard.insert(k.to_string(), (v.clone(), None)); 48 | Box::pin(async move { 49 | return Ok(v.to_string()); 50 | }) 51 | } 52 | 53 | fn get_string(&self, k: &str) -> BoxFuture> { 54 | self.recycling(); 55 | let k = k.to_string(); 56 | let guard = self.cache.lock(); 57 | let mut v = String::new(); 58 | if let Some(r) = guard.get(&k) { 59 | v = r.0.to_string(); 60 | } 61 | Box::pin(async move { Ok(v) }) 62 | } 63 | 64 | fn set_string_ex(&self, k: &str, v: &str, t: Option) -> BoxFuture> { 65 | self.recycling(); 66 | let k = k.to_string(); 67 | let v = v.to_string(); 68 | let mut locked = self.cache.lock(); 69 | let mut e = Option::None; 70 | if let Some(ex) = t { 71 | e = Some((Instant::now(), ex)); 72 | } 73 | let inserted = locked.insert(k, (v.clone(), e)); 74 | Box::pin(async move { 75 | if inserted.is_some() { 76 | return Ok(v.to_string()); 77 | } 78 | return Err(crate::error::Error::E(format!( 79 | "[ruoyi_rust][mem_service]insert fail!" 80 | ))); 81 | }) 82 | } 83 | 84 | fn ttl(&self, k: &str) -> BoxFuture> { 85 | self.recycling(); 86 | let locked = self.cache.lock(); 87 | let v = locked.get(k).cloned(); 88 | drop(locked); 89 | let v = match v { 90 | None => -2, 91 | Some((r, o)) => match o { 92 | None => -1, 93 | Some((i, d)) => { 94 | let use_time = i.elapsed(); 95 | if d > use_time { 96 | d.sub(use_time).as_secs() as i64 97 | } else { 98 | 0 99 | } 100 | } 101 | }, 102 | }; 103 | Box::pin(async move { Ok(v) }) 104 | } 105 | 106 | fn del(&self, k: &str) -> BoxFuture> { 107 | self.recycling(); 108 | let mut locked = self.cache.lock(); 109 | let v = locked.remove(k); 110 | drop(locked); 111 | let v = match v { 112 | None => false, 113 | Some((r, o)) => true, 114 | }; 115 | Box::pin(async move { Ok(v) }) 116 | } 117 | 118 | fn keys(&self, k: &str) -> BoxFuture>> { 119 | Box::pin(async move { Err(crate::error::Error::from("not implemented!")) }) 120 | } 121 | fn hgetall(&self, k: &str) -> BoxFuture>> { 122 | Box::pin(async move { Err(crate::error::Error::from("not implemented!")) }) 123 | } 124 | 125 | fn expire(&self, k: &str, time_sec: i32) -> BoxFuture> { 126 | Box::pin(async move { Err(crate::error::Error::from("not implemented!")) }) 127 | } 128 | 129 | fn hset(&self, k: &str, f: &str, v: &str) -> BoxFuture> { 130 | Box::pin(async move { Err(crate::error::Error::from("not implemented!")) }) 131 | } 132 | 133 | fn select(&self, db: &str) -> BoxFuture> { 134 | Box::pin(async move { Err(crate::error::Error::from("not implemented!")) }) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/service/mod.rs: -------------------------------------------------------------------------------- 1 | mod cache_service; 2 | mod mem_service; 3 | mod redis_service; 4 | mod sys_config_service; 5 | mod sys_dict_type_service; 6 | mod sys_dict_data_service; 7 | mod sys_menu_service; 8 | mod sys_role_menu_service; 9 | mod sys_role_service; 10 | mod sys_sms_service; 11 | mod sys_trash_service; 12 | mod sys_user_role_service; 13 | mod sys_user_service; 14 | mod sys_logininfor_service; 15 | mod sys_dept_service; 16 | mod sys_post_service; 17 | mod sys_notice_service; 18 | 19 | pub use crate::config::config::ApplicationConfig; 20 | pub use cache_service::*; 21 | pub use mem_service::*; 22 | use once_cell::sync::Lazy; 23 | use rbatis::rbatis::Rbatis; 24 | use rbdc_mysql::driver::MysqlDriver; 25 | pub use redis_service::*; 26 | pub use sys_config_service::*; 27 | pub use sys_dict_type_service::*; 28 | pub use sys_dict_data_service::*; 29 | pub use sys_menu_service::*; 30 | pub use sys_role_menu_service::*; 31 | pub use sys_role_service::*; 32 | pub use sys_sms_service::*; 33 | pub use sys_trash_service::*; 34 | pub use sys_user_role_service::*; 35 | pub use sys_user_service::*; 36 | pub use sys_dept_service::*; 37 | pub use sys_logininfor_service::*; 38 | pub use sys_post_service::*; 39 | pub use sys_notice_service::*; 40 | 41 | 42 | /// CONTEXT is all of the service struct 43 | pub static CONTEXT: Lazy = Lazy::new(|| ServiceContext::default()); 44 | 45 | #[macro_export] 46 | macro_rules! pool { 47 | () => { 48 | &mut $crate::service::CONTEXT.rb.clone() 49 | }; 50 | } 51 | 52 | #[macro_export] 53 | macro_rules! get_config_value { 54 | ($arg:expr)=> { 55 | $crate::service::CONTEXT.sys_config_service.select_config_by_key($arg).await.unwrap_or_default() 56 | }; 57 | } 58 | 59 | pub struct ServiceContext { 60 | pub config: ApplicationConfig, 61 | pub rb: Rbatis, 62 | pub cache_service: CacheService, 63 | pub sys_menu_service: SysMenuService, 64 | pub sys_user_service: SysUserService, 65 | pub sys_role_service: SysRoleService, 66 | pub sys_role_menu_service: SysRoleMenuService, 67 | pub sys_user_role_service: SysUserRoleService, 68 | pub sys_dict_type_service: SysDictTypeService, 69 | pub sys_dict_data_service: SysDictDataService, 70 | pub sys_config_service: SysConfigService, 71 | pub sys_dept_service: SysDeptService, 72 | pub sys_trash_service: SysTrashService, 73 | pub sys_logininfor_service: SysLogininforService, 74 | pub sys_post_service: SysPostService, 75 | pub sys_notice_service: SysNoticeService, 76 | } 77 | 78 | impl ServiceContext { 79 | /// init database pool 80 | pub async fn init_pool(&self) { 81 | //连接数据库 82 | println!( 83 | "[ruoyi_rust] rbatis pool init ({})...", 84 | self.config.database_url 85 | ); 86 | self.rb 87 | .init(MysqlDriver {}, &self.config.database_url) 88 | .expect("[ruoyi_rust] rbatis pool init fail!"); 89 | log::info!( 90 | "[ruoyi_rust] rbatis pool init success! pool state = {:?}", 91 | self.rb.get_pool().expect("pool not init!").status() 92 | ); 93 | 94 | println!("Local: http://{}",self.config.server_url.replace("0.0.0.0", "127.0.0.1")); 95 | } 96 | } 97 | 98 | impl Default for ServiceContext { 99 | fn default() -> Self { 100 | let config = ApplicationConfig::default(); 101 | ServiceContext { 102 | rb: crate::domain::init_rbatis(&config), 103 | cache_service: CacheService::new(&config).unwrap(), 104 | config, 105 | sys_menu_service: SysMenuService {}, 106 | sys_user_service: SysUserService {}, 107 | sys_role_service: SysRoleService {}, 108 | sys_role_menu_service: SysRoleMenuService {}, 109 | sys_user_role_service: SysUserRoleService {}, 110 | sys_dict_type_service: SysDictTypeService {}, 111 | sys_dict_data_service: SysDictDataService {}, 112 | sys_config_service: SysConfigService {}, 113 | sys_dept_service: SysDeptService {}, 114 | sys_trash_service: SysTrashService {}, 115 | sys_logininfor_service: SysLogininforService {}, 116 | sys_post_service: SysPostService {}, 117 | sys_notice_service: SysNoticeService{}, 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/service/sys_config_service.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::{Page, PageRequest}; 2 | use rbs::to_value; 3 | use crate::domain::dto::ConfigPageDTO; 4 | use crate::domain::table::SysConfig; 5 | use crate::domain::vo::SysConfigVO; 6 | use crate::error::Error; 7 | use crate::error::Result; 8 | use crate::pool; 9 | use crate::service::CONTEXT; 10 | 11 | const SYS_CONFIG_KEY: &'static str = "sys_config:"; 12 | 13 | /// dictionary service 14 | pub struct SysConfigService {} 15 | 16 | impl SysConfigService { 17 | pub async fn page(&self, arg: &ConfigPageDTO) -> Result> { 18 | let page_req = PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)); 19 | let data = SysConfig::select_page(pool!(), &PageRequest::from(arg), arg).await?; 20 | let page = Page::::from(data); 21 | Ok(page) 22 | } 23 | 24 | 25 | pub async fn detail(&self, config_id: &str) -> Result { 26 | let config = SysConfig::select_by_column(pool!(), field_name!(SysConfig.config_id), config_id) 27 | .await? 28 | .into_iter() 29 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", config_id)))?; 30 | let config_vo = SysConfigVO::from(config); 31 | return Ok(config_vo); 32 | } 33 | 34 | pub async fn add(&self, config: &SysConfig) -> Result { 35 | if !self.check_config_key_unique("", config.config_key.as_ref().unwrap()).await? { return Err(Error::from("参数键名重复!")); } 36 | 37 | let result = SysConfig::insert(pool!(), &config).await?.rows_affected; 38 | if result == 1 { 39 | self.add_to_cache(config).await; 40 | } 41 | Ok(result) 42 | } 43 | 44 | pub async fn update(&self, config: SysConfig) -> Result { 45 | let old_config = SysConfig::select_by_column(pool!(), "config_id", config.config_id.as_ref().unwrap()).await?.into_iter().next(); 46 | match old_config { 47 | None => { return Err(Error::from("参数错误")); } 48 | Some(c) => { 49 | if !c.config_key.clone().unwrap().eq(config.config_key.as_ref().unwrap()) { 50 | CONTEXT.cache_service.del(&self.get_cache_key(&c.config_key.unwrap())).await; 51 | } 52 | } 53 | } 54 | if !self.check_config_key_unique(config.config_id.as_ref().unwrap(), config.config_key.as_ref().unwrap()).await? { return Err(Error::from("参数键名重复!")); } 55 | let result = SysConfig::update_by_column(pool!(), &config, "config_id").await?.rows_affected; 56 | if result == 1 { 57 | self.add_to_cache(&config).await; 58 | } 59 | Ok(result) 60 | } 61 | 62 | pub async fn remove(&self, config_id: &str) -> Result { 63 | let targets = SysConfig::select_by_column(pool!(), "config_id", config_id).await?; 64 | let r = SysConfig::delete_by_column(pool!(), "config_id", config_id).await?; 65 | if r.rows_affected > 0 { 66 | //copy data to trash 67 | CONTEXT.sys_trash_service.add("sys_config", &targets).await; 68 | CONTEXT.cache_service.del(&self.get_cache_key(targets.into_iter().next().unwrap().config_key.clone().as_ref().unwrap())).await; 69 | } 70 | Ok(r.rows_affected) 71 | } 72 | 73 | 74 | /** 75 | * 获取验证码开关,默认不打开 76 | * 77 | * @return true开启,false关闭 78 | */ 79 | pub async fn select_captcha_enabled(&self) -> Result 80 | { 81 | let captcha_enabled = self.select_config_by_key("sys.account.captcha_enabled").await; 82 | match captcha_enabled { 83 | Ok(s) => { 84 | Ok(s.eq("true")) 85 | } 86 | Err(_) => { Ok(false) } 87 | } 88 | } 89 | 90 | /** 91 | * 根据键名查询参数配置信息 92 | * 93 | */ 94 | pub async fn select_config_by_key(&self, config_key: &str) -> Result 95 | { 96 | let config_value = CONTEXT.cache_service.get_string(&self.get_cache_key(config_key)).await?; 97 | if !config_value.is_empty() { 98 | return Ok(config_value); 99 | } 100 | let config = SysConfig::select_by_column(pool!(), "config_key", config_key).await?; 101 | match config.into_iter().next() { 102 | None => { Ok("".to_string()) } 103 | Some(c) => { 104 | CONTEXT.cache_service.set_string(&self.get_cache_key(c.config_key.as_ref().unwrap()), c.config_value.clone().as_ref().unwrap()).await; 105 | Ok(c.config_value.unwrap()) 106 | } 107 | } 108 | } 109 | /** 110 | * 校验参数键名是否唯一 111 | * 112 | */ 113 | pub async fn check_config_key_unique(&self, config_id: &str, config_key: &str) -> Result 114 | { 115 | let count: u64 = pool!() 116 | .query_decode("select count(1) as count from sys_config where config_id!=? and config_key =?", vec![to_value!(config_id), to_value!(config_key)]) 117 | .await 118 | .unwrap(); 119 | Ok(count < 1) 120 | } 121 | /* 122 | 清空参数缓存数据 123 | */ 124 | pub async fn clear_config_cache(&self) -> Result<()> 125 | { 126 | let keys = CONTEXT.cache_service.keys("sys_config:*").await?; 127 | for k in keys { 128 | CONTEXT.cache_service.del(&k).await; 129 | } 130 | Ok(()) 131 | } 132 | 133 | /** 134 | * 重置参数缓存数据 135 | */ 136 | 137 | pub async fn reset_config_cache(&self) -> Result<()> 138 | { 139 | self.clear_config_cache(); 140 | self.loading_config_cache(); 141 | Ok(()) 142 | } 143 | //加载所有的到redis 144 | pub async fn loading_config_cache(&self) -> Result { 145 | let config_list = SysConfig::select_all(pool!()).await?; 146 | for config in config_list { 147 | self.add_to_cache(&config).await; 148 | } 149 | Ok(1) 150 | } 151 | async fn add_to_cache(&self, config: &SysConfig) { 152 | CONTEXT.cache_service.set_string(&self.get_cache_key(config.config_key.as_ref().unwrap()), config.config_value.as_ref().unwrap()).await; 153 | } 154 | //对config_key进行处理 155 | fn get_cache_key(&self, origin: &str) -> String { 156 | format!("{}{}", SYS_CONFIG_KEY, origin) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/service/sys_dept_service.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{DeptQueryDTO}; 2 | use crate::domain::table::SysDept; 3 | 4 | use crate::domain::vo::{DeptTreeVO, SysDeptVO}; 5 | use crate::error::Error; 6 | use crate::error::Result; 7 | use crate::pool; 8 | use crate::service::CONTEXT; 9 | 10 | //const DICT_KEY: &'static str = "sys_dept:all"; 11 | 12 | /// dictionary service 13 | pub struct SysDeptService {} 14 | 15 | impl SysDeptService { 16 | pub async fn all(&self, arg: &DeptQueryDTO) -> Result> { 17 | let data = SysDept::select_all_query(pool!(), arg).await?; 18 | let mut res = vec![]; 19 | for d in data { 20 | res.push(SysDeptVO::from(d)); 21 | } 22 | Ok(res) 23 | } 24 | 25 | 26 | pub async fn detail(&self, dept_id: &str) -> Result { 27 | let dept = SysDept::select_by_column(pool!(), field_name!(SysDept.dept_id), dept_id) 28 | .await? 29 | .into_iter() 30 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", dept_id)))?; 31 | let dept_vo = SysDeptVO::from(dept); 32 | return Ok(dept_vo); 33 | } 34 | 35 | pub async fn add(&self, arg: &SysDept) -> Result { 36 | let old = SysDept::select_by_column( 37 | pool!(), 38 | rbatis::field_name!(SysDept.dept_id), 39 | arg.dept_id.as_deref().unwrap_or_default(), 40 | ) 41 | .await?; 42 | // if old.len() > 0 { 43 | // return Err(Error::from(format!( 44 | // "字典已存在! code={}", 45 | // arg.code.as_deref().unwrap_or_default() 46 | // ))); 47 | // } 48 | let result = Ok(SysDept::insert(pool!(), &arg).await?.rows_affected); 49 | return result; 50 | } 51 | 52 | pub async fn update(&self, data: SysDept) -> Result { 53 | let result = SysDept::update_by_column(pool!(), &data, "dept_id").await; 54 | return Ok(result?.rows_affected); 55 | } 56 | 57 | pub async fn remove(&self, dept_id: &str) -> Result { 58 | let targets = SysDept::select_by_column(pool!(), "dept_id", dept_id).await?; 59 | 60 | let r = SysDept::delete_by_column(pool!(), "dept_id", dept_id).await?; 61 | if r.rows_affected > 0 { 62 | //copy data to trash 63 | CONTEXT.sys_trash_service.add("sys_dept", &targets).await?; 64 | } 65 | Ok(r.rows_affected) 66 | } 67 | //根据user id获得本单位及下属单位部门列表 68 | pub async fn get_dept_tree(&self, user_id: &str) -> Result> { 69 | let depts = SysDept::select_all_query(pool!(), &DeptQueryDTO {// todo 70 | dept_name: None, 71 | status: None, 72 | }).await?; 73 | let mut res = vec![]; 74 | for d in depts { 75 | res.push(DeptTreeVO::from(d)); 76 | } 77 | self. build_dept_tree(&res) 78 | } 79 | ///An depts array with a hierarchy 80 | pub fn build_dept_tree(&self, all_depts: &Vec) -> Result> { 81 | //find tops 82 | let mut tops = vec![]; 83 | for item in all_depts { 84 | //parent id null, it is an top menu 85 | if item.is_parent() { 86 | tops.push(item.clone()); 87 | } 88 | } 89 | //find child 90 | // tops.sort_by(|a, b| a.order_num.cmp(&b.order_num)); 91 | for mut item in &mut tops { 92 | self.loop_find_children(&mut item, &all_depts); 93 | } 94 | Ok(tops) 95 | } 96 | 97 | ///Loop to find the parent-child associative relation array 98 | pub fn loop_find_children(&self, arg: &mut DeptTreeVO, all_depts: &Vec) { 99 | let mut children = vec![]; 100 | for item in all_depts { 101 | if !item.is_parent() && item.parent_id == arg.id { 102 | let mut item = item.clone(); 103 | self.loop_find_children(&mut item, all_depts); 104 | children.push(item); 105 | } 106 | } 107 | if !children.is_empty() { 108 | // children.sort_by(|a, b| a.order_num.cmp(&b.order_num)); 109 | arg.children = Some(children); 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/service/sys_dict_data_service.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use rbatis::sql::{Page, PageRequest}; 3 | 4 | use crate::domain::dto::{ DictDataPageDTO}; 5 | use crate::domain::table::SysDictData; 6 | use crate::domain::vo::{SysDictDataSimpleVO, SysDictDataVO}; 7 | use crate::error::Error; 8 | use crate::error::Result; 9 | use crate::pool; 10 | use crate::service::CONTEXT; 11 | 12 | //const DICT_KEY: &'static str = "sys_dict_data:all"; 13 | 14 | /// dictionary service 15 | pub struct SysDictDataService {} 16 | 17 | impl SysDictDataService { 18 | pub async fn page(&self, arg: &DictDataPageDTO) -> Result> { 19 | let page_req = PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)); 20 | let data = SysDictData::select_page(pool!(), &PageRequest::from(arg), arg).await?; 21 | let page = Page::::from(data); 22 | Ok(page) 23 | } 24 | 25 | pub async fn get_by_dict_type(&self, dict_type: &String) -> Result> { 26 | let data = SysDictData::select_by_dict_type(pool!(), &dict_type).await?; 27 | let mut res = vec![]; 28 | for d in data { 29 | res.push(SysDictDataSimpleVO::from(d)) 30 | } 31 | Ok(res) 32 | } 33 | 34 | pub async fn detail(&self, dict_code: &str) -> Result { 35 | let dict_data = SysDictData::select_by_column(pool!(), field_name!(SysDictData.dict_code), dict_code) 36 | .await? 37 | .into_iter() 38 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", dict_code)))?; 39 | let dict_data_vo = SysDictDataVO::from(dict_data); 40 | return Ok(dict_data_vo); 41 | } 42 | 43 | pub async fn add(&self, arg: &SysDictData) -> Result { 44 | let old = SysDictData::select_by_column( 45 | pool!(), 46 | rbatis::field_name!(SysDictData.dict_code), 47 | arg.dict_code.as_deref().unwrap_or_default(), 48 | ) 49 | .await?; 50 | // if old.len() > 0 { 51 | // return Err(Error::from(format!( 52 | // "字典已存在! code={}", 53 | // arg.code.as_deref().unwrap_or_default() 54 | // ))); 55 | // } 56 | let result = Ok(SysDictData::insert(pool!(), &arg).await?.rows_affected); 57 | self.update_cache().await?; 58 | return result; 59 | } 60 | 61 | pub async fn update(&self, data: SysDictData) -> Result { 62 | let result = SysDictData::update_by_column(pool!(), &data, "dict_code").await; 63 | if result.is_ok() { 64 | self.update_cache().await?; 65 | } 66 | return Ok(result?.rows_affected); 67 | } 68 | 69 | pub async fn remove(&self, dict_code: &str) -> Result { 70 | let targets = SysDictData::select_by_column(pool!(), "dict_code", dict_code).await?; 71 | 72 | let r = SysDictData::delete_by_column(pool!(), "dict_code", dict_code).await?; 73 | if r.rows_affected > 0 { 74 | self.update_cache().await?; 75 | //copy data to trash 76 | CONTEXT.sys_trash_service.add("sys_dict_data", &targets).await?; 77 | } 78 | Ok(r.rows_affected) 79 | } 80 | 81 | /// update for all cache 82 | pub async fn update_cache(&self) -> Result<()> { 83 | let mut all = SysDictData::select_all(pool!()).await?; 84 | all.sort_by(|a, b| a.dict_sort.cmp(&b.dict_sort)); 85 | let mut dict_data_map: HashMap<&str, Vec> = HashMap::new(); 86 | for dict_data in all { 87 | let key = dict_data.dict_type.clone().as_deref().unwrap(); 88 | let key = "1233";//fixme 89 | let data_sim = SysDictDataSimpleVO::from(dict_data); 90 | if dict_data_map.contains_key(key) { 91 | dict_data_map.get_mut(key).unwrap().push(data_sim); 92 | } else { 93 | dict_data_map.insert(key, vec![data_sim]); 94 | } 95 | } 96 | // CONTEXT.cache_service.set_json(DICT_KEY, &dict_data_map).await?; 97 | return Ok(()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/service/sys_dict_type_service.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::{Page, PageRequest}; 2 | use rbs::to_value; 3 | 4 | use crate::domain::dto::{DictTypePageDTO}; 5 | use crate::domain::table::{ SysDictType}; 6 | use crate::domain::vo::SysDictTypeVO; 7 | use crate::error::Error; 8 | use crate::error::Result; 9 | use crate::pool; 10 | use crate::service::CONTEXT; 11 | 12 | //const DICT_KEY: &'static str = "sys_dict_type:all"; 13 | 14 | /// dictionary service 15 | pub struct SysDictTypeService {} 16 | 17 | impl SysDictTypeService { 18 | pub async fn page(&self, arg: &DictTypePageDTO) -> Result> { 19 | let page_req = PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)); 20 | let data = SysDictType::select_page(pool!(), &PageRequest::from(arg), arg).await?; 21 | let page = Page::::from(data); 22 | Ok(page) 23 | } 24 | pub async fn finds_all(&self) -> Result> { 25 | let data = SysDictType::select_all(pool!()).await?; 26 | let mut dict_type_vos = vec![]; 27 | for d in data { 28 | dict_type_vos.push(SysDictTypeVO::from(d)); 29 | } 30 | Ok(dict_type_vos) 31 | } 32 | pub async fn detail(&self, dict_id: &str) -> Result { 33 | let dict_type = SysDictType::select_by_column(pool!(), field_name!(SysDictType.dict_id), dict_id) 34 | .await? 35 | .into_iter() 36 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", dict_id)))?; 37 | let dict_type_vo = SysDictTypeVO::from(dict_type); 38 | return Ok(dict_type_vo); 39 | } 40 | pub async fn add(&self, arg: &SysDictType) -> Result { 41 | let old = SysDictType::select_by_column( 42 | pool!(), 43 | rbatis::field_name!(SysDictType.dict_id), 44 | arg.dict_id.as_deref().unwrap_or_default(), 45 | ) 46 | .await?; 47 | // if old.len() > 0 { 48 | // return Err(Error::from(format!( 49 | // "字典已存在! code={}", 50 | // arg.code.as_deref().unwrap_or_default() 51 | // ))); 52 | // } 53 | let result = Ok(SysDictType::insert(pool!(), &arg).await?.rows_affected); 54 | CONTEXT.sys_dict_data_service.update_cache().await; 55 | return result; 56 | } 57 | 58 | pub async fn update(&self, data: SysDictType) -> Result { 59 | let result = SysDictType::update_by_column(pool!(), &data, "dict_id").await; 60 | if result.is_ok() { 61 | //更新dict_data 62 | CONTEXT.sys_dict_data_service.update_cache().await?; 63 | } 64 | return Ok(result?.rows_affected); 65 | } 66 | 67 | pub async fn remove(&self, dict_id: &str) -> Result { 68 | let targets = SysDictType::select_by_column(pool!(), "dict_id", dict_id).await?; 69 | if targets.len() == 1 { 70 | let dict_type=targets.get(0).unwrap().dict_type.clone().unwrap(); 71 | let count: u64 = pool!() 72 | .query_decode("select count(1) as count from sys_dict_type where dict_type =?", vec![to_value!(dict_type)]) 73 | .await 74 | .unwrap(); 75 | if count > 0 { return Err(Error::from("存在子项,不允许删除!")); } 76 | 77 | } else { return Err(Error::from(format!("字典id{}不存在!", dict_id)));} 78 | 79 | let r = SysDictType::delete_by_column(pool!(), "dict_id", dict_id).await?; 80 | if r.rows_affected > 0 { 81 | CONTEXT.sys_dict_data_service.update_cache().await?; 82 | //copy data to trash 83 | CONTEXT.sys_trash_service.add("sys_dict_type", &targets).await?; 84 | } 85 | Ok(r.rows_affected) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/service/sys_logininfor_service.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::{Page, PageRequest}; 2 | 3 | use crate::domain::dto::LogininforPageDTO; 4 | use crate::domain::table::SysLogininfor; 5 | use crate::domain::vo::SysLogininforVO; 6 | use crate::error::Result; 7 | use crate::pool; 8 | 9 | 10 | /// dictionary service 11 | pub struct SysLogininforService {} 12 | 13 | impl SysLogininforService { 14 | pub async fn page(&self, arg: &LogininforPageDTO) -> Result> { 15 | let page_req = PageRequest::new(arg.page_no.unwrap_or(1), arg.page_size.unwrap_or(10)); 16 | let data = SysLogininfor::select_page(pool!(), &PageRequest::from(arg), arg).await?; 17 | let page = Page::::from(data); 18 | Ok(page) 19 | } 20 | 21 | //异步加入日志 22 | pub async fn add_async(&self, arg: &SysLogininfor) -> Result { 23 | let info=arg.to_owned(); 24 | tokio::spawn(async move { 25 | SysLogininfor::insert(pool!(), &info).await; 26 | }); 27 | return Ok(1); 28 | } 29 | 30 | 31 | pub async fn remove(&self, info_id: &str) -> Result { 32 | let r = SysLogininfor::delete_by_column(pool!(), "info_id", info_id).await?; 33 | Ok(r.rows_affected) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/service/sys_notice_service.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::{Page, PageRequest}; 2 | 3 | use crate::domain::dto::NoticePageDTO; 4 | use crate::domain::table::SysNotice; 5 | use crate::domain::vo::SysNoticeVO; 6 | use crate::error::Error; 7 | use crate::error::Result; 8 | use crate::pool; 9 | use crate::service::CONTEXT; 10 | 11 | /// notice service 12 | pub struct SysNoticeService {} 13 | 14 | impl SysNoticeService { 15 | pub async fn page(&self, arg: &NoticePageDTO) -> Result> { 16 | let data = SysNotice::select_page( 17 | pool!(), 18 | &PageRequest::from(arg), 19 | arg 20 | ) 21 | .await?; 22 | let page = Page::::from(data); 23 | 24 | Ok(page) 25 | } 26 | 27 | pub async fn finds_all(&self) -> Result> { 28 | let data = SysNotice::select_all(pool!()).await?; 29 | let mut notice_vos = vec![]; 30 | for s in data { 31 | notice_vos.push(SysNoticeVO::from(s)); 32 | } 33 | Ok(notice_vos) 34 | } 35 | pub async fn detail(&self, notice_id: &str) -> Result { 36 | let notice = SysNotice::select_by_column(pool!(), field_name!(SysNotice.notice_id), notice_id) 37 | .await? 38 | .into_iter() 39 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", notice_id)))?; 40 | let notice_vo = SysNoticeVO::from(notice); 41 | return Ok(notice_vo); 42 | } 43 | 44 | pub async fn add(&self, arg: &SysNotice) -> Result { 45 | let old = SysNotice::select_by_column( 46 | pool!(), 47 | rbatis::field_name!(SysNotice.notice_id), 48 | arg.notice_id.as_deref().unwrap_or_default(), 49 | ) 50 | .await?; 51 | let result = Ok(SysNotice::insert(pool!(), &arg).await?.rows_affected); 52 | return result; 53 | } 54 | 55 | pub async fn update(&self, data: SysNotice) -> Result { 56 | let result = SysNotice::update_by_column(pool!(), &data, "notice_id").await; 57 | return Ok(result?.rows_affected); 58 | } 59 | 60 | pub async fn remove(&self, notice_id: &str) -> Result { 61 | let targets = SysNotice::select_by_column(pool!(), "notice_id", notice_id).await?; 62 | 63 | let r = SysNotice::delete_by_column(pool!(), "notice_id", notice_id).await?; 64 | if r.rows_affected > 0 { 65 | //copy data to trash 66 | CONTEXT.sys_trash_service.add("sys_notice", &targets).await?; 67 | } 68 | Ok(r.rows_affected) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/service/sys_post_service.rs: -------------------------------------------------------------------------------- 1 | use rbatis::sql::{Page, PageRequest}; 2 | 3 | use crate::domain::dto::PostPageDTO; 4 | use crate::domain::table::SysPost; 5 | use crate::domain::vo::SysPostVO; 6 | use crate::error::Error; 7 | use crate::error::Result; 8 | use crate::pool; 9 | use crate::service::CONTEXT; 10 | 11 | //const DICT_KEY: &'static str = "sys_post:all"; 12 | 13 | /// dictionary service 14 | pub struct SysPostService {} 15 | 16 | impl SysPostService { 17 | pub async fn page(&self, arg: &PostPageDTO) -> Result> { 18 | let data = SysPost::select_page( 19 | pool!(), 20 | &PageRequest::from(arg), 21 | arg 22 | ) 23 | .await?; 24 | let page = Page::::from(data); 25 | 26 | Ok(page) 27 | } 28 | 29 | pub async fn finds_all(&self) -> Result> { 30 | let data = SysPost::select_all(pool!()).await?; 31 | let mut post_vos = vec![]; 32 | for s in data { 33 | post_vos.push(SysPostVO::from(s)); 34 | } 35 | Ok(post_vos) 36 | } 37 | pub async fn detail(&self, post_id: &str) -> Result { 38 | let post = SysPost::select_by_column(pool!(), field_name!(SysPost.post_id), post_id) 39 | .await? 40 | .into_iter() 41 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", post_id)))?; 42 | let post_vo = SysPostVO::from(post); 43 | return Ok(post_vo); 44 | } 45 | 46 | pub async fn add(&self, arg: &SysPost) -> Result { 47 | let old = SysPost::select_by_column( 48 | pool!(), 49 | rbatis::field_name!(SysPost.post_id), 50 | arg.post_id.as_deref().unwrap_or_default(), 51 | ) 52 | .await?; 53 | let result = Ok(SysPost::insert(pool!(), &arg).await?.rows_affected); 54 | return result; 55 | } 56 | 57 | pub async fn update(&self, data: SysPost) -> Result { 58 | let result = SysPost::update_by_column(pool!(), &data, "post_id").await; 59 | return Ok(result?.rows_affected); 60 | } 61 | 62 | pub async fn remove(&self, post_id: &str) -> Result { 63 | let targets = SysPost::select_by_column(pool!(), "post_id", post_id).await?; 64 | 65 | let r = SysPost::delete_by_column(pool!(), "post_id", post_id).await?; 66 | if r.rows_affected > 0 { 67 | //copy data to trash 68 | CONTEXT.sys_trash_service.add("sys_post", &targets).await?; 69 | } 70 | Ok(r.rows_affected) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/service/sys_role_menu_service.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::SysRoleMenu; 2 | use crate::error::Result; 3 | use crate::pool; 4 | 5 | /// Role Menu Service 6 | pub struct SysRoleMenuService {} 7 | 8 | impl SysRoleMenuService { 9 | pub async fn add_role_menus(&self, role_id: String, menu_ids: Vec) -> Result { 10 | let mut sys_role_menu = vec![]; 11 | for menu_id in menu_ids { 12 | sys_role_menu.push(SysRoleMenu { 13 | role_id: Some(role_id.clone()), 14 | menu_id: Some(menu_id), 15 | }); 16 | } 17 | Ok(SysRoleMenu::insert_batch(pool!(), &sys_role_menu, 20) 18 | .await? 19 | .rows_affected) 20 | } 21 | 22 | pub async fn add_roles_menu(&self, menu_id: u64, role_ids: Vec) -> Result { 23 | let mut sys_role_menus = vec![]; 24 | for role_id in role_ids { 25 | sys_role_menus.push(SysRoleMenu { 26 | role_id: Some(role_id.clone()), 27 | menu_id: Some(menu_id), 28 | }); 29 | } 30 | Ok(SysRoleMenu::insert_batch(pool!(), &sys_role_menus, 20) 31 | .await? 32 | .rows_affected) 33 | } 34 | 35 | pub async fn remove_by_menu_id(&self, menu_id: &u64) -> Result { 36 | Ok(SysRoleMenu::delete_by_column(pool!(), "menu_id", menu_id) 37 | .await? 38 | .rows_affected) 39 | } 40 | 41 | pub async fn remove_by_role_id(&self, role_id: &String) -> Result { 42 | Ok(SysRoleMenu::delete_by_column(pool!(), "role_id", role_id) 43 | .await? 44 | .rows_affected) 45 | } 46 | 47 | pub async fn select_by_role_id(&self, role_id: &String) -> Result> { 48 | Ok(SysRoleMenu::select_by_column(pool!(), "role_id", role_id) 49 | .await?) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/service/sys_role_service.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::dto::{RolePageDTO, RoleAuthUserPageDTO}; 2 | use crate::domain::table::{SysRole, SysRoleMenu, SysUserRole}; 3 | use crate::domain::vo::{SysRoleVO, SysUserVO}; 4 | use crate::error::{Error, Result}; 5 | use crate::pool; 6 | use crate::service::CONTEXT; 7 | use rbatis::sql::{Page, PageRequest}; 8 | use crate::domain::mapper::sys_user::{db_auth_user_list, db_unallocated_user_list}; 9 | 10 | const RES_KEY: &'static str = "sys_role:all"; 11 | 12 | ///Role of service 13 | pub struct SysRoleService {} 14 | 15 | impl SysRoleService { 16 | pub async fn page(&self, arg: &RolePageDTO) -> Result> { 17 | let data = SysRole::select_page( 18 | pool!(), 19 | &PageRequest::from(arg), 20 | &arg, 21 | ) 22 | .await?; 23 | //let all_role = self.finds_all_map().await?; 24 | let page = Page::::from(data); 25 | // for mut vo in &mut page.records { 26 | // self.loop_find_childs(&mut vo, &all_role); 27 | // } 28 | 29 | Ok(page) 30 | } 31 | ///role details 32 | pub async fn detail(&self, role_id: &str) -> Result { 33 | let role = SysRole::select_by_column(pool!(), field_name!(SysRole.role_id), role_id) 34 | .await? 35 | .into_iter() 36 | .next().ok_or_else(|| Error::from(format!("不存在:{:?} 不存在!", role_id)))?; 37 | let role_vo = SysRoleVO::from(role); 38 | return Ok(role_vo); 39 | } 40 | 41 | pub async fn update_cache(&self) -> Result> { 42 | let all = SysRole::select_all(pool!()).await?; 43 | CONTEXT.cache_service.set_json(RES_KEY, &all).await?; 44 | return Ok(all); 45 | } 46 | 47 | 48 | pub async fn add(&self, role: SysRole, menu_ids: Vec) -> Result { 49 | let result = SysRole::insert(pool!(), &role).await?.rows_affected; 50 | 51 | if result > 0 && !menu_ids.is_empty() { 52 | CONTEXT.sys_role_menu_service.add_role_menus(role.role_id.unwrap(), menu_ids); 53 | } 54 | self.update_cache().await?; 55 | Ok(result) 56 | } 57 | 58 | pub async fn update(&self, role: SysRole, menu_ids: Vec) -> Result { 59 | let result = SysRole::update_by_column(pool!(), &role, field_name!(SysRole.role_id)).await?.rows_affected; 60 | if result > 0 { 61 | let role_id = role.role_id.clone().unwrap(); 62 | CONTEXT.sys_role_menu_service.remove_by_role_id(&role_id).await; 63 | if !menu_ids.is_empty() { 64 | CONTEXT.sys_role_menu_service.add_role_menus(role_id, menu_ids).await; 65 | } 66 | } 67 | self.update_cache().await?; 68 | Ok(result) 69 | } 70 | 71 | pub async fn remove(&self, id: &String) -> Result { 72 | let trash = SysRole::select_by_column(pool!(), field_name!(SysRole.role_id), id).await?; 73 | let result = SysRole::delete_by_column(pool!(), field_name!(SysRole.role_id), id).await?.rows_affected; 74 | if result > 0 { 75 | CONTEXT.sys_role_menu_service.remove_by_role_id(id).await; 76 | } 77 | CONTEXT.sys_trash_service.add("sys_role", &trash).await?; 78 | self.update_cache().await?; 79 | Ok(result) 80 | } 81 | 82 | pub async fn finds(&self, ids: &Vec) -> Result> { 83 | if ids.is_empty() { 84 | return Ok(vec![]); 85 | } 86 | Ok(SysRole::select_in_column(pool!(), "role_id", ids).await?) 87 | } 88 | 89 | pub async fn finds_all(&self) -> Result> { 90 | let data = SysRole::select_all(pool!()).await?; 91 | let mut role_vos = vec![]; 92 | for s in data { 93 | role_vos.push(SysRoleVO::from(s)); 94 | } 95 | Ok(role_vos) 96 | } 97 | 98 | //查找所有roles,如果用户包含此权限,则flag=true 99 | pub async fn finds_roles_by_user_id(&self, user_id: &str) -> Result> { 100 | let all = SysRole::select_all(pool!()).await?; 101 | let mut res = vec![]; 102 | let user_roles = SysUserRole::select_by_column(pool!(), "user_id", user_id).await?; 103 | for r in all { 104 | let mut r_vo = SysRoleVO::from(r); 105 | 106 | for ur in &user_roles { 107 | if r_vo.role_id.eq(&ur.role_id) { 108 | r_vo.flag = true; 109 | } 110 | } 111 | res.push(r_vo); 112 | } 113 | 114 | res.sort_by(|a, b| a.role_sort.cmp(&b.role_sort)); 115 | 116 | Ok(res) 117 | } 118 | 119 | pub async fn find_role_menu(&self, role_ids: &Vec) -> Result> { 120 | if role_ids.is_empty() { 121 | return Ok(vec![]); 122 | } 123 | Ok(SysRoleMenu::select_in_column(pool!(), "role_id", role_ids).await?) 124 | } 125 | 126 | // pub async fn find_user_permission( 127 | // &self, 128 | // user_id: &str, 129 | // all_menus: &BTreeMap, 130 | // ) -> Result> { 131 | // let user_roles = 132 | // SysUserRole::select_by_column(pool!(), field_name!(SysUserRole.user_id), user_id) 133 | // .await?; 134 | // let role_menu = self 135 | // .find_role_menu(&rbatis::make_table_field_vec!(&user_roles, role_id)) 136 | // .await?; 137 | // let menus = CONTEXT 138 | // .sys_menu_service.finds_menu(&rbatis::make_table_field_vec!(&role_menu, menu_id), &all_menus); 139 | // // 140 | // // let menus = CONTEXT 141 | // // .sys_menu_service 142 | // // .finds_layer(&rbatis::make_table_field_vec!(&role_menu, menu_id), &all_menus) 143 | // // .await?; 144 | // let permissions = rbatis::make_table_field_vec!(&menus, perms); 145 | // return Ok(permissions); 146 | // } 147 | // 148 | // ///Loop to find the parent-child associative relation array 149 | // pub fn loop_find_childs(&self, arg: &mut SysRoleVO, all: &HashMap) { 150 | // let mut childs = vec![]; 151 | // for (key, x) in all { 152 | // if x.parent_id.is_some() && x.parent_id.eq(&arg.id) { 153 | // let mut item = SysRoleVO::from(x.clone()); 154 | // self.loop_find_childs(&mut item, all); 155 | // childs.push(item); 156 | // } 157 | // } 158 | // if !childs.is_empty() { 159 | // arg.childs = Some(childs); 160 | // } 161 | // } 162 | 163 | pub async fn auth_user_list_page(&self, arg: &RoleAuthUserPageDTO) -> Result> { 164 | let res = db_auth_user_list(pool!(), &arg).await?; 165 | let mut sys_users = vec![]; 166 | for user in res { 167 | sys_users.push(SysUserVO::from(user)); 168 | } 169 | Ok(sys_users) 170 | } 171 | pub async fn unallocated_user_list_page(&self, arg: &RoleAuthUserPageDTO) -> Result> { 172 | let res = db_unallocated_user_list(pool!(), &arg).await?; 173 | let mut sys_users = vec![]; 174 | for user in res { 175 | sys_users.push(SysUserVO::from(user)); 176 | } 177 | Ok(sys_users) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/service/sys_sms_service.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::Sms; 2 | use crate::error::{Error, Result}; 3 | use crate::service::CONTEXT; 4 | use std::collections::HashMap; 5 | 6 | pub struct SysSmsService {} 7 | 8 | impl SysSmsService { 9 | ///Send verification code 10 | pub async fn send_verify_sms(&self, user_name: &str, sms_code: &str) -> Result<()> { 11 | let mut templete_arg = HashMap::new(); 12 | //短信类型:验证码 13 | templete_arg.insert("sms_type".to_string(), "verify_sms".to_string()); 14 | //验证码值 15 | templete_arg.insert("sms_code".to_string(), sms_code.to_string()); 16 | let r = CONTEXT 17 | .cache_service 18 | .set_json( 19 | &format!("{},{}", CONTEXT.config.sms_cache_send_key_prefix, user_name), 20 | &Sms { 21 | user_name: user_name.to_string(), 22 | args: templete_arg, 23 | }, 24 | ) 25 | .await?; 26 | return Ok(()); 27 | } 28 | 29 | ///Verifying verification code 30 | pub async fn do_verify_sms(&self, user_name: &str, sms_code: &str) -> Result { 31 | let sms: Option = CONTEXT 32 | .cache_service 33 | .get_json(&format!( 34 | "{},{}", 35 | CONTEXT.config.sms_cache_send_key_prefix, user_name 36 | )) 37 | .await?; 38 | return match sms { 39 | Some(v) => { 40 | let sms_code_cached = v.args.get("sms_code"); 41 | Ok(sms_code_cached.eq(&Some(&sms_code.to_string()))) 42 | } 43 | _ => Err(Error::from("请发送验证码!")), 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/service/sys_trash_service.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::table::SysTrash; 2 | use crate::pool; 3 | use rbatis::object_id::ObjectId; 4 | use rbatis::rbdc::datetime::DateTime; 5 | use rbatis::rbdc::Error; 6 | use serde::Serialize; 7 | 8 | /// A trash can service that can recycle data. Retrieve the data, display the trash can data 9 | pub struct SysTrashService {} 10 | 11 | impl SysTrashService { 12 | pub async fn add(&self, table_name: &str, args: &[T]) -> Result 13 | where 14 | T: Serialize, 15 | { 16 | if args.is_empty() { 17 | return Ok(0); 18 | } 19 | //copy data to trash 20 | let mut trashes = Vec::with_capacity(args.len()); 21 | for x in args { 22 | trashes.push(SysTrash { 23 | id: Some(ObjectId::new().to_string().into()), 24 | table_name: Some(table_name.to_string()), 25 | data: Some(serde_json::to_string(x).unwrap_or_default()), 26 | create_time: Some(DateTime::now()), 27 | }); 28 | } 29 | Ok(SysTrash::insert_batch(pool!(), &trashes, 20) 30 | .await? 31 | .rows_affected) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/token_auth/mod.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{HttpRequest}; 2 | use crate::config::cache_variables::LOGIN_TOKEN_KEY; 3 | use crate::config::global_variables::ADMIN_NAME; 4 | use crate::domain::vo::{JWTToken, RespVO, UserCache}; 5 | use crate::error::Error; 6 | use crate::service::CONTEXT; 7 | 8 | pub const TOKEN_PREFIX: &'static str = "Bearer "; 9 | 10 | pub fn get_token(req: &HttpRequest) -> String { 11 | let mut token = req 12 | .headers() 13 | .get("Authorization") 14 | .map(|v| v.to_str().unwrap_or_default().to_string()) 15 | .unwrap_or_default(); 16 | if token.starts_with(TOKEN_PREFIX) { 17 | token = token.replace(TOKEN_PREFIX, ""); 18 | } 19 | token 20 | } 21 | 22 | 23 | ///Whether the interface is in the whitelist 24 | pub fn is_white_list_api(path: &str) -> bool { 25 | if path.eq("/") { 26 | return true; 27 | } 28 | for x in &CONTEXT.config.white_list_api { 29 | if x.contains(path) { 30 | return true; 31 | } 32 | } 33 | return false; 34 | } 35 | 36 | ///Check whether the token_auth is valid and has not expired 37 | pub async fn checked_token(token: &str) -> Result { 38 | //check token_auth alive 39 | let claims = JWTToken::verify(&CONTEXT.config.jwt_secret, token); 40 | match claims { 41 | Ok(c) => { 42 | let key = format!("{}{}", LOGIN_TOKEN_KEY, c.login_user_key); 43 | let user_cache: Result = CONTEXT.cache_service.get_json(&key).await; 44 | match user_cache { 45 | Ok(u) => { 46 | CONTEXT.cache_service.expire(&key, (CONTEXT.config.token_expired_min * 60) as i32).await; 47 | Ok(u) 48 | } 49 | Err(e) => { 50 | Err(e) 51 | } 52 | } 53 | } 54 | Err(e) => { 55 | return Err(crate::error::Error::from(e.to_string())); 56 | } 57 | } 58 | } 59 | 60 | ///Permission to check 61 | /// permit_str支持与非 如sys:user:list||sys:user:delete,暂时不实现,只支持一个权限 62 | pub async fn check_auth(user_cache: &UserCache, permit_str: &str) -> Result<(), Error> { 63 | let permit_str = permit_str.replace("\"", ""); 64 | // println!("permit_str{}", permit_str); 65 | if permit_str.len() == 0 { 66 | return Ok(()); 67 | } 68 | if user_cache.user_name == ADMIN_NAME { return Ok(()); } 69 | 70 | let sys_menu = CONTEXT.sys_menu_service.all().await?; 71 | //权限校验 72 | for cache_permission in &user_cache.permissions { 73 | if cache_permission.eq(&permit_str) { 74 | return Ok(()); 75 | } 76 | } 77 | return Err(crate::error::Error::from(format!("无权限访问{}", permit_str))); 78 | } 79 | 80 | 81 | // 82 | pub async fn check_permit(req: HttpRequest, permit_str: &str) -> Option> { 83 | let token = get_token(&req); 84 | let path = req.path().to_string(); 85 | if is_white_list_api(&path) { 86 | return None; 87 | } 88 | match checked_token(&token).await { 89 | Ok(data) => { 90 | match check_auth(&data, permit_str).await { 91 | Ok(_) => { 92 | //刷新过期时间 93 | crate::web_data::set_user_name(data.user_name); 94 | } 95 | Err(e) => { 96 | //仅提示拦截 97 | let resp: RespVO = RespVO { 98 | code: 500, 99 | msg: Some(e.to_string()), 100 | data: None, 101 | }; 102 | return Some(resp); 103 | // return Ok(req.into_response(resp.resp_json())); 104 | } 105 | } 106 | } 107 | Err(e) => { 108 | //401 http状态码会强制前端退出当前登陆状态 109 | let resp: RespVO = RespVO { 110 | code: 401, 111 | msg: Some(format!("Unauthorized for:{}", e.to_string())), 112 | data: None, 113 | }; 114 | return Some(resp); 115 | } 116 | } 117 | None 118 | } 119 | -------------------------------------------------------------------------------- /src/util/base64.rs: -------------------------------------------------------------------------------- 1 | pub fn encode(bytes: &[u8], size: usize) -> String { 2 | let mut buf = String::new(); 3 | let i = 0; 4 | for mut i in 0..(size / 3) { 5 | i = i * 3; 6 | let f = bytes[i]; 7 | let s = bytes[i + 1]; 8 | let t = bytes[i + 2]; 9 | 10 | buf.push_str(&cvt((f & 0xfc) >> 2)); 11 | buf.push_str(&cvt((f & 0x03) << 4 | ((s & 0xf0) >> 4))); 12 | buf.push_str(&cvt((s & 0x0f) << 2 | ((t & 0xc0) >> 6))); 13 | buf.push_str(&cvt(t & 0x3f)); 14 | } 15 | 16 | let mut i = (i + 1) * 3; 17 | i = if size < i { 0 } else { i }; 18 | let remain = size - i; 19 | if remain == 1 { 20 | let f = bytes[i]; 21 | buf.push_str(&cvt((f & 0xfc) >> 2)); 22 | buf.push_str(&cvt((f & 0x03) << 4 | 0)); 23 | buf.push_str("=="); 24 | } else if remain == 2 { 25 | let f = bytes[i]; 26 | let s = bytes[i + 1]; 27 | buf.push_str(&cvt((f & 0xfc) >> 2)); 28 | buf.push_str(&cvt((f & 0x03) << 4 | ((s & 0xf0) >> 4))); 29 | buf.push_str(&cvt((s & 0x0f) << 2 | 0)); 30 | buf.push_str("="); 31 | } 32 | buf 33 | } 34 | 35 | const BASE64_TABLE: [char; 64] = [ 36 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 37 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 38 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', 39 | '5', '6', '7', '8', '9', '+', '/', 40 | ]; 41 | 42 | fn cvt(i: u8) -> String { 43 | BASE64_TABLE.get(i as usize).unwrap().to_string() 44 | } 45 | 46 | #[cfg(test)] 47 | mod test { 48 | use crate::util::base64::encode; 49 | #[test] 50 | fn test_encode() { 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/util/bencher.rs: -------------------------------------------------------------------------------- 1 | pub trait QPS { 2 | fn qps(&self, total: u64); 3 | fn time(&self, total: u64); 4 | fn cost(&self); 5 | } 6 | 7 | impl QPS for std::time::Instant { 8 | fn qps(&self, total: u64) { 9 | let time = self.elapsed(); 10 | println!( 11 | "use QPS: {} QPS/s", 12 | (total as u128 * 1000000000 as u128 / time.as_nanos() as u128) 13 | ); 14 | } 15 | 16 | fn time(&self, total: u64) { 17 | let time = self.elapsed(); 18 | println!( 19 | "use Time: {:?} ,each:{} ns/op", 20 | &time, 21 | time.as_nanos() / (total as u128) 22 | ); 23 | } 24 | 25 | fn cost(&self) { 26 | let time = self.elapsed(); 27 | println!("cost:{:?}", time); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bencher; 2 | pub mod options; 3 | pub mod password_encoder; 4 | pub mod string; 5 | pub mod base64; 6 | pub mod web_utils; 7 | pub mod hardware; 8 | -------------------------------------------------------------------------------- /src/util/options.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::Lazy; 2 | 3 | pub trait OptionStringRefUnwrapOrDefault { 4 | fn unwrap_or_def(&self) -> T; 5 | } 6 | 7 | static EMPTY_STR: Lazy = Lazy::new(|| String::new()); 8 | 9 | impl<'a> OptionStringRefUnwrapOrDefault<&'a String> for Option<&'a String> { 10 | fn unwrap_or_def(&self) -> &'a String { 11 | return match self { 12 | None => &EMPTY_STR, 13 | Some(v) => v, 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/util/password_encoder.rs: -------------------------------------------------------------------------------- 1 | use bcrypt::{DEFAULT_COST, hash, verify}; 2 | 3 | pub struct PasswordEncoder {} 4 | 5 | impl PasswordEncoder { 6 | pub fn encode(raw_password: &str) -> String { 7 | hash(raw_password,DEFAULT_COST).unwrap() 8 | } 9 | pub fn verify(password: &str, raw_password: &str) -> bool { 10 | verify(raw_password, password).unwrap() 11 | } 12 | } 13 | 14 | #[cfg(test)] 15 | mod test { 16 | use crate::util::password_encoder::PasswordEncoder; 17 | 18 | #[test] 19 | fn test_encode() { 20 | let s = PasswordEncoder::encode("123456"); 21 | println!("{}", s); 22 | assert_eq!( 23 | PasswordEncoder::encode("123456"), 24 | PasswordEncoder::encode("123456") 25 | ) 26 | } 27 | 28 | #[test] 29 | fn test_verify() { 30 | let password = "12345"; 31 | let raw_password = "12345"; 32 | 33 | assert!(PasswordEncoder::verify(password, raw_password)); 34 | 35 | let encode_password = PasswordEncoder::encode(password); 36 | assert!(PasswordEncoder::verify(&encode_password, password)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/util/string.rs: -------------------------------------------------------------------------------- 1 | pub trait IsEmptyString { 2 | fn is_empty(&self) -> bool; 3 | } 4 | 5 | impl IsEmptyString for Option { 6 | fn is_empty(&self) -> bool { 7 | return match self { 8 | Some(s) => s.is_empty(), 9 | _ => true, 10 | }; 11 | } 12 | } 13 | 14 | impl IsEmptyString for Option<&str> { 15 | fn is_empty(&self) -> bool { 16 | return match self { 17 | Some(s) => s.is_empty(), 18 | _ => true, 19 | }; 20 | } 21 | } 22 | pub fn capitalize(s: &str) -> String { 23 | 24 | let mut c = s.chars(); 25 | 26 | match c.next(){ 27 | Some(f) => f.to_uppercase().collect::() + c.as_str(), 28 | None => String::new(), 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/util/web_utils.rs: -------------------------------------------------------------------------------- 1 | use actix_http::header::{HeaderMap, USER_AGENT}; 2 | use actix_web::{HttpRequest}; 3 | use rbatis::object_id::ObjectId; 4 | use rbatis::rbdc::datetime::DateTime; 5 | use user_agent_parser::UserAgentParser; 6 | 7 | use crate::domain::table::SysLogininfor; 8 | 9 | // //将actix 上传保存在本地磁盘 10 | // pub async fn save_http_files(mut payload: Multipart) -> Result, Error> { 11 | // // iterate over multipart stream 12 | // let mut files = vec![]; 13 | // while let Some(mut field) = payload.try_next().await? { 14 | // // A multipart/form-data stream has to contain `content_disposition` 15 | // let content_disposition = field.content_disposition(); 16 | // let filename = content_disposition 17 | // .get_filename().unwrap().to_string(); 18 | // let filepath = format!("./tmp/{}", Uuid::new_v4().to_string()); 19 | // let filepath_ = filepath.clone(); 20 | // // File::create is blocking operation, use threadpool 21 | // let mut f = web::block(|| std::fs::File::create(filepath)).await??; 22 | // // Field in turn is stream of *Bytes* object 23 | // while let Some(chunk) = field.try_next().await? { 24 | // // filesystem operations are blocking, we have to use threadpool 25 | // f = web::block(move || f.write_all(&chunk).map(|_| f)).await??; 26 | // } 27 | // files.push((filepath_, filename)); 28 | // } 29 | // Ok(files) 30 | // } 31 | 32 | fn get_header_value(headers: &HeaderMap, head: &str) -> String { 33 | if headers.get(head).is_some() { 34 | headers.get(head).unwrap().to_str().unwrap_or("").to_string() 35 | } else { 36 | "".to_string() 37 | } 38 | } 39 | 40 | pub fn get_ip_addr(req: &HttpRequest) -> String 41 | { 42 | let headers = req.headers(); 43 | let mut ip = get_header_value(headers, "x-forwarded-for"); 44 | if ip.len() == 0 { ip = get_header_value(headers, "Proxy-Client-IP") } 45 | if ip.len() == 0 { ip = get_header_value(headers, "X-Forwarded-For") } 46 | if ip.len() == 0 { ip = get_header_value(headers, "WL-Proxy-Client-IP") } 47 | if ip.len() == 0 { ip = get_header_value(headers, "X-Real-IP") } 48 | if ip.len() == 0 { 49 | return req.peer_addr().unwrap().ip().to_string(); 50 | } 51 | ip.to_string() 52 | //return "0:0:0:0:0:0:0:1".equals(ip)?; 53 | // "127.0.0.1": getMultistageReverseProxyIp(ip); 54 | } 55 | 56 | pub fn build_logininfor(req: &HttpRequest,username:String,status:char,msg:String) ->SysLogininfor{ 57 | let ua_parser = UserAgentParser::from_path("./user_agent.yml").unwrap(); 58 | let user_agent = req.headers().get(USER_AGENT).unwrap().to_str().unwrap_or(""); 59 | let os = ua_parser.parse_os(user_agent).name.unwrap().to_string(); 60 | SysLogininfor { 61 | info_id: ObjectId::new().to_string().into(), 62 | user_name: Some(username), 63 | ipaddr: Some(crate::util::web_utils::get_ip_addr(&req)), 64 | login_location: None, 65 | browser: None, 66 | os: Some(os), 67 | status: Some(status), 68 | msg: Some(msg), 69 | login_time: DateTime::now().set_micro(0).into(), 70 | } 71 | } 72 | 73 | // pub(crate) fn timestamp() -> i64 { 74 | // let start = SystemTime::now(); 75 | // let since_the_epoch = start 76 | // .duration_since(UNIX_EPOCH) 77 | // .expect("Time went backwards"); 78 | // let ms = since_the_epoch.as_secs() as i64 * 1000i64 + (since_the_epoch.subsec_nanos() as f64 / 1_000_000.0) as i64; 79 | // ms 80 | // } -------------------------------------------------------------------------------- /src/web_data.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | 4 | //线程安全的变量,方便会话调用 5 | thread_local!(static USERNAME: RefCell = RefCell::new("".to_string())); 6 | 7 | 8 | 9 | 10 | pub fn set_user_name(user_name: String) { 11 | USERNAME.with(|f| { 12 | *f.borrow_mut() = user_name; 13 | }); 14 | } 15 | 16 | pub fn get_user_name() -> String { 17 | // we retain our original value of 2 despite the child thread 18 | let mut user_name = "".to_string(); 19 | USERNAME.with(|f| { 20 | user_name = f.borrow().to_string() 21 | }); 22 | user_name 23 | } 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /user_agent.yml: -------------------------------------------------------------------------------- 1 | user_agent_parsers: 2 | - regex: '(ESPN)[%20| ]+Radio/(\d+)\.(\d+)\.(\d+) CFNetwork' 3 | - regex: '(Namoroka|Shiretoko|Minefield)/(\d+)\.(\d+)\.(\d+(?:pre|))' 4 | family_replacement: 'Firefox ($1)' 5 | - regex: '(Android) Eclair' 6 | v1_replacement: '2' 7 | v2_replacement: '1' 8 | 9 | os_parsers: 10 | - regex: 'Win(?:dows)? ?(95|98|3.1|NT|ME|2000|XP|Vista|7|CE)' 11 | os_replacement: 'Windows' 12 | os_v1_replacement: '$1' 13 | 14 | device_parsers: 15 | - regex: '\bSmartWatch *\( *([^;]+) *; *([^;]+) *;' 16 | device_replacement: '$1 $2' 17 | brand_replacement: '$1' 18 | model_replacement: '$2' --------------------------------------------------------------------------------