import { Injectable } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { UserReferralRelationship, UserReferralStatus } from '../../domain/entities/user-referral-relationship.entity'; @Injectable() export class UserReferralRelationshipRepository { constructor(@InjectDataSource() private readonly ds: DataSource) {} async create( referrerUserId: string, referredUserId: string, referralCode: string, level: 1 | 2, ): Promise { const rows = await this.ds.query( `INSERT INTO public.user_referral_relationships (referrer_user_id, referred_user_id, referral_code, level) VALUES ($1, $2, $3, $4) ON CONFLICT (referred_user_id) DO NOTHING RETURNING *`, [referrerUserId, referredUserId, referralCode, level], ); return rows[0] ? this.map(rows[0]) : this.findByReferredUserId(referredUserId) as Promise; } async findByReferredUserId(userId: string): Promise { const rows = await this.ds.query( `SELECT * FROM public.user_referral_relationships WHERE referred_user_id = $1`, [userId], ); return rows[0] ? this.map(rows[0]) : null; } /** Direct circle members (level-1 referrals made by this user). */ async findByReferrerUserId( userId: string, limit = 20, offset = 0, ): Promise<{ items: UserReferralRelationship[]; total: number }> { const [rows, count] = await Promise.all([ this.ds.query( `SELECT * FROM public.user_referral_relationships WHERE referrer_user_id = $1 AND level = 1 ORDER BY created_at DESC LIMIT $2 OFFSET $3`, [userId, limit, offset], ), this.ds.query( `SELECT COUNT(*)::int AS total FROM public.user_referral_relationships WHERE referrer_user_id = $1 AND level = 1`, [userId], ), ]); return { items: rows.map((r: any) => this.map(r)), total: count[0]?.total ?? 0 }; } async updateStatus( id: string, status: UserReferralStatus, timestamps: { activatedAt?: Date; rewardedAt?: Date } = {}, ): Promise { await this.ds.query( `UPDATE public.user_referral_relationships SET status = $2, activated_at = COALESCE($3, activated_at), rewarded_at = COALESCE($4, rewarded_at) WHERE id = $1`, [id, status, timestamps.activatedAt ?? null, timestamps.rewardedAt ?? null], ); } /** Admin: list all user-level circle relationships (paginated). */ async findAll( status?: UserReferralStatus, limit = 50, offset = 0, ): Promise<{ items: UserReferralRelationship[]; total: number }> { const where = status ? `WHERE status = $3` : ''; const params = status ? [limit, offset, status] : [limit, offset]; const [rows, count] = await Promise.all([ this.ds.query( `SELECT * FROM public.user_referral_relationships ${where} ORDER BY created_at DESC LIMIT $1 OFFSET $2`, params, ), this.ds.query( `SELECT COUNT(*)::int AS total FROM public.user_referral_relationships ${where}`, status ? [status] : [], ), ]); return { items: rows.map((r: any) => this.map(r)), total: count[0]?.total ?? 0 }; } /** Count recurring-type point transactions already issued for a relationship. */ async countRecurringRewards(relationshipId: string): Promise { const rows = await this.ds.query( `SELECT COUNT(*)::int AS cnt FROM public.user_point_transactions WHERE ref_id = $1 AND type IN ('REFERRAL_RECURRING', 'REFERRAL_L2')`, [relationshipId], ); return rows[0]?.cnt ?? 0; } private map(r: any): UserReferralRelationship { return { id: r.id, referrerUserId: r.referrer_user_id, referredUserId: r.referred_user_id, referralCode: r.referral_code, level: r.level, status: r.status, activatedAt: r.activated_at, rewardedAt: r.rewarded_at, createdAt: r.created_at, }; } }