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