获取组织、用户等接口新增或更新
This commit is contained in:
parent
61de18077e
commit
8791aee96c
@ -36,11 +36,6 @@ pub trait OrgAccessor {
|
|||||||
&self,
|
&self,
|
||||||
parent_id: i32,
|
parent_id: i32,
|
||||||
) -> Result<Vec<OrganizationModel>, DbAccessError>;
|
) -> 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(
|
async fn update_org_name(
|
||||||
&self,
|
&self,
|
||||||
@ -175,9 +170,14 @@ impl OrgAccessor for RtsaDbAccessor {
|
|||||||
);
|
);
|
||||||
let row = sqlx::query_as::<_, OrganizationModel>(&sql)
|
let row = sqlx::query_as::<_, OrganizationModel>(&sql)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.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> {
|
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)
|
let row = sqlx::query_as::<_, OrganizationModel>(&sql)
|
||||||
.bind(code)
|
.bind(code)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(row)
|
match row {
|
||||||
|
Some(row) => Ok(row),
|
||||||
|
None => Err(DbAccessError::RowNotExist(
|
||||||
|
OrganizationColumn::Table.name().to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query_org_top_page(
|
async fn query_org_top_page(
|
||||||
@ -292,23 +297,6 @@ impl OrgAccessor for RtsaDbAccessor {
|
|||||||
Ok(rows)
|
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(
|
async fn update_org_name(
|
||||||
&self,
|
&self,
|
||||||
id: i32,
|
id: i32,
|
||||||
@ -642,7 +630,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[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 accessor = RtsaDbAccessor::new(pool);
|
||||||
let user = init_user(&accessor).await?;
|
let user = init_user(&accessor).await?;
|
||||||
|
|
||||||
@ -661,7 +649,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query organization names by ids
|
// 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);
|
assert_eq!(names.len(), 3);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -387,9 +387,14 @@ impl OrgUserAccessor for RtsaDbAccessor {
|
|||||||
let org_user = sqlx::query_as(&query_clause)
|
let org_user = sqlx::query_as(&query_clause)
|
||||||
.bind(org_id)
|
.bind(org_id)
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.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(
|
async fn query_org_user_by_student_id(
|
||||||
@ -528,7 +533,7 @@ mod tests {
|
|||||||
accessor.unbind_org_user(org.id, new_user.id).await?;
|
accessor.unbind_org_user(org.id, new_user.id).await?;
|
||||||
// Verify the user is unbound
|
// Verify the user is unbound
|
||||||
let result = accessor.query_org_user(org.id, new_user.id).await;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,11 +344,16 @@ impl UserAccessor for RtsaDbAccessor {
|
|||||||
let table = UserColumn::Table.name();
|
let table = UserColumn::Table.name();
|
||||||
let id_column = UserColumn::Id.name();
|
let id_column = UserColumn::Id.name();
|
||||||
let query_clause = format!("SELECT * FROM {table} WHERE {id_column} = {id}",);
|
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)
|
.bind(id)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.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> {
|
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)
|
let user = sqlx::query_as(&query_clause)
|
||||||
.bind(username)
|
.bind(username)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.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> {
|
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)
|
let user = sqlx::query_as(&query_clause)
|
||||||
.bind(email)
|
.bind(email)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.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> {
|
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)
|
let user = sqlx::query_as(&query_clause)
|
||||||
.bind(mobile)
|
.bind(mobile)
|
||||||
.fetch_one(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
.await?;
|
.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> {
|
async fn update_user_info(&self, id: i32, info: Value) -> Result<UserModel, DbAccessError> {
|
||||||
|
@ -28,6 +28,17 @@ impl OrgQuery {
|
|||||||
Ok(org.into())
|
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> {
|
async fn get_default_org(&self, ctx: &Context<'_>) -> async_graphql::Result<OrgDto> {
|
||||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||||
|
@ -8,6 +8,7 @@ use async_graphql::{
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use rtsa_db::prelude::*;
|
use rtsa_db::prelude::*;
|
||||||
use rtsa_dto::common::Role;
|
use rtsa_dto::common::Role;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
use super::{org::OrgId, user::UserId};
|
use super::{org::OrgId, user::UserId};
|
||||||
|
|
||||||
@ -33,6 +34,38 @@ impl OrgUserQuery {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(org_user.into())
|
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]
|
#[Object]
|
||||||
|
@ -2,7 +2,10 @@ use std::{collections::HashMap, sync::Arc};
|
|||||||
|
|
||||||
use async_graphql::{dataloader::Loader, Context, InputObject, Object, SimpleObject};
|
use async_graphql::{dataloader::Loader, Context, InputObject, Object, SimpleObject};
|
||||||
use chrono::NaiveDateTime;
|
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 serde_json::json;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -35,8 +38,8 @@ impl UserQuery {
|
|||||||
/// 获取用户信息
|
/// 获取用户信息
|
||||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||||
async fn login_user_info(&self, ctx: &Context<'_>) -> async_graphql::Result<UserDto> {
|
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 db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
|
let claims = ctx.data::<Jwt>()?.decode()?;
|
||||||
let user = db_accessor.query_user(claims.uid).await?;
|
let user = db_accessor.query_user(claims.uid).await?;
|
||||||
Ok(user.into())
|
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)]
|
#[derive(Debug, SimpleObject)]
|
||||||
pub struct UserDto {
|
pub struct UserDto {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
@ -2,6 +2,7 @@ use async_graphql::Guard;
|
|||||||
use rtsa_db::prelude::*;
|
use rtsa_db::prelude::*;
|
||||||
use rtsa_dto::common::Role;
|
use rtsa_dto::common::Role;
|
||||||
use rtsa_log::tracing::{info, warn};
|
use rtsa_log::tracing::{info, warn};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
mod jwt_auth;
|
mod jwt_auth;
|
||||||
use crate::{apis::UserLoginDto, error::BusinessError, sys_init::DEFAULT_ORG_CODE};
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::sys_init::{init_default_user_and_org, ADMIN_USER_NAME, ADMIN_USER_PASSWORD};
|
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);
|
assert_eq!(claims.oid, org.id);
|
||||||
Ok(())
|
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