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

73 lines
2.3 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { UserReferralCode } from '../../domain/entities/user-referral-code.entity';
/** Generates a random user referral code: USR-XXXX-XXXX */
function generateUserCode(): string {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
const seg = (n: number) =>
Array.from({ length: n }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
return `USR-${seg(4)}-${seg(4)}`;
}
@Injectable()
export class UserReferralCodeRepository {
constructor(@InjectDataSource() private readonly ds: DataSource) {}
/** Create a referral code for a user (idempotent — skips if already exists). */
async createForUser(userId: string): Promise<UserReferralCode> {
// Try up to 5 times to find a unique code
for (let attempt = 0; attempt < 5; attempt++) {
const code = generateUserCode();
try {
const rows = await this.ds.query(
`INSERT INTO public.user_referral_codes (user_id, code)
VALUES ($1, $2)
ON CONFLICT (user_id) DO UPDATE SET user_id = EXCLUDED.user_id
RETURNING *`,
[userId, code],
);
return this.map(rows[0]);
} catch {
// code collision — retry
}
}
// Fallback: just return whatever exists
return this.findByUserId(userId) as Promise<UserReferralCode>;
}
async findByCode(code: string): Promise<UserReferralCode | null> {
const rows = await this.ds.query(
`SELECT * FROM public.user_referral_codes WHERE code = $1`,
[code],
);
return rows[0] ? this.map(rows[0]) : null;
}
async findByUserId(userId: string): Promise<UserReferralCode | null> {
const rows = await this.ds.query(
`SELECT * FROM public.user_referral_codes WHERE user_id = $1`,
[userId],
);
return rows[0] ? this.map(rows[0]) : null;
}
/** Increment click count asynchronously (fire-and-forget safe). */
async incrementClickCount(code: string): Promise<void> {
await this.ds.query(
`UPDATE public.user_referral_codes SET click_count = click_count + 1 WHERE code = $1`,
[code],
);
}
private map(r: any): UserReferralCode {
return {
userId: r.user_id,
code: r.code,
clickCount: r.click_count,
createdAt: r.created_at,
};
}
}