11use crate :: { models:: raid_leaderboard:: RaidLeaderboard , repositories:: DbResult } ;
2- use sqlx:: PgPool ;
2+ use sqlx:: { PgPool , QueryBuilder } ;
33
44#[ derive( Clone , Debug ) ]
55pub struct RaidLeaderboardRepository {
@@ -19,33 +19,60 @@ impl RaidLeaderboardRepository {
1919 }
2020
2121 /// Retrieves the top raiders with their Rank and Address info.
22- pub async fn get_entries ( & self , raid_id : i32 , limit : i64 , offset : i64 ) -> DbResult < Vec < RaidLeaderboard > > {
23- let query = "
24- SELECT
25- l.raid_id,
26- l.total_submissions,
27- l.total_impressions,
28- l.total_replies,
29- l.total_retweets,
30- l.total_likes,
31- l.last_activity,
32- RANK() OVER (ORDER BY l.total_impressions DESC) as rank,
22+ pub async fn get_entries (
23+ & self ,
24+ raid_id : i32 ,
25+ limit : i64 ,
26+ offset : i64 ,
27+ referral_code : Option < String > ,
28+ ) -> DbResult < Vec < RaidLeaderboard > > {
29+ let mut qb = QueryBuilder :: new (
30+ "WITH ranked_entries AS (
31+ SELECT
32+ l.raid_id,
33+ l.total_submissions,
34+ l.total_impressions,
35+ l.total_replies,
36+ l.total_retweets,
37+ l.total_likes,
38+ l.last_activity,
39+ l.raider_id,
40+ RANK() OVER (ORDER BY l.total_impressions DESC) as rank
41+ FROM raid_leaderboards l
42+ WHERE l.raid_id = " ,
43+ ) ;
44+ qb. push_bind ( raid_id) ;
45+ qb. push ( " ) " ) ;
46+
47+ qb. push (
48+ "SELECT
49+ r.raid_id,
50+ r.total_submissions,
51+ r.total_impressions,
52+ r.total_replies,
53+ r.total_retweets,
54+ r.total_likes,
55+ r.last_activity,
56+ r.rank,
3357 a.quan_address,
3458 a.referral_code,
3559 a.referrals_count
36- FROM raid_leaderboards l
37- JOIN addresses a ON l.raider_id = a.quan_address
38- WHERE l.raid_id = $1
39- ORDER BY l.total_impressions DESC
40- LIMIT $2 OFFSET $3
41- " ;
60+ FROM ranked_entries r
61+ JOIN addresses a ON r.raider_id = a.quan_address
62+ WHERE 1=1 " ,
63+ ) ;
4264
43- let leaderboard = sqlx:: query_as :: < _ , RaidLeaderboard > ( query)
44- . bind ( raid_id)
45- . bind ( limit)
46- . bind ( offset)
47- . fetch_all ( & self . pool )
48- . await ?;
65+ if let Some ( code) = referral_code {
66+ qb. push ( " AND a.referral_code ILIKE " ) ;
67+ qb. push_bind ( format ! ( "{}%" , code) ) ;
68+ }
69+
70+ qb. push ( " ORDER BY r.total_impressions DESC LIMIT " ) ;
71+ qb. push_bind ( limit) ;
72+ qb. push ( " OFFSET " ) ;
73+ qb. push_bind ( offset) ;
74+
75+ let leaderboard = qb. build_query_as :: < RaidLeaderboard > ( ) . fetch_all ( & self . pool ) . await ?;
4976
5077 Ok ( leaderboard)
5178 }
@@ -88,10 +115,22 @@ impl RaidLeaderboardRepository {
88115 Ok ( entry)
89116 }
90117
91- pub async fn get_total_items ( & self , raid_id : i32 ) -> DbResult < i64 > {
92- let query = "SELECT COUNT(*) FROM raid_leaderboards WHERE raid_id = $1" ;
118+ pub async fn get_total_items ( & self , raid_id : i32 , referral_code : Option < String > ) -> DbResult < i64 > {
119+ let mut qb = QueryBuilder :: new ( "SELECT COUNT(*) FROM raid_leaderboards l " ) ;
120+
121+ if referral_code. is_some ( ) {
122+ qb. push ( " JOIN addresses a ON l.raider_id = a.quan_address " ) ;
123+ }
124+
125+ qb. push ( " WHERE l.raid_id = " ) ;
126+ qb. push_bind ( raid_id) ;
127+
128+ if let Some ( code) = referral_code {
129+ qb. push ( " AND a.referral_code ILIKE " ) ;
130+ qb. push_bind ( format ! ( "{}%" , code) ) ;
131+ }
93132
94- let total_items = sqlx :: query_scalar ( query ) . bind ( raid_id ) . fetch_one ( & self . pool ) . await ?;
133+ let total_items = qb . build_query_scalar ( ) . fetch_one ( & self . pool ) . await ?;
95134
96135 Ok ( total_items)
97136 }
@@ -209,7 +248,7 @@ mod tests {
209248 repo. refresh ( ) . await . expect ( "Failed to refresh view" ) ;
210249
211250 // 1. Get Top 3
212- let entries = repo. get_entries ( data. raid_id , 10 , 0 ) . await . unwrap ( ) ;
251+ let entries = repo. get_entries ( data. raid_id , 10 , 0 , None ) . await . unwrap ( ) ;
213252
214253 assert_eq ! ( entries. len( ) , 3 ) ;
215254
@@ -253,7 +292,7 @@ mod tests {
253292 repo. refresh ( ) . await . unwrap ( ) ;
254293
255294 // 1. Limit 1, Offset 1 (Should skip Rank 1, return Rank 2)
256- let page = repo. get_entries ( data. raid_id , 1 , 1 ) . await . unwrap ( ) ;
295+ let page = repo. get_entries ( data. raid_id , 1 , 1 , None ) . await . unwrap ( ) ;
257296
258297 assert_eq ! ( page. len( ) , 1 ) ;
259298 assert_eq ! ( page[ 0 ] . raider. address, data. user_mid) ;
@@ -282,11 +321,35 @@ mod tests {
282321 repo. refresh ( ) . await . unwrap ( ) ;
283322
284323 // Query the empty/old raid
285- let entries = repo. get_entries ( other_raid_id, 10 , 0 ) . await . unwrap ( ) ;
324+ let entries = repo. get_entries ( other_raid_id, 10 , 0 , None ) . await . unwrap ( ) ;
286325 assert ! ( entries. is_empty( ) , "Should not return entries from a different raid" ) ;
287326
288327 // Query the populated raid
289- let entries_main = repo. get_entries ( data. raid_id , 10 , 0 ) . await . unwrap ( ) ;
328+ let entries_main = repo. get_entries ( data. raid_id , 10 , 0 , None ) . await . unwrap ( ) ;
290329 assert_eq ! ( entries_main. len( ) , 3 ) ;
291330 }
331+
332+ #[ tokio:: test]
333+ async fn test_get_entries_filtered_by_referral_code ( ) {
334+ let repo = setup_test_repository ( ) . await ;
335+ let data = seed_leaderboard_scenario ( & repo. pool ) . await ;
336+ repo. refresh ( ) . await . unwrap ( ) ;
337+
338+ // user_mid has REF_MID
339+ let entries = repo
340+ . get_entries ( data. raid_id , 10 , 0 , Some ( "REF_MID" . to_string ( ) ) )
341+ . await
342+ . unwrap ( ) ;
343+
344+ assert_eq ! ( entries. len( ) , 1 ) ;
345+ assert_eq ! ( entries[ 0 ] . raider. address, data. user_mid) ;
346+ // Rank should be preserved from global ranking (Rank 2)
347+ assert_eq ! ( entries[ 0 ] . rank, Some ( 2 ) ) ;
348+
349+ let total = repo
350+ . get_total_items ( data. raid_id , Some ( "REF_MID" . to_string ( ) ) )
351+ . await
352+ . unwrap ( ) ;
353+ assert_eq ! ( total, 1 ) ;
354+ }
292355}
0 commit comments