53e0db6a98
Add log_promo_event for scan/attribution/register/order with IP; admin metrics APIs and ECharts on promo detail page. Co-authored-by: Cursor <cursoragent@cursor.com>
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { PromoMetricEventType } from '@dukang/shared-types';
|
|
import { PrismaService } from '../../common/prisma/prisma.module';
|
|
import { IpGeoService } from '../../common/geo/ip-geo.service';
|
|
|
|
export type PromoMetricLogInput = {
|
|
promoCodeId: bigint;
|
|
eventType: PromoMetricEventType;
|
|
userId?: bigint;
|
|
orderId?: bigint;
|
|
sessionId?: string;
|
|
clientIp?: string;
|
|
};
|
|
|
|
@Injectable()
|
|
export class PromoMetricLogService {
|
|
private readonly logger = new Logger(PromoMetricLogService.name);
|
|
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly ipGeoService: IpGeoService,
|
|
) {}
|
|
|
|
logEvent(input: PromoMetricLogInput): void {
|
|
void this.writeEvent(input).catch((err) => {
|
|
this.logger.debug(
|
|
`promo metric log failed: ${err instanceof Error ? err.message : err}`,
|
|
);
|
|
});
|
|
}
|
|
|
|
private async writeEvent(input: PromoMetricLogInput): Promise<void> {
|
|
const clientIp = input.clientIp?.trim() || null;
|
|
const geo = clientIp ? this.ipGeoService.resolve(clientIp) : { province: null, city: null };
|
|
|
|
await this.prisma.logPromoEvent.create({
|
|
data: {
|
|
promoCodeId: input.promoCodeId,
|
|
eventType: input.eventType,
|
|
userId: input.userId ?? null,
|
|
orderId: input.orderId ?? null,
|
|
sessionId: input.sessionId?.trim() || null,
|
|
clientIp,
|
|
ipProvince: geo.province,
|
|
ipCity: geo.city,
|
|
},
|
|
});
|
|
}
|
|
}
|