it0/packages/services/referral-service/src/infrastructure/repositories/user-referral-relationship....

117 lines
4.0 KiB
TypeScript

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<UserReferralRelationship> {
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<UserReferralRelationship>;
}
async findByReferredUserId(userId: string): Promise<UserReferralRelationship | null> {
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<void> {
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<number> {
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,
};
}
}