1use crate::request::{ContentType, Method, Request};
2use crate::tables::Tables;
3use crate::{ApiResponse, Tools};
4use crate::{CONFIG, GLOBAL_DATA, PLUGIN_TOOLS};
5use br_fields::Field;
6use json::{object, JsonValue};
7use std::any::type_name;
8
9pub trait Action {
11 fn _name(&self) -> String {
13 type_name::<Self>()
14 .rsplit("::")
15 .next()
16 .unwrap()
17 .to_lowercase()
18 }
19 fn module_name(&self) -> String {
21 let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
22 let plugin = t[2].to_lowercase();
23 let module = t[3].to_lowercase();
24 format!("{plugin}.{module}")
25 }
26 fn addon_name(&self) -> String {
28 let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
29 t[2].to_lowercase()
30 }
31 fn api(&self) -> String {
33 let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
34 let plugin = t[2].to_lowercase();
35 let module = t[3].to_lowercase();
36 let action = t[4].to_lowercase();
37 format!("{plugin}.{module}.{action}")
38 }
39 fn token(&self) -> bool {
41 true
42 }
43
44 fn sort(&self) -> usize {
45 99
46 }
47 fn title(&self) -> &'static str;
49 fn description(&self) -> &'static str {
51 ""
52 }
53 fn path(&self) -> &'static str {
55 ""
56 }
57 fn query(&self) -> JsonValue {
59 object! {}
60 }
61 fn tags(&self) -> &'static [&'static str] {
63 &[]
64 }
65 fn icon(&self) -> &'static str {
66 ""
67 }
68 fn public(&self) -> bool {
70 true
71 }
72 fn auth(&self) -> bool {
74 true
75 }
76 fn interface_type(&self) -> InterfaceType {
78 InterfaceType::API
79 }
80 fn method(&mut self) -> Method {
82 Method::Post
83 }
84 fn content_type(&mut self) -> ContentType {
86 ContentType::Json
87 }
88 fn params_check(&mut self) -> bool {
90 true
91 }
92 fn params(&mut self) -> JsonValue {
94 object! {}
95 }
96 fn success(&mut self) -> ApiResponse {
98 let mut data = object! {};
99 data["code"] = br_fields::int::Int::new(true, "code", "编号", 10, 0)
100 .example(0.into())
101 .swagger();
102 data["message"] = br_fields::str::Str::new(true, "message", "成功消息", 256, "")
103 .example("成功".into())
104 .swagger();
105 data["data"] = br_fields::text::Json::new(true, "data", "返回数据", object! {}).swagger();
106 data["success"] = br_fields::int::Switch::new(true, "success", "成功状态", true)
107 .example(true.into())
108 .swagger();
109 data["timestamp"] =
110 br_fields::datetime::Timestamp::new(true, "timestamp", "时间戳", 0, 0.0).swagger();
111 ApiResponse::success(data, "请求成功")
112 }
113 fn error(&mut self) -> ApiResponse {
115 let mut data = object! {};
116 data["code"] = br_fields::int::Int::new(true, "code", "编号", 10, 1000)
117 .example(1000.into())
118 .swagger();
119 data["message"] = br_fields::str::Str::new(true, "message", "错误消息", 256, "")
120 .example("失败".into())
121 .swagger();
122 data["data"] = br_fields::text::Json::new(true, "data", "返回数据", object! {}).swagger();
123 data["success"] = br_fields::int::Switch::new(true, "success", "成功状态", false)
124 .example(false.into())
125 .swagger();
126 data["timestamp"] =
127 br_fields::datetime::Timestamp::new(true, "timestamp", "时间戳", 0, 0.0).swagger();
128 ApiResponse::error(data, "请求失败")
129 }
130 fn run(&mut self, mut request: Request) -> Result<ApiResponse, ApiResponse> {
132 if self.public()
133 && !self.method().str().is_empty()
134 && self.method().str().to_lowercase() != request.method.str().to_lowercase()
135 {
136 return Err(ApiResponse::fail(
137 -1,
138 format!(
139 "Request type error: Actual [{}] Expected [{}]",
140 request.method.str(),
141 self.method().str()
142 )
143 .as_str(),
144 ));
145 }
146 let params = self.params().clone();
147 if self.params_check() {
148 self.check(&mut request.query.clone(), self.query().clone())?;
149 self.check(&mut request.body, params)?;
150 }
151 let res = self.index(request.clone());
152 match res.success {
153 true => Ok(res),
154 false => Err(res),
155 }
156 }
157 fn check(&mut self, request: &mut JsonValue, params: JsonValue) -> Result<(), ApiResponse> {
159 let req = request.clone();
160 for (name, _) in req.entries() {
161 if !params.has_key(name) {
162 request.remove(name);
163 }
164 }
165 for (name, field) in params.entries() {
166 let require = field["require"].as_bool().unwrap_or(false);
167 let title = field["title"].as_str().unwrap_or("");
168 if request.has_key(name) {
169 match field["mode"].as_str().unwrap() {
171 "key" => {
172 if !request[name].is_string() {
173 return Err(ApiResponse::fail(
174 900_001,
175 format!(
176 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
177 name, field["mode"]
178 )
179 .as_str(),
180 ));
181 }
182 if require && request[name].is_empty() {
183 return Err(ApiResponse::fail(
184 900014,
185 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
186 ));
187 }
188 }
189 "text" | "table" | "tree" => {
190 if !request[name].is_string() {
191 return Err(ApiResponse::fail(
192 900002,
193 format!(
194 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
195 name, field["mode"]
196 )
197 .as_str(),
198 ));
199 }
200 if require && request[name].is_empty() {
201 return Err(ApiResponse::fail(
202 900002,
203 format!("{title} 必填").as_str(),
204 ));
205 }
206 }
207 "file" => {
208 if !request[name].is_array() && !request[name].is_string() {
209 return Err(ApiResponse::fail(
210 900003,
211 format!("参数 [{}] 数据类型应为[{}]", name, field["mode"]).as_str(),
212 ));
213 }
214 if require && request[name].is_empty() {
215 return Err(ApiResponse::fail(
216 900002,
217 format!("{title} 必填").as_str(),
218 ));
219 }
220 }
221 "int" => {
222 if require && request[name].to_string().is_empty() {
223 return Err(ApiResponse::fail(
224 900_002,
225 format!("{title} 必填").as_str(),
226 ));
227 }
228 if !request[name].to_string().is_empty() {
229 match request[name].to_string().parse::<i64>() {
230 Ok(_) => {}
231 Err(_) => {
232 return Err(ApiResponse::fail(
233 900013,
234 format!(
235 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
236 name, field["mode"]
237 )
238 .as_str(),
239 ));
240 }
241 }
242 }
243 }
244 "timestamp" | "yearmonth" => {
245 if require && request[name].to_string().is_empty() {
246 return Err(ApiResponse::fail(
247 900002,
248 format!("{title} 必填").as_str(),
249 ));
250 }
251 if !request[name].to_string().is_empty() {
252 if request[name].is_array() && request[name].len() == 2 {
253 for item in request[name].members() {
255 match item.to_string().parse::<f64>() {
256 Ok(_) => {}
257 Err(_) => {
258 return Err(ApiResponse::fail(
259 900_013,
260 format!(
261 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
262 name, field["mode"]
263 ).as_str(),
264 ));
265 }
266 }
267 }
268 } else {
269 match request[name].to_string().parse::<f64>() {
270 Ok(_) => {}
271 Err(_) => {
272 return Err(ApiResponse::fail(
273 900013,
274 format!(
275 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
276 name, field["mode"]
277 )
278 .as_str(),
279 ));
280 }
281 }
282 }
283 }
284 }
285 "float" => {
286 if require && request[name].to_string().is_empty() {
287 return Err(ApiResponse::fail(
288 900002,
289 format!("{title} 必填").as_str(),
290 ));
291 }
292 if !request[name].to_string().is_empty() {
293 match request[name].to_string().parse::<f64>() {
294 Ok(_) => {}
295 Err(_) => {
296 return Err(ApiResponse::fail(
297 900023,
298 format!(
299 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
300 name, field["mode"]
301 )
302 .as_str(),
303 ));
304 }
305 }
306 }
307 }
308 "string" | "url" | "time" | "code" | "pass" | "email" | "location"
309 | "color" | "date" | "barcode" | "datetime" | "editor" | "tel" => {
310 if !request[name].is_string() {
311 return Err(ApiResponse::fail(
312 -1,
313 format!(
314 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
315 name, field["mode"]
316 )
317 .as_str(),
318 ));
319 }
320 if require && request[name].is_empty() {
321 return Err(ApiResponse::fail(
322 900004,
323 format!("{title} 必填").as_str(),
324 ));
325 }
326 }
327 "dict" => {
328 if require && request[name].is_empty() {
329 return Err(ApiResponse::fail(
330 900005,
331 format!("{title} 必填").as_str(),
332 ));
333 }
334 }
335 "switch" => match request[name].to_string().parse::<bool>() {
336 Ok(e) => {
337 request[name] = e.into();
338 }
339 Err(_) => {
340 return Err(ApiResponse::fail(
341 -1,
342 format!(
343 "请求参数数据类型错误: 参数 [{name}] 数据类型应为[{}]",
344 field["mode"]
345 )
346 .as_str(),
347 ));
348 }
349 },
350 "select" => {
351 if !request[name].is_array() && !request[name].is_string() {
352 return Err(ApiResponse::fail(
353 -1,
354 format!(
355 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
356 name, field["mode"]
357 )
358 .as_str(),
359 ));
360 }
361 let value = if request[name].is_array() {
362 request[name]
363 .members()
364 .map(|m| m.to_string())
365 .collect::<Vec<String>>()
366 } else {
367 request[name]
368 .to_string()
369 .split(",")
370 .map(|x| x.to_string())
371 .collect::<Vec<String>>()
372 };
373 let option = field["option"]
374 .members()
375 .map(|m| m.as_str().unwrap_or(""))
376 .collect::<Vec<&str>>();
377
378 let require = field["require"].as_bool().unwrap();
379 for item in value.clone() {
380 if !option.contains(&&*item.clone()) && (item.is_empty() && require) {
381 let option = field["option"]
382 .members()
383 .map(|m| m.as_str().unwrap_or(""))
384 .collect::<Vec<&str>>()
385 .join(",");
386 return Err(ApiResponse::fail(-1, format!("请求参数选项错误: 参数 [{item}] 数据类型应为[{option}]之内").as_str()));
387 }
388 }
389 request[name] = value.into();
390 }
391 "radio" => {
392 if !request[name].is_string() {
393 return Err(ApiResponse::fail(
394 -1,
395 format!(
396 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}] 实际为[{}]",
397 name, field["mode"], request[name]
398 )
399 .as_str(),
400 ));
401 }
402 let option = field["option"]
403 .members()
404 .map(|m| m.as_str().unwrap_or(""))
405 .collect::<Vec<&str>>()
406 .join(",");
407 if request[name].is_string()
408 && !field["option"].contains(request[name].as_str().unwrap_or(""))
409 {
410 return Err(ApiResponse::fail(
411 -1,
412 format!(
413 "请求参数选项错误: 参数 [{}] 数据 [{}] 应为 [{}] 之一",
414 name, request[name], option
415 )
416 .as_str(),
417 ));
418 }
419 }
420 "array" | "polygon" => {
421 if !request[name].is_array() {
422 return Err(ApiResponse::fail(
423 900_009,
424 format!(
425 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
426 name, field["mode"]
427 )
428 .as_str(),
429 ));
430 }
431 if require && request[name].is_empty() {
432 return Err(ApiResponse::fail(
433 900010,
434 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
435 ));
436 }
437 }
438 "object" => {
439 if !request[name].is_object() {
440 return Err(ApiResponse::fail(
441 900009,
442 format!(
443 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
444 name, field["mode"]
445 )
446 .as_str(),
447 ));
448 }
449 if require && request[name].is_empty() {
450 return Err(ApiResponse::fail(
451 900006,
452 format!("{title} 必填").as_str(),
453 ));
454 }
455 }
456 _ => {
457 println!("检查未知类型: {}", field["mode"])
458 }
459 }
460 } else {
461 if require {
462 return Err(ApiResponse::fail(900007, format!("{title} 必填").as_str()));
463 }
464 request[name] = field["def"].clone();
465 }
466 }
467 Ok(())
468 }
469 fn index(&mut self, request: Request) -> ApiResponse;
471 #[cfg(any(
472 feature = "sqlite",
473 feature = "mssql",
474 feature = "mysql",
475 feature = "pgsql"
476 ))]
477 fn table_select(
478 &mut self,
479 request: JsonValue,
480 table_name: &str,
481 fields: Vec<&str>,
482 ) -> JsonValue {
483 self.table()
484 .main_select_fields(table_name, fields)
485 .params(request.clone())
486 .get_table_select()
487 }
488
489 #[allow(clippy::too_many_arguments)]
497 #[cfg(any(
498 feature = "sqlite",
499 feature = "mssql",
500 feature = "mysql",
501 feature = "pgsql"
502 ))]
503 fn table_list(
504 &mut self,
505 request: JsonValue,
506 table_name: &str,
507 fields: JsonValue,
508 hidd_field: Vec<&str>,
509 show_field: Vec<&str>,
510 search_fields: Vec<&str>,
511 filter_fields: Vec<&str>,
512 ) -> JsonValue {
513 self.table()
514 .main_table_fields(table_name, fields, hidd_field, show_field)
515 .search_fields(search_fields)
516 .filter_fields(filter_fields)
517 .params(request)
518 .get_table()
519 }
520 #[allow(clippy::too_many_arguments)]
521 #[cfg(any(
522 feature = "sqlite",
523 feature = "mssql",
524 feature = "mysql",
525 feature = "pgsql"
526 ))]
527 fn table(&mut self) -> Tables {
528 Tables::new(self.tools().db.clone())
529 }
530
531 fn tools(&mut self) -> Tools {
533 let tools = PLUGIN_TOOLS.lock().unwrap();
534 let tools = tools.get("tools").unwrap().clone();
535 tools
536 }
537 fn config(&mut self, name: &str) -> JsonValue {
539 if CONFIG.lock().unwrap().get(name).is_none() {
540 object! {}
541 } else {
542 CONFIG.lock().unwrap().get(name).unwrap().clone()
543 }
544 }
545 fn btn(&mut self) -> Btn {
548 let mut btn = Btn::new(self.api().as_str());
549 btn.fields(self.params().clone());
550 btn.tags(self.tags());
551 btn.icon(self.icon());
552 btn.desc(self.description());
553 btn.auth(self.auth());
554 btn.public(self.public());
555 btn.title(self.title());
556 btn.btn_type(BtnType::Api);
557 btn.btn_color(BtnColor::Primary);
558 btn.path(self.api().clone().replace(".", "/").as_str());
559 btn.pass(false);
560 btn.addon();
561 btn
562 }
563 fn set_global_data(&mut self, key: &str, value: JsonValue) {
565 GLOBAL_DATA.with(|data| {
566 data.borrow_mut()[key] = value;
567 });
568 }
569 fn get_global_data(&mut self) -> JsonValue {
571 GLOBAL_DATA.with(|data| data.borrow().clone())
572 }
573 fn get_global_data_key(&mut self, key: &str) -> JsonValue {
575 GLOBAL_DATA.with(|data| data.borrow()[key].clone())
576 }
577}
578#[derive(Debug, Clone)]
579pub struct Btn {
580 api: String,
581 title: String,
582 desc: String,
583 tags: &'static [&'static str],
584 auth: bool,
585 public: bool,
586 btn_type: BtnType,
587 color: BtnColor,
588 icon: String,
589 cnd: Vec<JsonValue>,
590 url: String,
591 path: String,
592 fields: JsonValue,
593 addon: String,
594 version: usize,
595 pass: bool,
597}
598impl Btn {
599 pub fn new(api: &str) -> Self {
600 Self {
601 api: api.to_string(),
602 title: "".to_string(),
603 desc: "".to_string(),
604 btn_type: BtnType::Api,
605 color: BtnColor::Primary,
606 icon: "".to_string(),
607 auth: false,
608 public: false,
609 cnd: vec![],
610 url: "".to_string(),
611 path: "".to_string(),
612 fields: object! {},
613 tags: &[],
614 pass: false,
615 addon: "".to_string(),
616 version: 0,
617 }
618 }
619
620 pub fn addon(&mut self) -> &mut Self {
621 self.addon = self.api.split('.').nth(0).unwrap().to_string();
622 self
623 }
624 pub fn path(&mut self, path: &str) -> &mut Self {
625 self.path = path.to_string();
626 self
627 }
628 pub fn cnd(&mut self, cnd: Vec<JsonValue>) -> &mut Self {
629 self.cnd = cnd;
630 self
631 }
632 pub fn version(&mut self, version: usize) -> &mut Self {
633 self.version = version;
634 self
635 }
636 pub fn btn_type(&mut self, btn_type: BtnType) -> &mut Self {
637 self.btn_type = btn_type;
638 self
639 }
640 pub fn btn_color(&mut self, btn_color: BtnColor) -> &mut Self {
641 self.color = btn_color;
642 self
643 }
644 pub fn fields(&mut self, fields: JsonValue) -> &mut Self {
645 self.fields = fields;
646 self
647 }
648 pub fn pass(&mut self, pass: bool) -> &mut Self {
649 self.pass = pass;
650 self
651 }
652 pub fn url(&mut self, url: &str) -> &mut Self {
653 self.url = url.to_string();
654 self
655 }
656 pub fn title(&mut self, title: &str) -> &mut Self {
657 self.title = title.to_string();
658 self
659 }
660 pub fn desc(&mut self, desc: &str) -> &mut Self {
661 self.desc = desc.to_string();
662 self
663 }
664 pub fn tags(&mut self, tags: &'static [&'static str]) -> &mut Self {
665 self.tags = tags;
666 self
667 }
668 pub fn public(&mut self, public: bool) -> &mut Self {
669 self.public = public;
670 self
671 }
672 pub fn auth(&mut self, auth: bool) -> &mut Self {
673 self.auth = auth;
674 self
675 }
676 pub fn icon(&mut self, icon: &str) -> &mut Self {
677 self.icon = icon.to_string();
678 self
679 }
680 pub fn json(&mut self) -> JsonValue {
681 let color = match self.version {
682 0 => self.color.clone().str(),
683 _ => self.color.clone().str_v_1(),
684 };
685 object! {
686 addon:self.addon.to_string() ,
687 api:self.api.clone(),
688 title:self.title.clone(),
689 desc:self.desc.clone(),
690 auth:self.auth,
691 public:self.public,
692 btn_type:self.btn_type.clone().str(),
693 color:color,
694 icon:self.icon.clone(),
695 cnd:self.cnd.clone(),
696 url:self.url.clone(),
697 path:self.path.clone(),
698 fields:self.fields.clone(),
699 tags:self.tags,
700 pass:self.pass,
701 }
702 }
703}
704
705#[derive(Debug, Clone)]
707pub enum InterfaceType {
708 API,
709 BTN,
710 MENU,
711 OPENAPI,
712}
713
714impl InterfaceType {
715 pub fn str(&self) -> &'static str {
716 match self {
717 InterfaceType::API => "api",
718 InterfaceType::BTN => "btn",
719 InterfaceType::MENU => "menu",
720 InterfaceType::OPENAPI => "openapi",
721 }
722 }
723 pub fn types() -> Vec<&'static str> {
724 vec!["api", "btn", "menu", "openapi"]
725 }
726}
727
728#[derive(Debug, Clone)]
730pub enum BtnType {
731 Form,
733 FormDownload,
735 FormCustom,
737 FormData,
738 Url,
740 Api,
742 Download,
744 Path,
746 DialogCustom,
748 FormApiDialogCustom,
750 Preview,
752}
753
754impl BtnType {
755 fn str(self) -> &'static str {
756 match self {
757 BtnType::Form => "form",
758 BtnType::FormDownload => "form_download",
759 BtnType::FormCustom => "form_custom",
760 BtnType::FormData => "form_data",
761 BtnType::Api => "api",
762 BtnType::Download => "download",
763 BtnType::Url => "url",
764 BtnType::Path => "path",
765 BtnType::DialogCustom => "dialog_custom",
766 BtnType::FormApiDialogCustom => "form_api_dialog_custom",
767 BtnType::Preview => "preview",
768 }
769 }
770}
771#[derive(Debug, Clone)]
773pub enum BtnColor {
774 Primary,
775 Red,
776 Yellow,
777 Green,
778}
779
780impl BtnColor {
781 fn str(self) -> &'static str {
782 match self {
783 BtnColor::Primary => "primary",
784 BtnColor::Red => "negative",
785 BtnColor::Yellow => "warning",
786 BtnColor::Green => "positive",
787 }
788 }
789 fn str_v_1(self) -> &'static str {
790 match self {
791 BtnColor::Primary => "normal",
792 BtnColor::Red => "danger",
793 BtnColor::Yellow => "warning",
794 BtnColor::Green => "success",
795 }
796 }
797}
798
799pub struct Dashboard {
800 title: String,
802 data: JsonValue,
804 model: DashboardModel,
806 class: String,
808 icon: String,
810 desc: String,
812 api: String,
814 options: JsonValue,
816}
817impl Dashboard {
818 pub fn new(title: &str) -> Dashboard {
819 Dashboard {
820 title: title.to_string(),
821 data: JsonValue::Null,
822 model: DashboardModel::Number,
823 class: "col-4".to_string(),
824 icon: "".to_string(),
825 desc: "".to_string(),
826 api: "".to_string(),
827 options: JsonValue::Null,
828 }
829 }
830 pub fn options(&mut self, options: JsonValue) -> &mut Self {
831 self.options = options;
832 self
833 }
834 pub fn api(&mut self, api: &str) -> &mut Self {
835 self.api = api.to_string();
836 self
837 }
838 pub fn data(&mut self, data: JsonValue) -> &mut Self {
839 self.data = data;
840 self
841 }
842 pub fn class(&mut self, name: &str) -> &mut Self {
843 self.class = name.to_string();
844 self
845 }
846 pub fn icon(&mut self, name: &str) -> &mut Self {
847 self.icon = name.to_string();
848 self
849 }
850 pub fn model(&mut self, dashboard_model: DashboardModel) -> &mut Self {
851 self.model = dashboard_model;
852 self
853 }
854 pub fn desc(&mut self, desc: &str) -> &mut Self {
855 self.desc = desc.to_string();
856 self
857 }
858 pub fn json(&self) -> JsonValue {
859 object! {
860 title: self.title.clone(),
861 data: self.data.clone(),
862 class: self.class.clone(),
863 icon: self.icon.clone(),
864 model: self.model.str(),
865 desc: self.desc.clone(),
866 api: self.api.clone(),
867 options:self.options.clone(),
868 }
869 }
870}
871
872pub enum DashboardModel {
873 Number,
875 EchartsBar,
877 EchartsStackedBar,
879 EchartsBarRace,
881 EchartsPie,
883 EchartsDoughnut,
885 EchartsStackedLine,
887 EchartsStackedLineArea,
889 EchartsGeoGraph,
891}
892
893impl DashboardModel {
894 pub fn str(&self) -> &'static str {
895 match self {
896 Self::Number => "number",
897 Self::EchartsBar => "echarts-bar",
898 Self::EchartsStackedBar => "echarts-stacked_bar",
899 Self::EchartsBarRace => "echarts-bar_race",
900 Self::EchartsPie => "echarts-pie",
901 Self::EchartsDoughnut => "echarts-doughnut",
902 Self::EchartsStackedLine => "echarts-stacked_line",
903 Self::EchartsStackedLineArea => "echarts-stacked_line_area",
904 Self::EchartsGeoGraph => "echarts-geo_graph",
905 }
906 }
907}