Initial affiliate codes implementation (#4382)

* Initial affiliate codes implementation

* some more docs to codes

* sqlx prepare

* Address PR comments

* Address more PR comments

* fix clippy

* Switch to using Json<T> for type-safe responses
This commit is contained in:
aecsocket
2025-09-18 16:43:34 +01:00
committed by GitHub
parent 6da190ed01
commit 4def0e8407
15 changed files with 607 additions and 2 deletions

View File

@@ -0,0 +1,117 @@
use chrono::{DateTime, Utc};
use futures::{StreamExt, TryStreamExt};
use crate::database::models::{DBAffiliateCodeId, DBUserId, DatabaseError};
#[derive(Debug)]
pub struct DBAffiliateCode {
pub id: DBAffiliateCodeId,
pub created_at: DateTime<Utc>,
pub created_by: DBUserId,
pub affiliate: DBUserId,
}
impl DBAffiliateCode {
pub async fn get_by_id(
id: DBAffiliateCodeId,
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<DBAffiliateCode>, DatabaseError> {
let record = sqlx::query!(
"SELECT id, created_at, created_by, affiliate
FROM affiliate_codes WHERE id = $1",
id as DBAffiliateCodeId
)
.fetch_optional(exec)
.await?;
Ok(record.map(|record| DBAffiliateCode {
id: DBAffiliateCodeId(record.id),
created_at: record.created_at,
created_by: DBUserId(record.created_by),
affiliate: DBUserId(record.affiliate),
}))
}
pub async fn get_by_affiliate(
affiliate: DBUserId,
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Vec<DBAffiliateCode>, DatabaseError> {
let records = sqlx::query!(
"SELECT id, created_at, created_by, affiliate
FROM affiliate_codes WHERE affiliate = $1",
affiliate as DBUserId
)
.fetch(exec)
.map(|record| {
let record = record?;
Ok::<_, DatabaseError>(DBAffiliateCode {
id: DBAffiliateCodeId(record.id),
created_at: record.created_at,
created_by: DBUserId(record.created_by),
affiliate: DBUserId(record.affiliate),
})
})
.try_collect::<Vec<_>>()
.await?;
Ok(records)
}
pub async fn insert(
&self,
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<(), DatabaseError> {
sqlx::query!(
"INSERT INTO affiliate_codes (id, created_at, created_by, affiliate)
VALUES ($1, $2, $3, $4)",
self.id as DBAffiliateCodeId,
self.created_at,
self.created_by as DBUserId,
self.affiliate as DBUserId
)
.execute(exec)
.await?;
Ok(())
}
pub async fn remove(
id: DBAffiliateCodeId,
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<()>, DatabaseError> {
let result = sqlx::query!(
"DELETE FROM affiliate_codes WHERE id = $1",
id as DBAffiliateCodeId
)
.execute(exec)
.await?;
if result.rows_affected() > 0 {
Ok(Some(()))
} else {
Ok(None)
}
}
pub async fn get_all(
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Vec<DBAffiliateCode>, DatabaseError> {
let records = sqlx::query!(
"SELECT id, created_at, created_by, affiliate
FROM affiliate_codes ORDER BY created_at DESC"
)
.fetch(exec)
.map(|record| {
let record = record?;
Ok::<_, DatabaseError>(DBAffiliateCode {
id: DBAffiliateCodeId(record.id),
created_at: record.created_at,
created_by: DBUserId(record.created_by),
affiliate: DBUserId(record.affiliate),
})
})
.try_collect::<Vec<_>>()
.await?;
Ok(records)
}
}

View File

@@ -1,6 +1,6 @@
use super::DatabaseError;
use crate::models::ids::{
ChargeId, CollectionId, FileId, ImageId, NotificationId,
AffiliateCodeId, ChargeId, CollectionId, FileId, ImageId, NotificationId,
OAuthAccessTokenId, OAuthClientAuthorizationId, OAuthClientId,
OAuthRedirectUriId, OrganizationId, PatId, PayoutId, ProductId,
ProductPriceId, ProjectId, ReportId, SessionId, SharedInstanceId,
@@ -263,6 +263,10 @@ db_id_interface!(
VersionId,
generator: generate_version_id @ "versions",
);
db_id_interface!(
AffiliateCodeId,
generator: generate_affiliate_code_id @ "affiliate_codes",
);
short_id_type!(CategoryId);
short_id_type!(GameId);

View File

@@ -1,5 +1,6 @@
use thiserror::Error;
pub mod affiliate_code_item;
pub mod categories;
pub mod charge_item;
pub mod collection_item;
@@ -34,6 +35,7 @@ pub mod users_notifications_preferences_item;
pub mod users_redeemals;
pub mod version_item;
pub use affiliate_code_item::DBAffiliateCode;
pub use collection_item::DBCollection;
pub use ids::*;
pub use image_item::DBImage;