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::Object::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::Object::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 let is_multiple = field["multiple"].as_bool().unwrap_or(false);
192 if is_multiple {
193 if !request[name].is_array() {
194 return Err(ApiResponse::fail(
195 900_009,
196 format!(
197 "请求参数数据类型错误: 参数 [{}] 数据类型应为数组",
198 name
199 )
200 .as_str(),
201 ));
202 }
203 if require && request[name].is_empty() {
204 return Err(ApiResponse::fail(
205 900_010,
206 format!("{title} 必填").as_str(),
207 ));
208 }
209 } else {
210 if !request[name].is_string() {
211 return Err(ApiResponse::fail(
212 900_002,
213 format!(
214 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
215 name, field["mode"]
216 )
217 .as_str(),
218 ));
219 }
220 if require && request[name].is_empty() {
221 return Err(ApiResponse::fail(
222 900_002,
223 format!("{title} 必填").as_str(),
224 ));
225 }
226 }
227 }
228 "file" => {
229 if !request[name].is_array() && !request[name].is_string() {
230 return Err(ApiResponse::fail(
231 900_003,
232 format!("参数 [{}] 数据类型应为[{}]", name, field["mode"]).as_str(),
233 ));
234 }
235 if require && request[name].is_empty() {
236 return Err(ApiResponse::fail(
237 900_002,
238 format!("{title} 必填").as_str(),
239 ));
240 }
241 }
242 "int" => {
243 if require && request[name].to_string().is_empty() {
244 return Err(ApiResponse::fail(
245 900_002,
246 format!("{title} 必填").as_str(),
247 ));
248 }
249 if !request[name].to_string().is_empty() {
250 match request[name].to_string().parse::<i64>() {
251 Ok(_) => {}
252 Err(_) => {
253 return Err(ApiResponse::fail(
254 900_013,
255 format!(
256 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
257 name, field["mode"]
258 )
259 .as_str(),
260 ));
261 }
262 }
263 }
264 }
265 "timestamp" | "yearmonth" | "date" | "datetime" => {
266 if require && request[name].to_string().is_empty() {
267 return Err(ApiResponse::fail(
268 900_002,
269 format!("{title} 必填").as_str(),
270 ));
271 }
272 if !request[name].to_string().is_empty() {
273 if request[name].is_array() && request[name].len() == 2 {
274 for item in request[name].members() {
276 match item.to_string().parse::<f64>() {
277 Ok(_) => {}
278 Err(_) => {
279 return Err(ApiResponse::fail(
280 900_013,
281 format!(
282 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
283 name, field["mode"]
284 ).as_str(),
285 ));
286 }
287 }
288 }
289 } else {
290 match request[name].to_string().parse::<f64>() {
291 Ok(_) => {}
292 Err(_) => {
293 return Err(ApiResponse::fail(
294 900_013,
295 format!(
296 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
297 name, field["mode"]
298 )
299 .as_str(),
300 ));
301 }
302 }
303 }
304 }
305 }
306 "float" => {
307 if require && request[name].to_string().is_empty() {
308 return Err(ApiResponse::fail(
309 900_002,
310 format!("{title} 必填").as_str(),
311 ));
312 }
313 if !request[name].to_string().is_empty() {
314 match request[name].to_string().parse::<f64>() {
315 Ok(_) => {}
316 Err(_) => {
317 return Err(ApiResponse::fail(
318 900_023,
319 format!(
320 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
321 name, field["mode"]
322 )
323 .as_str(),
324 ));
325 }
326 }
327 }
328 }
329 "string" | "url" | "time" | "code" | "pass" | "email" | "location"
330 | "color" | "barcode" | "editor" | "tel" => {
331 if !request[name].is_string() {
332 return Err(ApiResponse::fail(
333 -1,
334 format!(
335 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
336 name, field["mode"]
337 )
338 .as_str(),
339 ));
340 }
341 if require && request[name].is_empty() {
342 return Err(ApiResponse::fail(
343 900_004,
344 format!("{title} 必填").as_str(),
345 ));
346 }
347 }
348 "dict" => {
349 if require && request[name].is_empty() {
350 return Err(ApiResponse::fail(
351 900_005,
352 format!("{title} 必填").as_str(),
353 ));
354 }
355 }
356 "switch" => match request[name].to_string().parse::<bool>() {
357 Ok(e) => {
358 request[name] = e.into();
359 }
360 Err(_) => {
361 return Err(ApiResponse::fail(
362 -1,
363 format!(
364 "请求参数数据类型错误: 参数 [{name}] 数据类型应为[{}]",
365 field["mode"]
366 )
367 .as_str(),
368 ));
369 }
370 },
371 "select" => {
372 if !request[name].is_array() && !request[name].is_string() {
373 return Err(ApiResponse::fail(
374 -1,
375 format!(
376 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
377 name, field["mode"]
378 )
379 .as_str(),
380 ));
381 }
382 let value = if request[name].is_array() {
383 request[name]
384 .members()
385 .map(ToString::to_string)
386 .collect::<Vec<String>>()
387 } else {
388 request[name]
389 .to_string()
390 .split(',')
391 .map(ToString::to_string)
392 .collect::<Vec<String>>()
393 };
394 let option = field["option"]
395 .members()
396 .map(|m| m.as_str().unwrap_or(""))
397 .collect::<Vec<&str>>();
398
399 let require = field["require"].as_bool().unwrap_or(false);
400 for item in value.clone() {
401 if !option.contains(&&*item.clone()) && (item.is_empty() && require) {
402 let option = field["option"]
403 .members()
404 .map(|m| m.as_str().unwrap_or(""))
405 .collect::<Vec<&str>>()
406 .join(",");
407 return Err(ApiResponse::fail(-1, format!("请求参数选项错误: 参数 [{item}] 数据类型应为[{option}]之内").as_str()));
408 }
409 }
410 request[name] = value.into();
411 }
412 "radio" => {
413 if !request[name].is_string() {
414 return Err(ApiResponse::fail(
415 -1,
416 format!(
417 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}] 实际为[{}]",
418 name, field["mode"], request[name]
419 )
420 .as_str(),
421 ));
422 }
423 let option = field["option"]
424 .members()
425 .map(|m| m.as_str().unwrap_or(""))
426 .collect::<Vec<&str>>()
427 .join(",");
428 if request[name].is_string()
429 && !field["option"].contains(request[name].as_str().unwrap_or(""))
430 {
431 return Err(ApiResponse::fail(
432 -1,
433 format!(
434 "请求参数选项错误: 参数 [{}] 数据 [{}] 应为 [{}] 之一",
435 name, request[name], option
436 )
437 .as_str(),
438 ));
439 }
440 }
441 "array" | "polygon" | "keys" | "table_multiple" => {
442 if !request[name].is_array() {
443 return Err(ApiResponse::fail(
444 900_009,
445 format!(
446 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
447 name, field["mode"]
448 )
449 .as_str(),
450 ));
451 }
452 if require && request[name].is_empty() {
453 return Err(ApiResponse::fail(
454 900_010,
455 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
456 ));
457 }
458 }
459 "object" => {
460 if !request[name].is_object() {
461 return Err(ApiResponse::fail(
462 900_009,
463 format!(
464 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
465 name, field["mode"]
466 )
467 .as_str(),
468 ));
469 }
470 if require && request[name].is_empty() {
471 return Err(ApiResponse::fail(
472 900_006,
473 format!("{title} 必填").as_str(),
474 ));
475 }
476 }
477 _ => {
478 log::warn!("检查未知类型: {}", field["mode"]);
479 }
480 }
481 } else {
482 if require {
483 return Err(ApiResponse::fail(900_007, format!("{title} 必填").as_str()));
484 }
485 request[name] = field["def"].clone();
486 }
487 }
488 Ok(())
489 }
490 fn index(&mut self, request: Request) -> ApiResponse;
492 #[cfg(any(
493 feature = "sqlite",
494 feature = "mssql",
495 feature = "mysql",
496 feature = "pgsql"
497 ))]
498 fn table_select(
499 &mut self,
500 request: JsonValue,
501 table_name: &str,
502 fields: Vec<&str>,
503 ) -> JsonValue {
504 self.table()
505 .main_select_fields(table_name, fields)
506 .params(request)
507 .get_table_select()
508 }
509
510 #[cfg(any(
511 feature = "sqlite",
512 feature = "mssql",
513 feature = "mysql",
514 feature = "pgsql"
515 ))]
516 fn table(&mut self) -> Tables {
517 Tables::new(self.tools().db)
518 }
519
520 fn tools(&mut self) -> Tools {
522 PLUGIN_TOOLS.get().expect("tools not initialized").clone()
523 }
524 fn config(&mut self, name: &str) -> JsonValue {
526 if let Ok(cfg) = CONFIG.lock() {
527 cfg.get(name).cloned().unwrap_or_else(|| object! {})
528 } else {
529 object! {}
530 }
531 }
532 fn btn(&mut self) -> Btn {
535 let mut btn = Btn::new(self.api().as_str());
536 btn.fields(self.params());
537 btn.tags(self.tags());
538 btn.icon(self.icon());
539 btn.desc(self.description());
540 btn.auth(self.auth());
541 btn.public(self.public());
542 btn.title(self.title());
543 btn.btn_type(BtnType::Api);
544 btn.btn_color(BtnColor::Primary);
545 btn.path(self.api().replace('.', "/").as_str());
546 btn.pass(false);
547 btn.addon();
548 btn
549 }
550 fn set_global_data(&mut self, key: &str, value: JsonValue) {
552 GLOBAL_DATA.with(|data| {
553 data.borrow_mut()[key] = value;
554 });
555 }
556 fn get_global_data(&mut self) -> JsonValue {
558 GLOBAL_DATA.with(|data| data.borrow().clone())
559 }
560 fn get_global_data_key(&mut self, key: &str) -> JsonValue {
562 GLOBAL_DATA.with(|data| data.borrow()[key].clone())
563 }
564}
565#[derive(Debug, Clone)]
566pub struct Btn {
567 api: String,
568 title: String,
569 desc: String,
570 tags: &'static [&'static str],
571 auth: bool,
572 public: bool,
573 btn_type: BtnType,
574 color: BtnColor,
575 icon: String,
576 cnd: Vec<JsonValue>,
577 url: String,
578 path: String,
579 fields: JsonValue,
580 addon: String,
581 pass: bool,
583}
584impl Btn {
585 #[must_use]
586 pub fn new(api: &str) -> Self {
587 Self {
588 api: api.to_string(),
589 title: String::new(),
590 desc: String::new(),
591 btn_type: BtnType::Api,
592 color: BtnColor::Primary,
593 icon: String::new(),
594 auth: false,
595 public: false,
596 cnd: vec![],
597 url: String::new(),
598 path: String::new(),
599 fields: object! {},
600 tags: &[],
601 pass: false,
602 addon: String::new(),
603 }
604 }
605
606 pub fn addon(&mut self) -> &mut Self {
607 self.addon = self.api.split('.').next().unwrap_or_default().to_string();
608 self
609 }
610 pub fn path(&mut self, path: &str) -> &mut Self {
611 self.path = path.to_string();
612 self
613 }
614 pub fn cnd(&mut self, cnd: Vec<JsonValue>) -> &mut Self {
615 self.cnd = cnd;
616 self
617 }
618 pub fn btn_type(&mut self, btn_type: BtnType) -> &mut Self {
619 self.btn_type = btn_type;
620 self
621 }
622 pub fn btn_color(&mut self, btn_color: BtnColor) -> &mut Self {
623 self.color = btn_color;
624 self
625 }
626 pub fn fields(&mut self, fields: JsonValue) -> &mut Self {
627 self.fields = fields;
628 self
629 }
630 pub fn pass(&mut self, pass: bool) -> &mut Self {
631 self.pass = pass;
632 self
633 }
634 pub fn url(&mut self, url: &str) -> &mut Self {
635 self.url = url.to_string();
636 self
637 }
638 pub fn title(&mut self, title: &str) -> &mut Self {
639 self.title = title.to_string();
640 self
641 }
642 pub fn desc(&mut self, desc: &str) -> &mut Self {
643 self.desc = desc.to_string();
644 self
645 }
646 pub fn tags(&mut self, tags: &'static [&'static str]) -> &mut Self {
647 self.tags = tags;
648 self
649 }
650 pub fn public(&mut self, public: bool) -> &mut Self {
651 self.public = public;
652 self
653 }
654 pub fn auth(&mut self, auth: bool) -> &mut Self {
655 self.auth = auth;
656 self
657 }
658 pub fn icon(&mut self, icon: &str) -> &mut Self {
659 self.icon = icon.to_string();
660 self
661 }
662 pub fn json(&mut self) -> JsonValue {
663 let color = self.color.clone().str();
664 object! {
665 addon:self.addon.to_string() ,
666 api:self.api.clone(),
667 title:self.title.clone(),
668 desc:self.desc.clone(),
669 auth:self.auth,
670 public:self.public,
671 btn_type:self.btn_type.clone().str(),
672 color:color,
673 icon:self.icon.clone(),
674 cnd:self.cnd.clone(),
675 url:self.url.clone(),
676 path:self.path.clone(),
677 fields:self.fields.clone(),
678 tags:self.tags,
679 pass:self.pass,
680 }
681 }
682}
683
684#[derive(Debug, Clone)]
686pub enum InterfaceType {
687 Api,
688 Btn,
689 Menu,
690 OpenApi,
691}
692
693impl InterfaceType {
694 pub fn str(&self) -> &'static str {
695 match self {
696 InterfaceType::Api => "api",
697 InterfaceType::Btn => "btn",
698 InterfaceType::Menu => "menu",
699 InterfaceType::OpenApi => "openapi",
700 }
701 }
702 pub fn types() -> Vec<&'static str> {
703 vec!["api", "btn", "menu", "openapi"]
704 }
705}
706
707#[derive(Debug, Clone)]
709pub enum BtnType {
710 Form,
712 FormDownload,
714 FormCustom,
716 FormData,
717 Url,
719 Api,
721 Download,
723 Path,
725 DialogCustom,
727 FormApiDialogCustom,
729 Preview,
731}
732
733impl BtnType {
734 fn str(self) -> &'static str {
735 match self {
736 BtnType::Form => "form",
737 BtnType::FormDownload => "form_download",
738 BtnType::FormCustom => "form_custom",
739 BtnType::FormData => "form_data",
740 BtnType::Api => "api",
741 BtnType::Download => "download",
742 BtnType::Url => "url",
743 BtnType::Path => "path",
744 BtnType::DialogCustom => "dialog_custom",
745 BtnType::FormApiDialogCustom => "form_api_dialog_custom",
746 BtnType::Preview => "preview",
747 }
748 }
749}
750#[derive(Debug, Clone)]
752pub enum BtnColor {
753 Primary,
754 Red,
755 Yellow,
756 Green,
757 Blue,
758 Orange,
759 Purple,
760 Cyan,
761 Teal,
762 Grey,
763}
764
765impl BtnColor {
766 fn str(self) -> &'static str {
767 match self {
768 BtnColor::Primary => "primary",
769 BtnColor::Red => "negative",
770 BtnColor::Yellow => "warning",
771 BtnColor::Green => "positive",
772 BtnColor::Blue => "blue",
773 BtnColor::Orange => "orange",
774 BtnColor::Purple => "purple",
775 BtnColor::Cyan => "cyan",
776 BtnColor::Teal => "teal",
777 BtnColor::Grey => "grey",
778 }
779 }
780}
781
782pub struct Dashboard {
783 title: String,
785 data: JsonValue,
787 model: DashboardModel,
789 class: String,
791 icon: String,
793 desc: String,
795 api: String,
797 options: JsonValue,
799}
800impl Dashboard {
801 pub fn new(title: &str) -> Dashboard {
802 Dashboard {
803 title: title.to_string(),
804 data: JsonValue::Null,
805 model: DashboardModel::Number,
806 class: "col-4".to_string(),
807 icon: "".to_string(),
808 desc: "".to_string(),
809 api: "".to_string(),
810 options: JsonValue::Null,
811 }
812 }
813 pub fn options(&mut self, options: JsonValue) -> &mut Self {
814 self.options = options;
815 self
816 }
817 pub fn api(&mut self, api: &str) -> &mut Self {
818 self.api = api.to_string();
819 self
820 }
821 pub fn data(&mut self, data: JsonValue) -> &mut Self {
822 self.data = data;
823 self
824 }
825 pub fn class(&mut self, name: &str) -> &mut Self {
826 self.class = name.to_string();
827 self
828 }
829 pub fn icon(&mut self, name: &str) -> &mut Self {
830 self.icon = name.to_string();
831 self
832 }
833 pub fn model(&mut self, dashboard_model: DashboardModel) -> &mut Self {
834 self.model = dashboard_model;
835 self
836 }
837 pub fn desc(&mut self, desc: &str) -> &mut Self {
838 self.desc = desc.to_string();
839 self
840 }
841 pub fn json(&self) -> JsonValue {
842 object! {
843 title: self.title.clone(),
844 data: self.data.clone(),
845 class: self.class.clone(),
846 icon: self.icon.clone(),
847 model: self.model.str(),
848 desc: self.desc.clone(),
849 api: self.api.clone(),
850 options:self.options.clone(),
851 }
852 }
853}
854
855pub enum DashboardModel {
856 Number,
858 EchartsBar,
860 EchartsStackedBar,
862 EchartsBarRace,
864 EchartsPie,
866 EchartsDoughnut,
868 EchartsStackedLine,
870 EchartsStackedLineArea,
872 EchartsGeoGraph,
874}
875
876impl DashboardModel {
877 pub fn str(&self) -> &'static str {
878 match self {
879 Self::Number => "number",
880 Self::EchartsBar => "echarts-bar",
881 Self::EchartsStackedBar => "echarts-stacked_bar",
882 Self::EchartsBarRace => "echarts-bar_race",
883 Self::EchartsPie => "echarts-pie",
884 Self::EchartsDoughnut => "echarts-doughnut",
885 Self::EchartsStackedLine => "echarts-stacked_line",
886 Self::EchartsStackedLineArea => "echarts-stacked_line_area",
887 Self::EchartsGeoGraph => "echarts-geo_graph",
888 }
889 }
890}