You've already forked AstralRinth
forked from didirus/AstralRinth
New Creator Notifications (#4383)
* Some new notification types * Fix error * Use existing DB models rather than inline queries * Fix template fillout * Fix ModerationThreadMessageReceived * Insert more notifications, fix some formatting * chore: query cache, clippy, fmt * chore: query cache, clippy, fmt * Use outer transactions to insert notifications instead of creating a new one * Join futures
This commit is contained in:
committed by
GitHub
parent
8149618187
commit
6da190ed01
@@ -60,12 +60,15 @@ macro_rules! generate_bulk_ids {
|
||||
count: usize,
|
||||
con: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Vec<$return_type>, DatabaseError> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut retry_count = 0;
|
||||
|
||||
// Check if ID is unique
|
||||
loop {
|
||||
let base = random_base62_rng_range(&mut rng, 1, 10) as i64;
|
||||
// We re-acquire a thread-local RNG handle for each uniqueness loop for
|
||||
// the bulk generator future to be `Send + Sync`.
|
||||
let base =
|
||||
random_base62_rng_range(&mut rand::thread_rng(), 1, 10)
|
||||
as i64;
|
||||
let ids =
|
||||
(0..count).map(|x| base + x as i64).collect::<Vec<_>>();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ pub mod oauth_token_item;
|
||||
pub mod organization_item;
|
||||
pub mod pat_item;
|
||||
pub mod payout_item;
|
||||
pub mod payouts_values_notifications;
|
||||
pub mod product_item;
|
||||
pub mod project_item;
|
||||
pub mod report_item;
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::ids::*;
|
||||
use crate::database::{models::DatabaseError, redis::RedisPool};
|
||||
use crate::models::notifications::{
|
||||
NotificationBody, NotificationChannel, NotificationDeliveryStatus,
|
||||
NotificationType,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use futures::TryStreamExt;
|
||||
@@ -41,6 +42,71 @@ impl NotificationBuilder {
|
||||
self.insert_many(vec![user], transaction, redis).await
|
||||
}
|
||||
|
||||
pub async fn insert_many_payout_notifications(
|
||||
users: Vec<DBUserId>,
|
||||
dates_available: Vec<DateTime<Utc>>,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
redis: &RedisPool,
|
||||
) -> Result<(), DatabaseError> {
|
||||
let notification_ids =
|
||||
generate_many_notification_ids(users.len(), &mut *transaction)
|
||||
.await?;
|
||||
|
||||
let users_raw_ids = users.iter().map(|x| x.0).collect::<Vec<_>>();
|
||||
let notification_ids =
|
||||
notification_ids.iter().map(|x| x.0).collect::<Vec<_>>();
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
WITH
|
||||
period_payouts AS (
|
||||
SELECT
|
||||
ids.notification_id,
|
||||
ids.user_id,
|
||||
ids.date_available,
|
||||
COALESCE(SUM(pv.amount), 0.0) sum
|
||||
FROM UNNEST($1::bigint[], $2::bigint[], $3::timestamptz[]) AS ids(notification_id, user_id, date_available)
|
||||
LEFT JOIN payouts_values pv ON pv.user_id = ids.user_id AND pv.date_available = ids.date_available
|
||||
GROUP BY ids.user_id, ids.notification_id, ids.date_available
|
||||
)
|
||||
INSERT INTO notifications (
|
||||
id, user_id, body
|
||||
)
|
||||
SELECT
|
||||
notification_id id,
|
||||
user_id,
|
||||
JSONB_BUILD_OBJECT(
|
||||
'type', 'payout_available',
|
||||
'date_available', to_jsonb(date_available),
|
||||
'amount', to_jsonb(sum)
|
||||
) body
|
||||
FROM period_payouts
|
||||
",
|
||||
¬ification_ids[..],
|
||||
&users_raw_ids[..],
|
||||
&dates_available[..],
|
||||
)
|
||||
.execute(&mut **transaction)
|
||||
.await?;
|
||||
|
||||
let notification_types = notification_ids
|
||||
.iter()
|
||||
.map(|_| NotificationType::PayoutAvailable.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
NotificationBuilder::insert_many_deliveries(
|
||||
transaction,
|
||||
redis,
|
||||
¬ification_ids,
|
||||
&users_raw_ids,
|
||||
¬ification_types,
|
||||
&users,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_many(
|
||||
&self,
|
||||
users: Vec<DBUserId>,
|
||||
@@ -80,6 +146,27 @@ impl NotificationBuilder {
|
||||
.map(|_| self.body.notification_type().as_str())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
NotificationBuilder::insert_many_deliveries(
|
||||
transaction,
|
||||
redis,
|
||||
¬ification_ids,
|
||||
&users_raw_ids,
|
||||
¬ification_types,
|
||||
&users,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn insert_many_deliveries(
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
redis: &RedisPool,
|
||||
notification_ids: &[i64],
|
||||
users_raw_ids: &[i64],
|
||||
notification_types: &[&str],
|
||||
users: &[DBUserId],
|
||||
) -> Result<(), DatabaseError> {
|
||||
let notification_channels = NotificationChannel::list()
|
||||
.iter()
|
||||
.map(|x| x.as_str())
|
||||
@@ -159,7 +246,7 @@ impl NotificationBuilder {
|
||||
|
||||
query.execute(&mut **transaction).await?;
|
||||
|
||||
DBNotification::clear_user_notifications_cache(&users, redis).await?;
|
||||
DBNotification::clear_user_notifications_cache(users, redis).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
use crate::database::models::{DBUserId, DatabaseError};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
pub struct PayoutsValuesNotification {
|
||||
pub id: i32,
|
||||
pub user_id: DBUserId,
|
||||
pub date_available: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl PayoutsValuesNotification {
|
||||
pub async fn unnotified_users_with_available_payouts_with_limit(
|
||||
exec: impl sqlx::PgExecutor<'_>,
|
||||
limit: i64,
|
||||
) -> Result<Vec<PayoutsValuesNotification>, DatabaseError> {
|
||||
Ok(sqlx::query_as!(
|
||||
QueryResult,
|
||||
"
|
||||
SELECT
|
||||
id,
|
||||
user_id,
|
||||
date_available
|
||||
FROM payouts_values_notifications
|
||||
WHERE
|
||||
notified = FALSE
|
||||
AND date_available <= NOW()
|
||||
FOR UPDATE SKIP LOCKED
|
||||
LIMIT $1
|
||||
",
|
||||
limit,
|
||||
)
|
||||
.fetch_all(exec)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn set_notified_many(
|
||||
ids: &[i32],
|
||||
exec: impl sqlx::PgExecutor<'_>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE payouts_values_notifications
|
||||
SET notified = TRUE
|
||||
WHERE id = ANY($1)
|
||||
",
|
||||
&ids[..],
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn synchronize_future_payout_values(
|
||||
exec: impl sqlx::PgExecutor<'_>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO payouts_values_notifications (date_available, user_id, notified)
|
||||
SELECT DISTINCT date_available, user_id, false notified
|
||||
FROM payouts_values
|
||||
WHERE date_available > NOW()
|
||||
ON CONFLICT (date_available, user_id) DO NOTHING
|
||||
",
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct QueryResult {
|
||||
id: i32,
|
||||
user_id: i64,
|
||||
date_available: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl From<QueryResult> for PayoutsValuesNotification {
|
||||
fn from(result: QueryResult) -> Self {
|
||||
PayoutsValuesNotification {
|
||||
id: result.id,
|
||||
user_id: DBUserId(result.user_id),
|
||||
date_available: result.date_available,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user