获取组织、用户等接口新增或更新
This commit is contained in:
parent
61de18077e
commit
8791aee96c
@ -36,11 +36,6 @@ pub trait OrgAccessor {
|
||||
&self,
|
||||
parent_id: i32,
|
||||
) -> Result<Vec<OrganizationModel>, DbAccessError>;
|
||||
/// 通过id列表获取组织code和name
|
||||
async fn query_org_name_by_ids(
|
||||
&self,
|
||||
ids: Vec<i32>,
|
||||
) -> Result<Vec<(i32, String)>, DbAccessError>;
|
||||
/// 更新组织名称
|
||||
async fn update_org_name(
|
||||
&self,
|
||||
@ -175,9 +170,14 @@ impl OrgAccessor for RtsaDbAccessor {
|
||||
);
|
||||
let row = sqlx::query_as::<_, OrganizationModel>(&sql)
|
||||
.bind(id)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
match row {
|
||||
Some(row) => Ok(row),
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
OrganizationColumn::Table.name().to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_org_names(&self, ids: &[i32]) -> Result<Vec<(i32, String)>, DbAccessError> {
|
||||
@ -204,9 +204,14 @@ impl OrgAccessor for RtsaDbAccessor {
|
||||
);
|
||||
let row = sqlx::query_as::<_, OrganizationModel>(&sql)
|
||||
.bind(code)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
match row {
|
||||
Some(row) => Ok(row),
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
OrganizationColumn::Table.name().to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_org_top_page(
|
||||
@ -292,23 +297,6 @@ impl OrgAccessor for RtsaDbAccessor {
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
async fn query_org_name_by_ids(
|
||||
&self,
|
||||
ids: Vec<i32>,
|
||||
) -> Result<Vec<(i32, String)>, DbAccessError> {
|
||||
let table = OrganizationColumn::Table.name();
|
||||
let id_column = OrganizationColumn::Id.name();
|
||||
let name_column = OrganizationColumn::Name.name();
|
||||
let sql = format!(
|
||||
"SELECT {id_column}, {name_column} FROM {table} WHERE {id_column} = ANY($1)",
|
||||
id_column = id_column,
|
||||
name_column = name_column,
|
||||
table = table
|
||||
);
|
||||
let rows: Vec<(i32, String)> = sqlx::query_as(&sql).bind(ids).fetch_all(&self.pool).await?;
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
async fn update_org_name(
|
||||
&self,
|
||||
id: i32,
|
||||
@ -642,7 +630,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_query_org_name_by_ids(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
async fn test_query_org_names(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
let accessor = RtsaDbAccessor::new(pool);
|
||||
let user = init_user(&accessor).await?;
|
||||
|
||||
@ -661,7 +649,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Query organization names by ids
|
||||
let names = accessor.query_org_name_by_ids(ids).await?;
|
||||
let names = accessor.query_org_names(&ids).await?;
|
||||
assert_eq!(names.len(), 3);
|
||||
|
||||
Ok(())
|
||||
|
@ -387,9 +387,14 @@ impl OrgUserAccessor for RtsaDbAccessor {
|
||||
let org_user = sqlx::query_as(&query_clause)
|
||||
.bind(org_id)
|
||||
.bind(user_id)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(org_user)
|
||||
match org_user {
|
||||
Some(org_user) => Ok(org_user),
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
OrganizationUserColumn::Table.name().to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_org_user_by_student_id(
|
||||
@ -528,7 +533,7 @@ mod tests {
|
||||
accessor.unbind_org_user(org.id, new_user.id).await?;
|
||||
// Verify the user is unbound
|
||||
let result = accessor.query_org_user(org.id, new_user.id).await;
|
||||
assert!(matches!(result, Err(DbAccessError::SqlxError(_))));
|
||||
assert!(matches!(result, Err(DbAccessError::RowNotExist(_))));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -344,11 +344,16 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
let table = UserColumn::Table.name();
|
||||
let id_column = UserColumn::Id.name();
|
||||
let query_clause = format!("SELECT * FROM {table} WHERE {id_column} = {id}",);
|
||||
let user: UserModel = sqlx::query_as(&query_clause)
|
||||
let user = sqlx::query_as(&query_clause)
|
||||
.bind(id)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(user)
|
||||
match user {
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
UserColumn::Table.name().to_string(),
|
||||
)),
|
||||
Some(user) => Ok(user),
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_user_by_username(&self, username: &str) -> Result<UserModel, DbAccessError> {
|
||||
@ -361,9 +366,14 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
);
|
||||
let user = sqlx::query_as(&query_clause)
|
||||
.bind(username)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(user)
|
||||
match user {
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
UserColumn::Table.name().to_string(),
|
||||
)),
|
||||
Some(user) => Ok(user),
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_user_by_email(&self, email: &str) -> Result<UserModel, DbAccessError> {
|
||||
@ -376,9 +386,14 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
);
|
||||
let user = sqlx::query_as(&query_clause)
|
||||
.bind(email)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(user)
|
||||
match user {
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
UserColumn::Table.name().to_string(),
|
||||
)),
|
||||
Some(user) => Ok(user),
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_user_by_mobile(&self, mobile: &str) -> Result<UserModel, DbAccessError> {
|
||||
@ -391,9 +406,14 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
);
|
||||
let user = sqlx::query_as(&query_clause)
|
||||
.bind(mobile)
|
||||
.fetch_one(&self.pool)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
Ok(user)
|
||||
match user {
|
||||
None => Err(DbAccessError::RowNotExist(
|
||||
UserColumn::Table.name().to_string(),
|
||||
)),
|
||||
Some(user) => Ok(user),
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_user_info(&self, id: i32, info: Value) -> Result<UserModel, DbAccessError> {
|
||||
|
@ -28,6 +28,17 @@ impl OrgQuery {
|
||||
Ok(org.into())
|
||||
}
|
||||
|
||||
/// 通过组织代码获取组织信息
|
||||
async fn get_org_by_code(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
code: String,
|
||||
) -> async_graphql::Result<OrgDto> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let org = dba.query_org_by_code(&code).await?;
|
||||
Ok(org.into())
|
||||
}
|
||||
|
||||
/// 获取默认组织信息
|
||||
async fn get_default_org(&self, ctx: &Context<'_>) -> async_graphql::Result<OrgDto> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
|
@ -8,6 +8,7 @@ use async_graphql::{
|
||||
use chrono::NaiveDateTime;
|
||||
use rtsa_db::prelude::*;
|
||||
use rtsa_dto::common::Role;
|
||||
use serde_json::json;
|
||||
|
||||
use super::{org::OrgId, user::UserId};
|
||||
|
||||
@ -33,6 +34,38 @@ impl OrgUserQuery {
|
||||
.await?;
|
||||
Ok(org_user.into())
|
||||
}
|
||||
|
||||
/// 获取登录用户的组织用户信息
|
||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||
async fn login_org_user_info(&self, ctx: &Context<'_>) -> async_graphql::Result<OrgUserDto> {
|
||||
let claims = ctx.data::<Jwt>()?.decode()?;
|
||||
let org_user = ctx
|
||||
.data::<RtsaDbAccessor>()?
|
||||
.query_org_user(claims.oid, claims.uid)
|
||||
.await
|
||||
.or_else(|e| {
|
||||
match e {
|
||||
DbAccessError::RowNotExist(_) => {
|
||||
// 组织用户不存在,构造组织游客角色用户
|
||||
let org_user = OrganizationUserModel {
|
||||
id: 0,
|
||||
organization_id: claims.oid,
|
||||
user_id: claims.uid,
|
||||
student_id: None,
|
||||
roles: json!([Role::OrgGuest]),
|
||||
info: None,
|
||||
creator_id: claims.uid,
|
||||
created_at: chrono::Local::now(),
|
||||
updater_id: claims.uid,
|
||||
updated_at: chrono::Local::now(),
|
||||
};
|
||||
Ok(org_user)
|
||||
}
|
||||
_ => Err(e),
|
||||
}
|
||||
})?;
|
||||
Ok(org_user.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[Object]
|
||||
|
@ -2,7 +2,10 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use async_graphql::{dataloader::Loader, Context, InputObject, Object, SimpleObject};
|
||||
use chrono::NaiveDateTime;
|
||||
use rtsa_db::{model::UserModel, DbAccessError, RtsaDbAccessor, UserAccessor};
|
||||
use rtsa_db::{
|
||||
model::{OrganizationModel, OrganizationUserModel, UserModel},
|
||||
DbAccessError, RtsaDbAccessor, UserAccessor,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
@ -35,8 +38,8 @@ impl UserQuery {
|
||||
/// 获取用户信息
|
||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||
async fn login_user_info(&self, ctx: &Context<'_>) -> async_graphql::Result<UserDto> {
|
||||
let claims = ctx.data::<Jwt>()?.decode()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let claims = ctx.data::<Jwt>()?.decode()?;
|
||||
let user = db_accessor.query_user(claims.uid).await?;
|
||||
Ok(user.into())
|
||||
}
|
||||
@ -170,6 +173,46 @@ impl From<UserQueryDto> for rtsa_db::UserPageFilter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SimpleObject)]
|
||||
pub struct LoginUserInfoDto {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub nickname: String,
|
||||
pub mobile: Option<String>,
|
||||
pub email: Option<String>,
|
||||
/// 用户角色
|
||||
pub roles: Vec<Role>,
|
||||
/// 组织
|
||||
pub org: LoginUserOrgInfoDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, SimpleObject)]
|
||||
pub struct LoginUserOrgInfoDto {
|
||||
pub org_id: i32,
|
||||
pub org_code: Option<String>,
|
||||
pub org_name: String,
|
||||
pub roles: Vec<Role>,
|
||||
}
|
||||
|
||||
impl From<(UserModel, OrganizationUserModel, OrganizationModel)> for LoginUserInfoDto {
|
||||
fn from(value: (UserModel, OrganizationUserModel, OrganizationModel)) -> Self {
|
||||
Self {
|
||||
id: value.0.id,
|
||||
username: value.0.username.clone(),
|
||||
nickname: value.0.nickname.clone(),
|
||||
mobile: value.0.mobile.clone(),
|
||||
email: value.0.email.clone(),
|
||||
roles: serde_json::from_value(value.0.roles).unwrap(),
|
||||
org: LoginUserOrgInfoDto {
|
||||
org_id: value.2.id,
|
||||
org_code: value.2.code,
|
||||
org_name: value.2.name,
|
||||
roles: serde_json::from_value(value.1.roles).unwrap(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SimpleObject)]
|
||||
pub struct UserDto {
|
||||
pub id: i32,
|
||||
|
@ -2,6 +2,7 @@ use async_graphql::Guard;
|
||||
use rtsa_db::prelude::*;
|
||||
use rtsa_dto::common::Role;
|
||||
use rtsa_log::tracing::{info, warn};
|
||||
use serde_json::json;
|
||||
|
||||
mod jwt_auth;
|
||||
use crate::{apis::UserLoginDto, error::BusinessError, sys_init::DEFAULT_ORG_CODE};
|
||||
@ -120,6 +121,36 @@ pub(crate) async fn handle_login(
|
||||
}
|
||||
}
|
||||
|
||||
/// 查询登录用户、组织等信息
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn query_login_user_info(
|
||||
db_accessor: &RtsaDbAccessor,
|
||||
claims: Claims,
|
||||
) -> Result<(UserModel, OrganizationUserModel, OrganizationModel), BusinessError> {
|
||||
let user = db_accessor.query_user(claims.uid).await?;
|
||||
let org = db_accessor.query_org(claims.oid).await?;
|
||||
let org_user = db_accessor.query_org_user(claims.oid, claims.uid).await;
|
||||
match org_user {
|
||||
Ok(org_user) => Ok((user, org_user, org)),
|
||||
Err(_) => {
|
||||
// 组织用户不存在,构造组织游客角色用户
|
||||
let org_user = OrganizationUserModel {
|
||||
id: 0,
|
||||
organization_id: claims.oid,
|
||||
user_id: claims.uid,
|
||||
student_id: None,
|
||||
roles: json!([Role::OrgGuest]),
|
||||
info: None,
|
||||
creator_id: 0,
|
||||
created_at: chrono::Local::now(),
|
||||
updater_id: 0,
|
||||
updated_at: chrono::Local::now(),
|
||||
};
|
||||
Ok((user, org_user, org))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::sys_init::{init_default_user_and_org, ADMIN_USER_NAME, ADMIN_USER_PASSWORD};
|
||||
@ -154,4 +185,24 @@ mod tests {
|
||||
assert_eq!(claims.oid, org.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "rtsa_db::MIGRATOR")]
|
||||
async fn test_query_login_user_info(pool: PgPool) -> anyhow::Result<()> {
|
||||
Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = RtsaDbAccessor::new(pool);
|
||||
rtsa_db::set_default_db_accessor(accessor.clone());
|
||||
init_default_user_and_org().await.unwrap();
|
||||
|
||||
let info = UserLoginDto {
|
||||
username: ADMIN_USER_NAME.to_string(),
|
||||
password: ADMIN_USER_PASSWORD.to_string(),
|
||||
org_id: None,
|
||||
};
|
||||
let jwt = handle_login(&accessor, info).await.unwrap();
|
||||
let claims = jwt.decode().unwrap();
|
||||
let (user, org_user, org) = query_login_user_info(&accessor, claims).await.unwrap();
|
||||
assert_eq!(user.username, ADMIN_USER_NAME);
|
||||
assert_eq!(org_user.organization_id, org.id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user