use crate::PLUGIN_TOOLS;
use crate::{ApiResponse, Tools};
use br_fields::Field;
use br_web_server::request::{ContentType, Method, Request};
use json::{array, object, JsonValue};
use std::any::type_name;
pub trait Action {
fn name(&self) -> String {
type_name::<Self>()
.rsplit("::")
.next()
.unwrap()
.to_lowercase()
}
fn api(&self) -> String {
let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
let plugin = t[2].to_lowercase();
let module = t[3].to_lowercase();
let action = t[4].to_lowercase();
format!("{plugin}.{module}.{action}")
}
fn token(&self) -> bool {
false
}
fn version(&self) -> &'static str {
"v1"
}
fn title(&self) -> &'static str;
fn description(&self) -> &'static str {
""
}
fn tags(&self) -> &'static [&'static str] {
&[]
}
fn public(&mut self) -> bool {
true
}
fn auth(&mut self) -> bool {
false
}
fn interface_type(&mut self) -> InterfaceType {
InterfaceType::API
}
fn method(&mut self) -> Method {
Method::POST
}
fn content_type(&mut self) -> ContentType {
ContentType::Json
}
fn params(&mut self) -> JsonValue {
object! {}
}
fn success(&mut self) -> ApiResponse {
let mut data = object! {};
data["code"] = br_fields::int::Int::new(true, "code", "编号", 10, 0)
.example(0.into())
.swagger();
data["message"] = br_fields::str::Str::new(true, "message", "成功消息", 256, "")
.example("成功".into())
.swagger();
data["data"] = br_fields::text::Json::new(true, "data", "返回数据", object! {}).swagger();
data["success"] = br_fields::int::Switch::new(true, "success", "成功状态", true)
.example(true.into())
.swagger();
ApiResponse::success(data, "请求成功")
}
fn error(&mut self) -> ApiResponse {
let mut data = object! {};
data["code"] = br_fields::int::Int::new(true, "code", "编号", 10, 1000)
.example(1000.into())
.swagger();
data["message"] = br_fields::str::Str::new(true, "message", "错误消息", 256, "")
.example("失败".into())
.swagger();
data["data"] = br_fields::text::Json::new(true, "data", "返回数据", object! {}).swagger();
data["success"] = br_fields::int::Switch::new(true, "success", "成功状态", false)
.example(false.into())
.swagger();
ApiResponse::error(data, "请求失败")
}
fn run(&mut self, mut request: Request) -> Result<ApiResponse, ApiResponse> {
if self.method().str().to_lowercase() != request.method.str().to_lowercase() {
return Err(ApiResponse::fail(
-1,
format!(
"Request type error: Actual [{}] Expected [{}]",
request.method.str(),
self.method().str()
)
.as_str(),
));
}
self.check(&mut request.body.content)?;
let res = self.index(request.clone());
match res.success {
true => Ok(res),
false => Err(res),
}
}
fn check(&mut self, request: &mut JsonValue) -> Result<(), ApiResponse> {
let params = self.params();
let req = request.clone();
for (name, _) in req.entries() {
if !params.has_key(name) {
request.remove(name);
}
}
for (name, field) in params.entries() {
let require = field["require"].as_bool().unwrap_or(false);
if request.has_key(name) {
match field["mode"].as_str().unwrap() {
"int" => {
if !request[name].is_number() {
return Err(ApiResponse::fail(
-1,
format!(
"请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
name, field["mode"]
)
.as_str(),
));
}
}
"string" => {
if !request[name].is_string() {
return Err(ApiResponse::fail(
-1,
format!(
"请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
name, field["mode"]
)
.as_str(),
));
}
if require && request[name].is_empty() {
return Err(ApiResponse::fail(
-1,
format!("请求参数数据类型错误: 参数 [{}] 不能为空", name).as_str(),
));
}
}
"switch" => {
if !request[name].is_boolean() {
return Err(ApiResponse::fail(
-1,
format!(
"请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
name, field["mode"]
)
.as_str(),
));
}
}
"select" => {
if !request[name].is_array() && !request[name].is_string() {
return Err(ApiResponse::fail(
-1,
format!(
"请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
name, field["mode"]
)
.as_str(),
));
}
let option = field["option"]
.members()
.map(|m| m.as_str().unwrap_or(""))
.collect::<Vec<&str>>()
.join(",");
if request[name].is_string()
&& !field["option"].contains(request[name].as_str().unwrap_or(""))
{
return Err(ApiResponse::fail(
-1,
format!(
"请求参数选项错误: 参数 [{}] 数据类型应为[{}]之内",
name, option
)
.as_str(),
));
}
if request[name].is_array() {
let res1 = request[name]
.members()
.map(|m| m.as_str().unwrap_or(""))
.collect::<Vec<&str>>();
let diff = res1
.iter()
.filter(|&item| {
!field["option"]
.members()
.map(|m| m.as_str().unwrap_or(""))
.collect::<Vec<&str>>()
.contains(item)
})
.collect::<Vec<&&str>>();
if !diff.is_empty() {
return Err(ApiResponse::fail(
-1,
format!(
"请求参数选项错误: 参数 [{}] 数据 {:?} 应为[{:?}]范围之内",
name, diff, option
)
.as_str(),
));
}
}
}
"radio" => {
if !request[name].is_string() {
return Err(ApiResponse::fail(
-1,
format!(
"请求参数数据类型错误: 参数 [{}] 数据类型应为[{}] 实际为[{}]",
name, field["mode"], request[name]
)
.as_str(),
));
}
let option = field["option"]
.members()
.map(|m| m.as_str().unwrap_or(""))
.collect::<Vec<&str>>()
.join(",");
if request[name].is_string()
&& !field["option"].contains(request[name].as_str().unwrap_or(""))
{
return Err(ApiResponse::fail(
-1,
format!(
"请求参数选项错误: 参数 [{}] 数据 [{}] 应为 [{}] 之一",
name, request[name], option
)
.as_str(),
));
}
}
_ => {
}
}
} else {
if require {
return Err(ApiResponse::fail(
-1,
format!("请求参数错误: 参数[{}]必填", name).as_str(),
));
}
request[name] = field["def"].clone();
}
}
Ok(())
}
fn index(&mut self, request: Request) -> ApiResponse;
fn params_table_list(&mut self, mut params: JsonValue) -> JsonValue {
params["page"] = br_fields::int::Int::new(false, "page", "页数", 10, 1)
.example(1.into())
.field();
params["limit"] = br_fields::int::Int::new(false, "limit", "行数", 10, 10)
.example(10.into())
.field();
params["first"] = br_fields::int::Switch::new(false, "first", "首次", true)
.example(true.into())
.field();
params["sorts"] = br_fields::text::Object::new(false, "sorts", "排序", object! {id:false})
.example(object! {id:false})
.field();
params["where_or"] = br_fields::text::Array::new(false, "where_or", "查询条件", array![])
.example(array![])
.field();
params["where_and"] = br_fields::text::Array::new(false, "where_and", "查询条件", array![])
.example(array![])
.field();
params
}
#[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql"))]
fn table_list(
&mut self,
request: JsonValue,
table_name: &str,
fields: JsonValue,
hidden: &str,
field: &str,
) -> (i64, JsonValue) {
let page = request["page"].as_i32().unwrap_or(1);
let limit = request["limit"].as_i32().unwrap_or(10);
let _first = request["first"].as_bool().unwrap_or(false);
let sorts = request["sorts"].clone();
let where_or = request["where_or"].clone();
let where_and = request["where_and"].clone();
let fields_key = fields
.entries()
.map(|(field, _)| field)
.collect::<Vec<&str>>()
.join(",");
let mut tools = self.tools();
let db = tools.db.table(table_name);
for (key, value) in where_or.entries() {
if value.is_empty() {
continue;
}
if value.is_array() {
db.where_or(key, "between", value.clone());
db.where_or(key, "in", value.clone());
} else if value.is_boolean() {
db.where_or(key, "=", value.clone());
} else {
db.where_or(key, "like", format!("%{}%", value).into());
}
}
for value in where_and.members() {
db.where_and(
value[0].as_str().unwrap(),
value[1].as_str().unwrap(),
value[2].clone(),
);
}
let total = (db.clone().count().as_f64().unwrap() / limit as f64).ceil() as i64;
let mut db_list = db.clone();
for (key, sort) in sorts.entries() {
db_list.order(key, sort.as_bool().unwrap());
}
if !hidden.is_empty() {
db_list.hidden(hidden);
}
if !field.is_empty() {
db_list.field(field);
}
if hidden.is_empty() && field.is_empty() {
db_list.field(fields_key.as_str());
}
let data = db_list.page(page, limit).select();
(total, data)
}
fn tools(&mut self) -> Tools {
let tools = PLUGIN_TOOLS.lock().unwrap();
let tools = tools.get("tools").unwrap().clone();
tools
}
fn btn_info(
&mut self,
btn_type: BtnType,
btn_color: BtnColor,
title: &str,
desc: &str,
url: &str,
cnd: Vec<JsonValue>,
) -> JsonValue {
object! {
api:self.api().clone(),
title:if title.is_empty() {self.title()}else{title},
desc:desc,
btn_type:btn_type.str(),
btn_color:btn_color.str(),
cnd:cnd,
url:url,
path:self.api().replace(".","/")
}
}
}
#[derive(Debug, Clone)]
pub enum InterfaceType {
API,
BTN,
MENU,
}
impl InterfaceType {
pub fn str(self) -> String {
match self {
InterfaceType::API => "api",
InterfaceType::BTN => "btn",
InterfaceType::MENU => "menu",
}
.to_string()
}
}
#[derive(Debug, Clone)]
pub enum BtnType {
Form,
FormDownload,
FormCustom,
Url,
Api,
Download,
Path,
}
impl BtnType {
fn str(self) -> String {
match self {
BtnType::Form => "form",
BtnType::FormDownload => "form_download",
BtnType::FormCustom => "form_custom",
BtnType::Api => "api",
BtnType::Download => "download",
BtnType::Url => "url",
BtnType::Path => "path",
}
.to_string()
}
}
#[derive(Debug, Clone)]
pub enum BtnColor {
Primary,
Red,
Blue,
Yellow,
Green,
}
impl BtnColor {
fn str(self) -> String {
match self {
BtnColor::Primary => "primary",
BtnColor::Red => "negative",
BtnColor::Blue => "info",
BtnColor::Yellow => "warning",
BtnColor::Green => "positive",
}
.to_string()
}
}