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 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();
147 if self.params_check() {
148 self.check(&mut request.query, self.query())?;
149 self.check(&mut request.body, params)?;
150 }
151 let res = self.index(request);
152 if res.success {
153 Ok(res)
154 } else {
155 Err(res)
156 }
157 }
158 fn check(&mut self, request: &mut JsonValue, params: JsonValue) -> Result<(), ApiResponse> {
160 let req = request.clone();
161 for (name, _) in req.entries() {
162 if !params.has_key(name) {
163 request.remove(name);
164 }
165 }
166 for (name, field) in params.entries() {
167 let require = field["require"].as_bool().unwrap_or(false);
168 let title = field["title"].as_str().unwrap_or("");
169 if request.has_key(name) {
170 match field["mode"].as_str().unwrap_or("text") {
172 "key" => {
173 if !request[name].is_string() {
174 return Err(ApiResponse::fail(
175 900_001,
176 format!(
177 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
178 name, field["mode"]
179 )
180 .as_str(),
181 ));
182 }
183 if require && request[name].is_empty() {
184 return Err(ApiResponse::fail(
185 900_014,
186 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
187 ));
188 }
189 }
190 "text" | "table" | "tree" => {
191 if !request[name].is_string() {
192 return Err(ApiResponse::fail(
193 900_002,
194 format!(
195 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
196 name, field["mode"]
197 )
198 .as_str(),
199 ));
200 }
201 if require && request[name].is_empty() {
202 return Err(ApiResponse::fail(
203 900_002,
204 format!("{title} 必填").as_str(),
205 ));
206 }
207 }
208 "file" => {
209 if !request[name].is_array() && !request[name].is_string() {
210 return Err(ApiResponse::fail(
211 900_003,
212 format!("参数 [{}] 数据类型应为[{}]", name, field["mode"]).as_str(),
213 ));
214 }
215 if require && request[name].is_empty() {
216 return Err(ApiResponse::fail(
217 900_002,
218 format!("{title} 必填").as_str(),
219 ));
220 }
221 }
222 "int" => {
223 if require && request[name].to_string().is_empty() {
224 return Err(ApiResponse::fail(
225 900_002,
226 format!("{title} 必填").as_str(),
227 ));
228 }
229 if !request[name].to_string().is_empty() {
230 match request[name].to_string().parse::<i64>() {
231 Ok(_) => {}
232 Err(_) => {
233 return Err(ApiResponse::fail(
234 900_013,
235 format!(
236 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
237 name, field["mode"]
238 )
239 .as_str(),
240 ));
241 }
242 }
243 }
244 }
245 "timestamp" | "yearmonth" => {
246 if require && request[name].to_string().is_empty() {
247 return Err(ApiResponse::fail(
248 900_002,
249 format!("{title} 必填").as_str(),
250 ));
251 }
252 if !request[name].to_string().is_empty() {
253 if request[name].is_array() && request[name].len() == 2 {
254 for item in request[name].members() {
256 match item.to_string().parse::<f64>() {
257 Ok(_) => {}
258 Err(_) => {
259 return Err(ApiResponse::fail(
260 900_013,
261 format!(
262 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
263 name, field["mode"]
264 ).as_str(),
265 ));
266 }
267 }
268 }
269 } else {
270 match request[name].to_string().parse::<f64>() {
271 Ok(_) => {}
272 Err(_) => {
273 return Err(ApiResponse::fail(
274 900_013,
275 format!(
276 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
277 name, field["mode"]
278 )
279 .as_str(),
280 ));
281 }
282 }
283 }
284 }
285 }
286 "float" => {
287 if require && request[name].to_string().is_empty() {
288 return Err(ApiResponse::fail(
289 900_002,
290 format!("{title} 必填").as_str(),
291 ));
292 }
293 if !request[name].to_string().is_empty() {
294 match request[name].to_string().parse::<f64>() {
295 Ok(_) => {}
296 Err(_) => {
297 return Err(ApiResponse::fail(
298 900_023,
299 format!(
300 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
301 name, field["mode"]
302 )
303 .as_str(),
304 ));
305 }
306 }
307 }
308 }
309 "string" | "url" | "time" | "code" | "pass" | "email" | "location"
310 | "color" | "date" | "barcode" | "datetime" | "editor" | "tel" => {
311 if !request[name].is_string() {
312 return Err(ApiResponse::fail(
313 -1,
314 format!(
315 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
316 name, field["mode"]
317 )
318 .as_str(),
319 ));
320 }
321 if require && request[name].is_empty() {
322 return Err(ApiResponse::fail(
323 900_004,
324 format!("{title} 必填").as_str(),
325 ));
326 }
327 }
328 "dict" => {
329 if require && request[name].is_empty() {
330 return Err(ApiResponse::fail(
331 900_005,
332 format!("{title} 必填").as_str(),
333 ));
334 }
335 }
336 "switch" => match request[name].to_string().parse::<bool>() {
337 Ok(e) => {
338 request[name] = e.into();
339 }
340 Err(_) => {
341 return Err(ApiResponse::fail(
342 -1,
343 format!(
344 "请求参数数据类型错误: 参数 [{name}] 数据类型应为[{}]",
345 field["mode"]
346 )
347 .as_str(),
348 ));
349 }
350 },
351 "select" => {
352 if !request[name].is_array() && !request[name].is_string() {
353 return Err(ApiResponse::fail(
354 -1,
355 format!(
356 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
357 name, field["mode"]
358 )
359 .as_str(),
360 ));
361 }
362 let value = if request[name].is_array() {
363 request[name]
364 .members()
365 .map(ToString::to_string)
366 .collect::<Vec<String>>()
367 } else {
368 request[name]
369 .to_string()
370 .split(',')
371 .map(ToString::to_string)
372 .collect::<Vec<String>>()
373 };
374 let option = field["option"]
375 .members()
376 .map(|m| m.as_str().unwrap_or(""))
377 .collect::<Vec<&str>>();
378
379 let require = field["require"].as_bool().unwrap_or(false);
380 for item in value.clone() {
381 if !option.contains(&&*item.clone()) && (item.is_empty() && require) {
382 let option = field["option"]
383 .members()
384 .map(|m| m.as_str().unwrap_or(""))
385 .collect::<Vec<&str>>()
386 .join(",");
387 return Err(ApiResponse::fail(-1, format!("请求参数选项错误: 参数 [{item}] 数据类型应为[{option}]之内").as_str()));
388 }
389 }
390 request[name] = value.into();
391 }
392 "radio" => {
393 if !request[name].is_string() {
394 return Err(ApiResponse::fail(
395 -1,
396 format!(
397 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}] 实际为[{}]",
398 name, field["mode"], request[name]
399 )
400 .as_str(),
401 ));
402 }
403 let option = field["option"]
404 .members()
405 .map(|m| m.as_str().unwrap_or(""))
406 .collect::<Vec<&str>>()
407 .join(",");
408 if request[name].is_string()
409 && !field["option"].contains(request[name].as_str().unwrap_or(""))
410 {
411 return Err(ApiResponse::fail(
412 -1,
413 format!(
414 "请求参数选项错误: 参数 [{}] 数据 [{}] 应为 [{}] 之一",
415 name, request[name], option
416 )
417 .as_str(),
418 ));
419 }
420 }
421 "array" | "polygon" => {
422 if !request[name].is_array() {
423 return Err(ApiResponse::fail(
424 900_009,
425 format!(
426 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
427 name, field["mode"]
428 )
429 .as_str(),
430 ));
431 }
432 if require && request[name].is_empty() {
433 return Err(ApiResponse::fail(
434 900_010,
435 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
436 ));
437 }
438 }
439 "object" => {
440 if !request[name].is_object() {
441 return Err(ApiResponse::fail(
442 900_009,
443 format!(
444 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
445 name, field["mode"]
446 )
447 .as_str(),
448 ));
449 }
450 if require && request[name].is_empty() {
451 return Err(ApiResponse::fail(
452 900_006,
453 format!("{title} 必填").as_str(),
454 ));
455 }
456 }
457 _ => {
458 log::warn!("检查未知类型: {}", field["mode"]);
459 }
460 }
461 } else {
462 if require {
463 return Err(ApiResponse::fail(900_007, format!("{title} 必填").as_str()));
464 }
465 request[name] = field["def"].clone();
466 }
467 }
468 Ok(())
469 }
470 fn index(&mut self, request: Request) -> ApiResponse;
472 #[cfg(any(
473 feature = "sqlite",
474 feature = "mssql",
475 feature = "mysql",
476 feature = "pgsql"
477 ))]
478 fn table_select(
479 &mut self,
480 request: JsonValue,
481 table_name: &str,
482 fields: Vec<&str>,
483 ) -> JsonValue {
484 self.table()
485 .main_select_fields(table_name, fields)
486 .params(request)
487 .get_table_select()
488 }
489
490 #[allow(clippy::too_many_arguments)]
498 #[cfg(any(
499 feature = "sqlite",
500 feature = "mssql",
501 feature = "mysql",
502 feature = "pgsql"
503 ))]
504 fn table_list(
505 &mut self,
506 request: JsonValue,
507 table_name: &str,
508 fields: JsonValue,
509 hidd_field: Vec<&str>,
510 show_field: Vec<&str>,
511 search_fields: Vec<&str>,
512 filter_fields: Vec<&str>,
513 ) -> JsonValue {
514 self.table()
515 .main_table_fields(table_name, fields, hidd_field, show_field)
516 .search_fields(search_fields)
517 .filter_fields(filter_fields)
518 .params(request)
519 .get_table()
520 }
521 #[allow(clippy::too_many_arguments)]
522 #[cfg(any(
523 feature = "sqlite",
524 feature = "mssql",
525 feature = "mysql",
526 feature = "pgsql"
527 ))]
528 fn table(&mut self) -> Tables {
529 Tables::new(self.tools().db)
530 }
531
532 fn tools(&mut self) -> Tools {
534 PLUGIN_TOOLS.get().expect("tools not initialized").clone()
535 }
536 fn config(&mut self, name: &str) -> JsonValue {
538 if let Ok(cfg) = CONFIG.lock() {
539 cfg.get(name).cloned().unwrap_or_else(|| object! {})
540 } else {
541 object! {}
542 }
543 }
544 fn btn(&mut self) -> Btn {
547 let mut btn = Btn::new(self.api().as_str());
548 btn.fields(self.params());
549 btn.tags(self.tags());
550 btn.icon(self.icon());
551 btn.desc(self.description());
552 btn.auth(self.auth());
553 btn.public(self.public());
554 btn.title(self.title());
555 btn.btn_type(BtnType::Api);
556 btn.btn_color(BtnColor::Primary);
557 btn.path(self.api().replace('.', "/").as_str());
558 btn.pass(false);
559 btn.addon();
560 btn
561 }
562 fn set_global_data(&mut self, key: &str, value: JsonValue) {
564 GLOBAL_DATA.with(|data| {
565 data.borrow_mut()[key] = value;
566 });
567 }
568 fn get_global_data(&mut self) -> JsonValue {
570 GLOBAL_DATA.with(|data| data.borrow().clone())
571 }
572 fn get_global_data_key(&mut self, key: &str) -> JsonValue {
574 GLOBAL_DATA.with(|data| data.borrow()[key].clone())
575 }
576}
577#[derive(Debug, Clone)]
578pub struct Btn {
579 api: String,
580 title: String,
581 desc: String,
582 tags: &'static [&'static str],
583 auth: bool,
584 public: bool,
585 btn_type: BtnType,
586 color: BtnColor,
587 icon: String,
588 cnd: Vec<JsonValue>,
589 url: String,
590 path: String,
591 fields: JsonValue,
592 addon: String,
593 version: usize,
594 pass: bool,
596}
597impl Btn {
598 #[must_use]
599 pub fn new(api: &str) -> Self {
600 Self {
601 api: api.to_string(),
602 title: String::new(),
603 desc: String::new(),
604 btn_type: BtnType::Api,
605 color: BtnColor::Primary,
606 icon: String::new(),
607 auth: false,
608 public: false,
609 cnd: vec![],
610 url: String::new(),
611 path: String::new(),
612 fields: object! {},
613 tags: &[],
614 pass: false,
615 addon: String::new(),
616 version: 0,
617 }
618 }
619
620 pub fn addon(&mut self) -> &mut Self {
621 self.addon = self.api.split('.').next().unwrap_or_default().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}