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