Add affiliate code revenue analytics (#4883)

* Add affiliate code revenue analytics

* clean up some error handling

* Add conversions to affiliate code analytics

* Only include affiliate subscriptions which have an associated successful charge

* wip: affiliate code clicks

* affiliate code click ingest route

* Add affiliate code clicks to analytics

* add new cols
This commit is contained in:
aecsocket
2025-12-18 18:02:49 +00:00
committed by GitHub
parent dc16a65b62
commit 8d894541e8
12 changed files with 662 additions and 131 deletions

View File

@@ -4,42 +4,6 @@ use serde::{Deserialize, Serialize};
use crate::models::ids::AffiliateCodeId;
/// Affiliate code used to track referral purchases.
///
/// See [`AffiliateCode`].
///
/// This struct contains information which should only be visible to admins.
#[derive(Serialize, Deserialize)]
pub struct AdminAffiliateCode {
/// Affiliate code ID.
pub id: AffiliateCodeId,
/// When the code was created.
pub created_at: DateTime<Utc>,
/// User who created the code.
pub created_by: UserId,
/// User who refers the purchaser.
pub affiliate: UserId,
/// Affiliate-defined name for this affiliate code - where the click came
/// from.
pub source_name: String,
}
impl From<crate::database::models::affiliate_code_item::DBAffiliateCode>
for AdminAffiliateCode
{
fn from(
data: crate::database::models::affiliate_code_item::DBAffiliateCode,
) -> Self {
Self {
id: data.id.into(),
created_at: data.created_at,
created_by: data.created_by.into(),
affiliate: data.affiliate.into(),
source_name: data.source_name,
}
}
}
/// Affiliate code used to track referral purchases.
///
/// When a user follows a URL with [`AffiliateCode::id`] as an affiliate
@@ -49,10 +13,14 @@ impl From<crate::database::models::affiliate_code_item::DBAffiliateCode>
///
/// This struct contains information which is allowed to be seen by an
/// affiliate.
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct AffiliateCode {
/// Affiliate code ID.
pub id: AffiliateCodeId,
/// When the code was created.
pub created_at: Option<DateTime<Utc>>,
/// User who created the code.
pub created_by: Option<UserId>,
/// User who refers the purchaser.
pub affiliate: UserId,
/// Affiliate-defined name for this affiliate code - where the click came
@@ -60,14 +28,23 @@ pub struct AffiliateCode {
pub source_name: String,
}
impl From<crate::database::models::affiliate_code_item::DBAffiliateCode>
for AffiliateCode
{
fn from(
impl AffiliateCode {
pub fn from(
data: crate::database::models::affiliate_code_item::DBAffiliateCode,
is_admin: bool,
) -> Self {
Self {
id: data.id.into(),
created_at: if is_admin {
Some(data.created_at)
} else {
None
},
created_by: if is_admin {
Some(data.created_by.into())
} else {
None
},
affiliate: data.affiliate.into(),
source_name: data.source_name,
}

View File

@@ -45,6 +45,21 @@ pub struct PageView {
pub headers: Vec<(String, String)>,
}
#[derive(Debug, Row, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
pub struct AffiliateCodeClick {
pub recorded: i64,
pub domain: String,
// Modrinth User ID for logged in users
pub user_id: u64,
pub affiliate_code_id: u64,
pub ip: Ipv6Addr,
pub country: String,
pub user_agent: String,
pub headers: Vec<(String, String)>,
}
#[derive(Row, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Playtime {
pub recorded: i64,