117 lines
4.0 KiB
TypeScript
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,
|
|
};
|
|
}
|
|
}
|