299 lines
8.3 KiB
Rust
299 lines
8.3 KiB
Rust
use crate::model_ext::{
|
|
AliasSourceExt, AliasSuffixExt, CursorPaginationExt, EntityPrefixExt, MagIden, ModelPagination,
|
|
};
|
|
use crate::{model_ext::SelectColumnsExt, CalckeyDbError, CalckeyModel};
|
|
use chrono::{DateTime, Utc};
|
|
use ck::{drive_file, follow_request, following, user, user_profile};
|
|
use ext_model_migration::{IntoIden, SelectStatement};
|
|
use magnetar_sdk::types::SpanFilter;
|
|
use sea_orm::{
|
|
ColumnTrait, DbErr, EntityTrait, FromQueryResult, Iden, JoinType, QueryFilter, QueryResult,
|
|
QuerySelect,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UserData {
|
|
pub user: user::Model,
|
|
pub profile: Option<user_profile::Model>,
|
|
pub avatar: Option<drive_file::Model>,
|
|
pub banner: Option<drive_file::Model>,
|
|
}
|
|
|
|
impl FromQueryResult for UserData {
|
|
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
|
|
let prefix = if prefix.is_empty() {
|
|
user::Entity.base_prefix()
|
|
} else {
|
|
MagIden::alias(prefix)
|
|
};
|
|
|
|
Ok(UserData {
|
|
user: user::Model::from_query_result(res, &prefix.to_string())?,
|
|
profile: user_profile::Model::from_query_result_optional(
|
|
res,
|
|
&prefix.join_str_as_str(PROFILE),
|
|
)?,
|
|
avatar: drive_file::Model::from_query_result_optional(
|
|
res,
|
|
&prefix.join_str_as_str(AVATAR),
|
|
)?,
|
|
banner: drive_file::Model::from_query_result_optional(
|
|
res,
|
|
&prefix.join_str_as_str(BANNER),
|
|
)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ModelPagination for UserData {
|
|
fn id(&self) -> &str {
|
|
&self.user.id
|
|
}
|
|
|
|
fn time(&self) -> DateTime<Utc> {
|
|
self.user.created_at.into()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UserFollowData {
|
|
pub follow: following::Model,
|
|
pub user: UserData,
|
|
}
|
|
|
|
impl FromQueryResult for UserFollowData {
|
|
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
|
|
Ok(UserFollowData {
|
|
user: UserData::from_query_result(res, prefix)?,
|
|
follow: following::Model::from_query_result(res, prefix)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ModelPagination for UserFollowData {
|
|
fn id(&self) -> &str {
|
|
&self.follow.id
|
|
}
|
|
|
|
fn time(&self) -> DateTime<Utc> {
|
|
self.follow.created_at.into()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UserFollowRequestData {
|
|
pub follow_request: follow_request::Model,
|
|
pub user: UserData,
|
|
}
|
|
|
|
impl FromQueryResult for UserFollowRequestData {
|
|
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
|
|
Ok(UserFollowRequestData {
|
|
user: UserData::from_query_result(res, prefix)?,
|
|
follow_request: follow_request::Model::from_query_result(res, prefix)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ModelPagination for UserFollowRequestData {
|
|
fn id(&self) -> &str {
|
|
&self.follow_request.id
|
|
}
|
|
|
|
fn time(&self) -> DateTime<Utc> {
|
|
self.follow_request.created_at.into()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct UserResolveOptions {
|
|
pub with_avatar: bool,
|
|
pub with_banner: bool,
|
|
pub with_profile: bool,
|
|
}
|
|
|
|
const PROFILE: &str = "profile.";
|
|
const AVATAR: &str = "avatar.";
|
|
const BANNER: &str = "banner.";
|
|
|
|
#[derive(Clone)]
|
|
pub struct UserResolver {
|
|
db: CalckeyModel,
|
|
}
|
|
|
|
impl UserResolver {
|
|
pub fn new(db: CalckeyModel) -> Self {
|
|
Self { db }
|
|
}
|
|
|
|
pub fn resolve(
|
|
&self,
|
|
q: &mut SelectStatement,
|
|
user_tbl: &MagIden,
|
|
UserResolveOptions {
|
|
with_avatar,
|
|
with_banner,
|
|
with_profile,
|
|
}: &UserResolveOptions,
|
|
) {
|
|
q.add_aliased_columns::<user::Entity>(user_tbl);
|
|
|
|
if *with_profile {
|
|
let profile_tbl = user_tbl.join_str(PROFILE);
|
|
|
|
q.add_aliased_columns::<user_profile::Entity>(&profile_tbl);
|
|
|
|
q.join_columns(
|
|
JoinType::LeftJoin,
|
|
user::Relation::UserProfile.with_from_alias(user_tbl),
|
|
&profile_tbl,
|
|
);
|
|
}
|
|
|
|
if *with_avatar {
|
|
let avatar_tbl = user_tbl.join_str(AVATAR);
|
|
|
|
q.add_aliased_columns::<drive_file::Entity>(&avatar_tbl);
|
|
|
|
q.join_columns(
|
|
JoinType::LeftJoin,
|
|
user::Relation::DriveFile2.with_from_alias(user_tbl),
|
|
&avatar_tbl,
|
|
);
|
|
}
|
|
|
|
if *with_banner {
|
|
let banner_tbl = user_tbl.join_str(BANNER);
|
|
|
|
q.add_aliased_columns::<drive_file::Entity>(&banner_tbl);
|
|
|
|
q.join_columns(
|
|
JoinType::LeftJoin,
|
|
user::Relation::DriveFile1.with_from_alias(user_tbl),
|
|
&banner_tbl,
|
|
);
|
|
}
|
|
}
|
|
|
|
pub async fn get_follow_requests(
|
|
&self,
|
|
options: &UserResolveOptions,
|
|
followee: &str,
|
|
pagination: &SpanFilter,
|
|
prev: &mut Option<SpanFilter>,
|
|
next: &mut Option<SpanFilter>,
|
|
limit: u64,
|
|
) -> Result<Vec<UserData>, CalckeyDbError> {
|
|
let user_tbl = user::Entity.base_prefix();
|
|
|
|
let mut select = follow_request::Entity::find().join_as(
|
|
JoinType::InnerJoin,
|
|
follow_request::Relation::User1.with_to_alias(&user_tbl),
|
|
user_tbl.clone().into_iden(),
|
|
);
|
|
|
|
let query = QuerySelect::query(&mut select);
|
|
self.resolve(query, &user_tbl, options);
|
|
|
|
let followers = select
|
|
.filter(follow_request::Column::FolloweeId.eq(followee))
|
|
.get_paginated_model::<UserFollowRequestData, _, _>(
|
|
&self.db.0,
|
|
None,
|
|
(
|
|
follow_request::Column::CreatedAt,
|
|
follow_request::Column::Id,
|
|
),
|
|
pagination,
|
|
prev,
|
|
next,
|
|
limit,
|
|
)
|
|
.await?
|
|
.into_iter()
|
|
.map(|u| u.user)
|
|
.collect();
|
|
|
|
Ok(followers)
|
|
}
|
|
|
|
pub async fn get_followees(
|
|
&self,
|
|
options: &UserResolveOptions,
|
|
follower: &str,
|
|
pagination: &SpanFilter,
|
|
prev: &mut Option<SpanFilter>,
|
|
next: &mut Option<SpanFilter>,
|
|
limit: u64,
|
|
) -> Result<Vec<UserData>, CalckeyDbError> {
|
|
let user_tbl = user::Entity.base_prefix();
|
|
|
|
let mut select = following::Entity::find().join_as(
|
|
JoinType::InnerJoin,
|
|
following::Relation::User2.with_to_alias(&user_tbl),
|
|
user_tbl.clone().into_iden(),
|
|
);
|
|
|
|
let query = QuerySelect::query(&mut select);
|
|
self.resolve(query, &user_tbl, options);
|
|
|
|
let followers = select
|
|
.filter(following::Column::FollowerId.eq(follower))
|
|
.get_paginated_model::<UserFollowData, _, _>(
|
|
&self.db.0,
|
|
None,
|
|
(following::Column::CreatedAt, following::Column::Id),
|
|
pagination,
|
|
prev,
|
|
next,
|
|
limit,
|
|
)
|
|
.await?
|
|
.into_iter()
|
|
.map(|u| u.user)
|
|
.collect();
|
|
|
|
Ok(followers)
|
|
}
|
|
|
|
pub async fn get_followers(
|
|
&self,
|
|
options: &UserResolveOptions,
|
|
followee: &str,
|
|
pagination: &SpanFilter,
|
|
prev: &mut Option<SpanFilter>,
|
|
next: &mut Option<SpanFilter>,
|
|
limit: u64,
|
|
) -> Result<Vec<UserData>, CalckeyDbError> {
|
|
let user_tbl = user::Entity.base_prefix();
|
|
|
|
let mut select = following::Entity::find().join_as(
|
|
JoinType::InnerJoin,
|
|
following::Relation::User1.with_to_alias(&user_tbl),
|
|
user_tbl.clone().into_iden(),
|
|
);
|
|
|
|
let query = QuerySelect::query(&mut select);
|
|
self.resolve(query, &user_tbl, options);
|
|
|
|
let followers = select
|
|
.filter(following::Column::FolloweeId.eq(followee))
|
|
.get_paginated_model::<UserFollowData, _, _>(
|
|
&self.db.0,
|
|
None,
|
|
(following::Column::CreatedAt, following::Column::Id),
|
|
pagination,
|
|
prev,
|
|
next,
|
|
limit,
|
|
)
|
|
.await?
|
|
.into_iter()
|
|
.map(|u| u.user)
|
|
.collect();
|
|
|
|
Ok(followers)
|
|
}
|
|
}
|