网络参数校验错误消息接入i18n

This commit is contained in:
李运家 2024-06-21 10:00:07 +08:00
parent 70162a37f6
commit 9a7271cf83
15 changed files with 67 additions and 44 deletions

1
Cargo.lock generated
View File

@ -477,6 +477,7 @@ name = "domain"
version = "0.1.0"
dependencies = [
"chrono",
"i18n",
"serde",
"serde_json",
"sqlx",

View File

@ -3,19 +3,17 @@ use domain::{dto::account::{AuthenticateGooleAccountReq, AuthenticateWithPasswor
use library::{context::Context, res::{response::{ ResData, ResResult}, validator}};
pub async fn authenticate_google(
Extension(context): Extension<Context>,
Json(req): Json<AuthenticateGooleAccountReq>
) -> ResResult<ResData<LoginAccount>> {
validator::validate_params(&req, "")?;
service::account::authenticate_google(context, req).await
validator::validate_params(&req, &req.lang_tag.to_owned().unwrap())?;
service::account::authenticate_google(req).await
}
pub async fn authenticate_with_password(
Extension(context): Extension<Context>,
Json(req): Json<AuthenticateWithPassword>
) -> ResResult<ResData<LoginAccount>> {
validator::validate_params(&req, "")?;
service::sys_account::authenticate_with_password(context, req).await
validator::validate_params(&req, &req.lang_tag.to_owned().unwrap())?;
service::sys_account::authenticate_with_password(req).await
}
pub async fn refresh_token(

View File

@ -1,5 +1,6 @@
use axum::extract::Query;
use axum::Json;
use axum::{Extension, Json};
use library::context::Context;
use validator::Validate;
use domain::dto::feedback::FeedbackAdd;
use domain::dto::pageable::PageParams;
@ -9,11 +10,12 @@ use library::res::response::{ResData, ResResult};
/// 添加反馈信息
pub async fn add_feedback(
Extension(context): Extension<Context>,
Json(req): Json<FeedbackAdd>
) -> ResResult<ResData<()>> {
req.validate()?;
service::feedback::add_feedback(req).await
service::feedback::add_feedback(context, req).await
}
/// 获取反馈信息列表

View File

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

View File

@ -3,16 +3,20 @@ use validator::Validate;
#[derive(Debug, Validate, Deserialize, Serialize)]
pub struct AuthenticateWithPassword {
#[validate(required(message = "用户名不能为空"), length(min = 1, message = "用户名不能为空"))]
#[validate(required(message = "VALIDATE_ACCOUNT_NAME_REQUIRED"), length(min = 1, message = "VALIDATE_ACCOUNT_NAME_REQUIRED"))]
pub username: Option<String>,
#[validate(required(message = "密码不能为空"), length(min = 1, message = "密码不能为空"))]
#[validate(required(message = "VALIDATE_ACCOUNT_PASSWORD_REQUIRED"), length(min = 1, message = "VALIDATE_ACCOUNT_PASSWORD_REQUIRED"))]
pub password: Option<String>,
#[validate(required(message = "VALIDATE_ACCOUNT_LANG_TAG_REQUIRED"), length(min = 1, message = "VALIDATE_ACCOUNT_LANG_TAG_REQUIRED"))]
pub lang_tag: Option<String>,
}
#[derive(Debug, Validate, Deserialize, Serialize)]
pub struct AuthenticateGooleAccountReq {
#[validate(required(message = "用户ID Token不能为空"), length(min = 1, message = "用户ID Token不能为空"))]
#[validate(required(message = "VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED"), length(min = 1, message = "VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED"))]
pub id_token: Option<String>,
#[validate(required(message = "VALIDATE_ACCOUNT_LANG_TAG_REQUIRED"), length(min = 1, message = "VALIDATE_ACCOUNT_LANG_TAG_REQUIRED"))]
pub lang_tag: Option<String>,
}
#[derive(Debug, Validate, Deserialize, Serialize)]

View File

@ -1,10 +1,9 @@
use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Validate, Deserialize, Serialize)]
pub struct FeedbackAdd {
#[validate(required(message = "用户ID不能为空"))]
pub user_id: Option<i64>,
#[validate(required(message = "反馈内容不能为空"), length(min = 1, message = "反馈内容不能为空"))]
#[validate(required(message = "VALIDATE_FEEDBACK_CONTENT_REQUIRED"), length(min = 1, message = "VALIDATE_FEEDBACK_CONTENT_REQUIRED"))]
pub content: Option<String>
}

View File

@ -7,7 +7,7 @@ use crate::db_result::CountResult;
#[derive(Debug, Clone, Deserialize, Default, Serialize)]
pub struct Feedback {
pub id: i64,
pub user_id: i64,
pub user_id: String,
pub content: String,
pub created_at: NaiveDateTime,
}

View File

@ -3,10 +3,10 @@ use std::collections::HashMap;
use lazy_static::lazy_static;
use crate::message_ids::{
ACCOUNT_DISABLED, ACCOUNT_NO_PERMISSION, HELLO, INCORRECT_USERNAME_OR_PASSWORD, INVALID_TOKEN,
ACCOUNT_DISABLED, ACCOUNT_NO_PERMISSION, HELLO, INCORRECT_USERNAME_OR_PASSWORD, INVALID_TOKEN, VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED, VALIDATE_ACCOUNT_LANG_TAG_REQUIRED, VALIDATE_ACCOUNT_NAME_REQUIRED, VALIDATE_ACCOUNT_PASSWORD_REQUIRED, VALIDATE_FEEDBACK_CONTENT_REQUIRED
};
pub const LANGUAGE_ID: &str = "en-US";
pub const LANGUAGE_ID: &str = "en_US";
lazy_static! {
pub static ref MESSAGE: HashMap<&'static str, &'static str> = {
@ -14,11 +14,14 @@ lazy_static! {
map.insert(HELLO, "hello {}");
map.insert(ACCOUNT_DISABLED, "account is disabled");
map.insert(ACCOUNT_NO_PERMISSION, "account has no permission");
map.insert(
INCORRECT_USERNAME_OR_PASSWORD,
"incorrect username or password",
);
map.insert(INCORRECT_USERNAME_OR_PASSWORD,"incorrect username or password");
map.insert(INVALID_TOKEN, "invalid token");
map.insert(VALIDATE_FEEDBACK_CONTENT_REQUIRED, "feedback content is required");
map.insert(VALIDATE_ACCOUNT_NAME_REQUIRED, "username is required");
map.insert(VALIDATE_ACCOUNT_PASSWORD_REQUIRED, "password is required");
map.insert(VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED, "ID Token is required");
map.insert(VALIDATE_ACCOUNT_LANG_TAG_REQUIRED, "lang tag is required");
map
};
}

View File

@ -19,7 +19,7 @@ fn get_i18n() -> &'static HashMap<&'static str, HashMap<&'static str, &'static s
I18N.get_or_init(init_i18n)
}
pub fn lang(lang_id: &str, message_id: &'static str) -> &'static str {
pub fn lang(lang_id: &str, message_id: &str) -> &'static str {
get_i18n()
.get(lang_id)
.and_then(|map| map.get(message_id))

View File

@ -1,5 +1,11 @@
pub const HELLO: &str = "hello";
pub const ACCOUNT_DISABLED: &str = "accout is disabled";
pub const ACCOUNT_NO_PERMISSION: &str = "account has no permission";
pub const INCORRECT_USERNAME_OR_PASSWORD: &str = "Incorrect username or password";
pub const INVALID_TOKEN: &str = "invalid token";
pub const HELLO: &str = "HELLO";
pub const ACCOUNT_DISABLED: &str = "ACCOUNT_DISABLED";
pub const ACCOUNT_NO_PERMISSION: &str = "ACCOUNT_NO_PERMISSION";
pub const INCORRECT_USERNAME_OR_PASSWORD: &str = "INCORRECT_USERNAME_OR_PASSWORD";
pub const INVALID_TOKEN: &str = "INVALID_TOKEN";
pub const VALIDATE_FEEDBACK_CONTENT_REQUIRED: &'static str = "VALIDATE_FEEDBACK_CONTENT_REQUIRED";
pub const VALIDATE_ACCOUNT_NAME_REQUIRED: &'static str = "VALIDATE_ACCOUNT_NAME_REQUIRED";
pub const VALIDATE_ACCOUNT_PASSWORD_REQUIRED: &'static str = "VALIDATE_ACCOUNT_PASSWORD_REQUIRED";
pub const VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED: &'static str = "VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED";
pub const VALIDATE_ACCOUNT_LANG_TAG_REQUIRED: &'static str = "VALIDATE_ACCOUNT_LANG_TAG_REQUIRED";

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use lazy_static::lazy_static;
use crate::message_ids::{
ACCOUNT_DISABLED, ACCOUNT_NO_PERMISSION, HELLO, INCORRECT_USERNAME_OR_PASSWORD, INVALID_TOKEN,
ACCOUNT_DISABLED, ACCOUNT_NO_PERMISSION, HELLO, INCORRECT_USERNAME_OR_PASSWORD, INVALID_TOKEN, VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED, VALIDATE_ACCOUNT_LANG_TAG_REQUIRED, VALIDATE_ACCOUNT_NAME_REQUIRED, VALIDATE_ACCOUNT_PASSWORD_REQUIRED, VALIDATE_FEEDBACK_CONTENT_REQUIRED
};
pub const LANGUAGE_ID: &str = "zh_CN";
@ -16,6 +16,12 @@ lazy_static! {
map.insert(ACCOUNT_NO_PERMISSION, "账户无权限");
map.insert(INCORRECT_USERNAME_OR_PASSWORD, "用户名或密码错误");
map.insert(INVALID_TOKEN, "无效令牌");
map.insert(VALIDATE_FEEDBACK_CONTENT_REQUIRED, "反馈内容不能为空");
map.insert(VALIDATE_ACCOUNT_NAME_REQUIRED, "用户名称不能为空");
map.insert(VALIDATE_ACCOUNT_PASSWORD_REQUIRED, "密码不能为空");
map.insert(VALIDATE_ACCOUNT_ID_TOKEN_REQUIRED, "用户ID Token不能为空");
map.insert(VALIDATE_ACCOUNT_LANG_TAG_REQUIRED, "用户语言标识不能为空");
map
};
}

View File

@ -1,8 +1,9 @@
use i18n::message;
use validator::Validate;
use super::response::{ResData, ResErr, ResResult};
pub fn validate_params(params: &impl Validate, _local: &str) -> ResResult<ResData<()>> {
pub fn validate_params(params: &impl Validate, local: &str) -> ResResult<ResData<()>> {
let validate_err = params.validate();
match validate_err {
Ok(_) => Result::Ok(ResData::none()),
@ -14,8 +15,7 @@ pub fn validate_params(params: &impl Validate, _local: &str) -> ResResult<ResDat
err.field_errors().iter().for_each(|(_field, errs)| {
errs.iter().for_each(|e| {
let msg = e.message.clone().unwrap_or_default();
let msg_str = msg.to_string();
errors.push(msg_str)
errors.push(message!(local, msg.trim()))
});
});
},
@ -24,8 +24,7 @@ pub fn validate_params(params: &impl Validate, _local: &str) -> ResResult<ResDat
err.field_errors().iter().for_each(|(_field, errs)| {
errs.iter().for_each(|e| {
let msg = e.message.clone().unwrap_or_default();
let msg_str = msg.to_string();
errors.push(msg_str)
errors.push(message!(local, msg.trim()))
});
});
}
@ -33,8 +32,7 @@ pub fn validate_params(params: &impl Validate, _local: &str) -> ResResult<ResDat
validator::ValidationErrorsKind::Field(err) => {
err.iter().for_each(|e| {
let msg = e.message.clone().unwrap_or_default();
let msg_str = msg.to_string();
errors.push(msg_str)
errors.push(message!(local, msg.trim()))
});
},
};

View File

@ -15,9 +15,9 @@ use library::token::{generate_refresh_token, generate_token};
use library::{db, token};
pub async fn authenticate_google(
context: Context,
req: AuthenticateGooleAccountReq,
) -> ResResult<ResData<LoginAccount>> {
let lang_tag = req.lang_tag.unwrap();
let verify_result = GOOGLE_SOCIAL
.verify_id_token(&req.id_token.unwrap())
.await
@ -47,7 +47,7 @@ pub async fn authenticate_google(
tracing::info!("账户已存在, {:?}", account);
if account.disable_time > Utc::now() {
tracing::error!("账户已禁用");
return Err(ResErr::system(message!(context.get_lang_id(), ACCOUNT_DISABLED)));
return Err(ResErr::system(message!(&lang_tag, ACCOUNT_DISABLED)));
}
account
}

View File

@ -1,5 +1,6 @@
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};
@ -26,9 +27,13 @@ async fn get_feedback_count() -> i64 {
}
/// 添加反馈信息
pub async fn add_feedback(req: FeedbackAdd) -> ResResult<ResData<()>> {
pub async fn add_feedback(
context: Context,
req: FeedbackAdd
) -> ResResult<ResData<()>> {
let account = context.account;
match Feedback::add_feedback(&mut Feedback{
user_id: req.user_id.unwrap(),
user_id: account.id.clone(),
content: req.content.unwrap(),
..Default::default()
}, db!()).await {

View File

@ -12,22 +12,21 @@ use i18n::{
};
use library::{
cache::account_cache::{CacheAccount, LOGIN_CACHE},
context::Context,
db,
res::response::{ResData, ResErr, ResResult},
token::{generate_refresh_token, generate_token},
};
pub async fn authenticate_with_password(
context: Context,
req: AuthenticateWithPassword,
) -> ResResult<ResData<LoginAccount>> {
let lang_tag = req.lang_tag.unwrap();
let account =
Account::find_with_password(req.username.unwrap(), req.password.unwrap(), db!()).await?;
if account.is_none() {
tracing::info!("登录用户失败,用户查询为空");
return Err(ResErr::params(message!(
context.get_lang_id(),
&lang_tag,
INCORRECT_USERNAME_OR_PASSWORD
)));
}
@ -35,14 +34,14 @@ pub async fn authenticate_with_password(
if account.disable_time > Utc::now() {
tracing::error!("账户已禁用");
return Err(ResErr::auth(message!(
context.get_lang_id(),
&lang_tag,
ACCOUNT_DISABLED
)));
}
if !account.role.is_admin() {
tracing::error!("账户不是管理员,无权限");
return Err(ResErr::perm(message!(
context.get_lang_id(),
&lang_tag,
ACCOUNT_NO_PERMISSION
)));
}