添加responsable过程宏,处理接口数据返回

This commit is contained in:
李运家 2024-09-24 11:40:00 +08:00
parent 185c9eaea1
commit 732b67b8c6
26 changed files with 301 additions and 89 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"rust-analyzer.checkOnSave": false
}

53
Cargo.lock generated
View File

@ -85,7 +85,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -428,7 +428,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -439,7 +439,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -462,6 +462,19 @@ dependencies = [
"powerfmt",
]
[[package]]
name = "derive"
version = "0.1.0"
dependencies = [
"axum",
"hyper",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn 2.0.77",
]
[[package]]
name = "digest"
version = "0.10.7"
@ -478,7 +491,10 @@ dependencies = [
name = "domain"
version = "0.1.0"
dependencies = [
"axum",
"chrono",
"derive",
"hyper",
"i18n",
"serde",
"serde_json",
@ -676,7 +692,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -1381,7 +1397,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -1485,7 +1501,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -1592,9 +1608,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.36"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
@ -1945,7 +1961,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -2352,9 +2368,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.72"
version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [
"proc-macro2",
"quote",
@ -2380,6 +2396,7 @@ dependencies = [
"axum",
"axum-extra",
"chrono",
"derive",
"domain",
"error-stack",
"futures-executor",
@ -2453,7 +2470,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -2553,7 +2570,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -2712,7 +2729,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -2890,7 +2907,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]
@ -2953,7 +2970,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
"wasm-bindgen-shared",
]
@ -2987,7 +3004,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -3241,7 +3258,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.77",
]
[[package]]

View File

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[workspace]
members = [".", "system", "domain", "i18n","library"]
members = [".", "system", "domain", "i18n","library", "derive"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -48,3 +48,7 @@ tokio-cron-scheduler = "0.10.2"
hmac = "0.12.1"
sha2 = "0.10.8"
hex-literal = "0.4.1"
proc-macro2 = "1.0.86"
syn = "2.0.77"
quote = "1.0.37"
hyper = "1.4.1"

18
derive/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "derive"
version = "0.1.0"
edition = "2021"
[lib]
name = "derive"
path = "src/lib.rs"
proc-macro = true
[dependencies]
axum = { workspace = true }
proc-macro2 = { workspace = true, features = ["proc-macro"] }
syn = { workspace = true, features = ["full"] }
quote = { workspace = true, features = ["proc-macro"] }
hyper = { workspace = true }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

11
derive/README.MD Normal file
View File

@ -0,0 +1,11 @@
### 注意事项
1. 过程宏,引入其他第三方模块,需要使用`extern crate`引入,或者直接在代码中使用全路径调用,如`axum::http::Response::new(axum::body::Body::from(body))`
2. 过程宏单独放在一个library中需要使用`#[proc_macro_derive]`注解,或者`#[proc_macro]`注解,或者`#[proc_macro_attribute]`注解等
3. 过程宏工程的cargo.toml中需要添加
```toml
[lib]
name = "derive"
path = "src/lib.rs"
proc-macro = true
```
4. 调用过程宏的模块需要将过程宏依赖的包添加到cargo.toml中如domain工程需要添加axum和hyper依赖

142
derive/src/lib.rs Normal file
View File

@ -0,0 +1,142 @@
extern crate proc_macro2;
extern crate quote;
extern crate syn;
extern crate proc_macro;
use proc_macro::TokenStream; // 用于处理宏输入
use quote::quote; // 用于生成代码
use syn::{parse_macro_input, Data, DeriveInput, Fields}; // 用于解析宏输入
// 定义一个名为`responsable`的过程宏
// #[proc_macro_derive(Responsable, attributes(status, headers))]
#[proc_macro_derive(Responsable)]
pub fn responsable(input: TokenStream) -> TokenStream {
// 解析宏输入为`DeriveInput`
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident; // 获取结构体名称
// 根据数据类型生成不同的代码
let gen = match input.data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref _fields) => {
// // 处理命名字段
// let field_names = fields.named.iter().map(|f| &f.ident); // 获取所有字段名称
// let field_types = fields.named.iter().map(|f| &f.ty); // 获取所有字段类型
// // 获取`status`属性
// let status = match input.attrs.iter().find(|attr| attr.path().is_ident("status")) {
// Some(attr) => {
// let status_lit = attr.parse_args::<Ident>().unwrap(); // 解析`status`属性值
// quote! { #status_lit } // 生成状态码
// },
// None => quote! { hyper::StatusCode::OK }, // 默认状态码为`OK`
// };
// // 获取`headers`属性
// let headers = match input.attrs.iter().find(|attr| attr.path().is_ident("headers")) {
// Some(attr) => {
// let headers_lit = attr.parse_args::<Ident>().unwrap(); // 解析`headers`属性值
// quote! { #headers_lit } // 生成头部信息
// },
// None => quote! { hyper::HeaderMap::new() }, // 默认头部信息为空
// };
// 生成实现`IntoResponse` trait的代码
quote! {
impl axum::response::IntoResponse for #name {
fn into_response(self) -> axum::http::Response<axum::body::Body> {
let mut builder = hyper::Response::builder();
// .status(#status) // 设置状态码
// .headers(#headers); // 设置头部信息
let body = serde_json::to_string(&self).expect("Failed to serialize response"); // 序列化结构体为JSON字符串
axum::http::Response::new(axum::body::Body::from(body)) // 构建响应
}
}
}
},
Fields::Unnamed(ref _fields) => {
// // 处理未命名字段
// let field_names = fields.unnamed.iter().enumerate().map(|(i, _)| i); // 获取所有字段索引
// let field_types = fields.unnamed.iter().map(|f| &f.ty); // 获取所有字段类型
// // 获取`status`属性
// let status = match input.attrs.iter().find(|attr| attr.path().is_ident("status")) {
// Some(attr) => {
// let status_lit = attr.parse_args::<Ident>().unwrap(); // 解析`status`属性值
// quote! { #status_lit } // 生成状态码
// },
// None => quote! { hyper::StatusCode::OK }, // 默认状态码为`OK`
// };
// // 获取`headers`属性
// let headers = match input.attrs.iter().find(|attr| attr.path().is_ident("headers")) {
// Some(attr) => {
// let headers_lit = attr.parse_args::<Ident>().unwrap(); // 解析`headers`属性值
// quote! { #headers_lit } // 生成头部信息
// },
// None => quote! { hyper::HeaderMap::new() }, // 默认头部信息为空
// };
// 生成实现`IntoResponse` trait的代码
quote! {
impl axum::response::IntoResponse for #name {
fn into_response(self) -> axum::http::Response<axum::body::Body> {
// let mut builder = hyper::Response::builder();
// .status(#status) // 设置状态码
// .headers(#headers); // 设置头部信息
let body = serde_json::to_string(&self).expect("Failed to serialize response"); // 序列化结构体为JSON字符串
axum::http::Response::new(axum::body::Body::from(body)) // 构建响应
}
}
}
},
Fields::Unit => {
// // 处理无字段的结构体
// // 获取`status`属性
// let status = match input.attrs.iter().find(|attr| attr.path().is_ident("status")) {
// Some(attr) => {
// let status_lit = attr.parse_args::<Ident>().unwrap(); // 解析`status`属性值
// quote! { #status_lit } // 生成状态码
// },
// None => quote! { hyper::StatusCode::OK }, // 默认状态码为`OK`
// };
// // 获取`headers`属性
// let headers = match input.attrs.iter().find(|attr| attr.path().is_ident("headers")) {
// Some(attr) => {
// let headers_lit = attr.parse_args::<Ident>().unwrap(); // 解析`headers`属性值
// quote! { #headers_lit } // 生成头部信息
// },
// None => quote! { hyper::HeaderMap::new() }, // 默认头部信息为空
// };
// 生成实现`IntoResponse` trait的代码
quote! {
impl axum::response::IntoResponse for #name {
fn into_response(self) -> axum::http::Response<axum::body::Body> {
// let mut builder = hyper::Response::builder();
// .status(#status) // 设置状态码
// .headers(#headers); // 设置头部信息
let body = serde_json::to_string(&self).expect("Failed to serialize response"); // 序列化结构体为JSON字符串
axum::http::Response::new(hyper::body::Body::from(body)) // 构建响应
}
}
}
},
},
Data::Enum(_) | Data::Union(_) => {
// 不支持枚举或联合体
unimplemented!("IntoResponse derive is not implemented for enums or unions.")
},
};
gen.into() // 将生成的代码转换为`TokenStream`
}

View File

@ -14,5 +14,8 @@ chrono = { workspace = true, features = ["serde"]}
tracing = { workspace = true }
tracing-appender = { workspace = true }
tracing-subscriber = { workspace = true, features = ["json"] }
hyper = { workspace = true }
axum = { workspace = true }
i18n = { path = "../i18n" }
derive = { path = "../derive" }

View File

@ -1,4 +1,5 @@
pub mod entities;
pub mod dto;
pub mod vo;
mod db_result;
pub mod pageable;
pub mod db_result;

36
domain/src/pageable.rs Normal file
View File

@ -0,0 +1,36 @@
use derive::Responsable;
use serde::Serialize;
use std::fmt::Debug;
#[derive(Debug, Serialize, Responsable)]
pub struct Pageable<T: Debug + Serialize>
{
pub data: Vec<T>,
pub total: i64,
pub page: i64,
#[serde(rename = "pageSize")]
pub page_size: i64,
}
impl<T> Pageable<T>
where
T: Debug + Serialize,
{
pub fn new(data: Vec<T>, total: i64, page: i64, page_size: i64) -> Self {
Pageable {
data,
total,
page,
page_size,
}
}
pub fn empty(page: i64, page_size: i64) -> Self {
Pageable {
data: vec![],
total: 0,
page,
page_size,
}
}
}

View File

@ -1,7 +1,8 @@
use derive::Responsable;
use serde::{Deserialize, Serialize};
use sqlx::types::JsonValue;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Responsable)]
pub struct LoginAccount {
pub username: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "displayName")]
@ -17,7 +18,7 @@ pub struct LoginAccount {
pub refresh_token: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Responsable)]
pub struct RefreshTokenResult {
pub token: String,
#[serde(rename = "refreshToken")]

View File

@ -1,7 +1,7 @@
extern crate self as library;
pub mod core;
pub mod res;
pub mod model;
pub mod middleware;
pub mod token;
pub mod social;

View File

@ -1,4 +1,4 @@
use crate::res::response::ResErr;
use crate::model::response::ResErr;
use axum::body::Body;
use axum::extract::Request;
use axum::http::header::CONTENT_TYPE;

2
library/src/model/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod response;
pub mod validator;

View File

@ -1,4 +1,4 @@
use std::{error::Error as StdError, fmt::Display, sync::Arc};
use std::{error::Error as StdError, fmt::Display};
use axum::{
response::{IntoResponse, Response},

View File

@ -1,3 +0,0 @@
pub mod response;
pub mod pageable;
pub mod validator;

View File

@ -1,24 +0,0 @@
use std::fmt::Debug;
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct Pageable<T> where T: Debug + Serialize {
pub data: Vec<T>,
pub total: i64,
}
impl <T> Pageable<T> where T: Debug + Serialize {
pub fn new(data: Vec<T>, total: i64) -> Self {
Self {
data,
total,
}
}
pub fn empty() -> Self {
Self {
data: vec![],
total: 0,
}
}
}

View File

@ -8,7 +8,7 @@ use reqwest::Client;
use serde::Deserialize;
use serde_json::Value;
use crate::res::response::ResErr;
use crate::model::response::ResErr;
use super::SocialResult;

View File

@ -8,7 +8,7 @@ use reqwest::Client;
use serde::Deserialize;
use serde_json::Value;
use crate::res::response::ResErr;
use crate::model::response::ResErr;
use super::SocialResult;

View File

@ -9,7 +9,7 @@ use sha2::Sha256;
use crate::{
config,
res::response::ResErr,
model::response::ResErr,
};
use super::SocialResult;

View File

@ -24,3 +24,4 @@ tokio-cron-scheduler = { workspace = true }
library = { path = "../library" }
domain = { path = "../domain" }
i18n = { path = "../i18n" }
derive = { path = "../derive" }

View File

@ -1,6 +1,6 @@
use axum::{Extension, Json};
use domain::{dto::account::{AuthenticateGooleAccountReq, AuthenticateWithPassword, RefreshToken}, vo::account::{LoginAccount, RefreshTokenResult}};
use library::{context::{Context, WhiteContext}, res::{response::{ ResData, ResResult}, validator}};
use library::{context::{Context, WhiteContext}, model::{response::{ ResData, ResResult}, validator}};
use crate::service;
@ -9,7 +9,7 @@ use crate::service;
pub async fn authenticate_google(
Extension(context): Extension<WhiteContext>,
Json(req): Json<AuthenticateGooleAccountReq>
) -> ResResult<ResData<LoginAccount>> {
) -> ResResult<LoginAccount> {
validator::validate_params(&req, context.get_lang_tag())?;
service::account_service::authenticate_google(context, req).await
}
@ -18,7 +18,7 @@ pub async fn authenticate_google(
pub async fn authenticate_with_password(
Extension(context): Extension<WhiteContext>,
Json(req): Json<AuthenticateWithPassword>
) -> ResResult<ResData<LoginAccount>> {
) -> ResResult<LoginAccount> {
validator::validate_params(&req, context.get_lang_tag())?;
service::sys_account_service::authenticate_with_password(context, req).await
}
@ -27,13 +27,13 @@ pub async fn authenticate_with_password(
pub async fn refresh_token(
Extension(context): Extension<Context>,
Json(refresh_token): Json<RefreshToken>
) -> ResResult<ResData<RefreshTokenResult>> {
) -> ResResult<RefreshTokenResult> {
tracing::debug!("刷新token, {:?}", context);
validator::validate_params(&refresh_token, "")?;
service::account_service::refresh_token(context, refresh_token.token).await
}
/// 添加管理员账号
pub async fn add_account() -> ResResult<ResData<()>> {
pub async fn add_account() -> ResResult<()> {
service::sys_account_service::add_account().await
}

View File

@ -4,9 +4,9 @@ use domain::dto::feedback::FeedbackAdd;
use domain::dto::pageable::PageParams;
use domain::entities::feedback::Feedback;
use library::context::Context;
use library::res::pageable::Pageable;
use library::res::response::{ResData, ResResult};
use library::res::validator;
use library::model::pageable::Pageable;
use library::model::response::{ResData, ResResult};
use library::model::validator;
use crate::service;
@ -14,7 +14,7 @@ use crate::service;
pub async fn add_feedback(
Extension(context): Extension<Context>,
Json(req): Json<FeedbackAdd>,
) -> ResResult<ResData<()>> {
) -> ResResult<()> {
validator::validate_params(&req, context.get_lang_tag())?;
service::feedback_service::add_feedback(context, req).await
}
@ -23,7 +23,7 @@ pub async fn add_feedback(
pub async fn get_feedback_list_by_page(
Extension(context): Extension<Context>,
Query(page_params): Query<PageParams>,
) -> ResResult<ResData<Pageable<Feedback>>> {
) -> ResResult<Pageable<Feedback>> {
validator::validate_params(&page_params, context.get_lang_tag())?;
service::feedback_service::get_feedback_list_by_page(
context,

View File

@ -8,8 +8,8 @@ use i18n::message;
use i18n::message_ids::{ACCOUNT_DISABLED, INVALID_TOKEN};
use library::cache::account_cache::{CacheAccount, LOGIN_CACHE};
use library::context::{Context, WhiteContext};
use library::res::response::ResErr::ErrPerm;
use library::res::response::{ResData, ResErr, ResResult};
use library::model::response::ResErr::ErrPerm;
use library::model::response::{ResData, ResErr, ResResult};
use library::social::google::GOOGLE_SOCIAL;
use library::token::{generate_refresh_token, generate_token};
use library::{db, token};
@ -17,7 +17,7 @@ use library::{db, token};
pub async fn authenticate_google(
context: WhiteContext,
req: AuthenticateGooleAccountReq,
) -> ResResult<ResData<LoginAccount>> {
) -> ResResult<LoginAccount> {
let verify_result = GOOGLE_SOCIAL
.verify_id_token(&req.id_token.unwrap())
.await
@ -79,13 +79,13 @@ pub async fn authenticate_google(
token,
refresh_token,
};
return Ok(ResData::some(login_account));
return Ok(login_account);
}
pub async fn refresh_token(
context: Context,
refresh_token: String,
) -> ResResult<ResData<RefreshTokenResult>> {
) -> ResResult<RefreshTokenResult> {
let account = context.account.clone();
if token::verify_refresh_token(&refresh_token).is_err() {
@ -108,5 +108,5 @@ pub async fn refresh_token(
)
.await;
Ok(ResData::some(refresh_token))
Ok(refresh_token)
}

View File

@ -2,26 +2,26 @@ use domain::dto::feedback::FeedbackAdd;
use domain::entities::feedback::Feedback;
use library::context::Context;
use library::db;
use library::res::pageable::Pageable;
use library::res::response::{ResData, ResResult};
use library::model::pageable::Pageable;
use library::model::response::{ResData, ResResult};
/// 获取反馈信息列表
pub async fn get_feedback_list_by_page(
context: Context,
page: i64,
page_size: i64
) -> ResResult<ResData<Pageable<Feedback>>> {
) -> ResResult<Pageable<Feedback>> {
if !context.account.role.is_admin() {
tracing::error!("非管理员用户,无法获取反馈信息列表");
return Ok(ResData::some(Pageable::<Feedback>::empty()));
return Ok(Pageable::<Feedback>::empty());
}
let feedback_list = Feedback::search_feedback(page, page_size, db!()).await.ok();
if feedback_list.is_none() {
tracing::error!("反馈信息为空");
return Ok(ResData::some(Pageable::<Feedback>::empty()));
return Ok(Pageable::<Feedback>::empty());
}
let total = get_feedback_count().await;
Ok(ResData::some(Pageable::new(feedback_list.unwrap(), total)))
Ok(Pageable::new(feedback_list.unwrap(), total))
}
/// 获取反馈信息总数
@ -38,7 +38,7 @@ async fn get_feedback_count() -> i64 {
pub async fn add_feedback(
context: Context,
req: FeedbackAdd
) -> ResResult<ResData<()>> {
) -> ResResult<()> {
let account = context.account;
match Feedback::add_feedback(&mut Feedback{
user_id: account.id.clone(),
@ -50,9 +50,9 @@ pub async fn add_feedback(
}
Err(err) => {
tracing::error!(error = ?err, "添加反馈信息失败");
return Err(library::res::response::ResErr::ErrService(None));
return Err(library::model::response::ResErr::ErrService(None));
}
}
Ok(ResData::none())
Ok(())
}

View File

@ -11,14 +11,14 @@ use i18n::{
message_ids::{ACCOUNT_DISABLED, ACCOUNT_NO_PERMISSION, INCORRECT_USERNAME_OR_PASSWORD},
};
use library::{
cache::account_cache::{CacheAccount, LOGIN_CACHE}, context::WhiteContext, db, res::response::{ResData, ResErr, ResResult}, token::{generate_refresh_token, generate_token}
cache::account_cache::{CacheAccount, LOGIN_CACHE}, context::WhiteContext, db, model::response::{ResData, ResErr, ResResult}, token::{generate_refresh_token, generate_token}
};
/// 登录, 使用账号和密码
pub async fn authenticate_with_password(
context: WhiteContext,
req: AuthenticateWithPassword,
) -> ResResult<ResData<LoginAccount>> {
) -> ResResult<LoginAccount> {
let account =
Account::find_with_password(req.username.unwrap(), req.password.unwrap(), db!()).await?;
if account.is_none() {
@ -69,11 +69,11 @@ pub async fn authenticate_with_password(
token,
refresh_token,
};
Ok(ResData::some(login_account))
Ok(login_account)
}
/// 仅仅用于管理员维护,不对外暴露接口
pub async fn add_account() -> ResResult<ResData<()>> {
pub async fn add_account() -> ResResult<()> {
let account = Account::add_account(
&Account {
username: "admin".to_string(),
@ -88,5 +88,5 @@ pub async fn add_account() -> ResResult<ResData<()>> {
tracing::error!("添加用户失败");
return Err(ResErr::service("添加用户失败"));
}
Ok(ResData(None))
Ok(())
}