import { Injectable, NotFoundException } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { eventNamesForPartnerLogCategory, resolvePartnerLogCategory, } from '@dukang/shared-types'; import { PrismaService } from '../../common/prisma/prisma.module'; import { serializeBigInt } from '../../common/decorators/current-user.decorator'; import type { AdminPartnerLogsQueryDto } from './dto/admin-query.dto'; @Injectable() export class AdminPartnerLogsService { constructor(private readonly prisma: PrismaService) {} async list(query: AdminPartnerLogsQueryDto) { const page = query.page ?? 1; const pageSize = query.pageSize ?? 20; const partnerIds = await this.resolvePartnerIds(query); if (partnerIds && partnerIds.length === 0) { return { items: [], total: 0, page, pageSize }; } const where = this.buildWhere(query, partnerIds); const [rows, total] = await Promise.all([ this.prisma.logPartnerAnalytics.findMany({ where, orderBy: { createdAt: 'desc' }, skip: (page - 1) * pageSize, take: pageSize, }), this.prisma.logPartnerAnalytics.count({ where }), ]); const items = await this.enrichRows(rows); return serializeBigInt({ items, total, page, pageSize }); } async detail(id: string) { const row = await this.prisma.logPartnerAnalytics.findUnique({ where: { id: BigInt(id) } }); if (!row) throw new NotFoundException('日志不存在'); const [item] = await this.enrichRows([row]); return serializeBigInt(item); } private buildWhere( query: AdminPartnerLogsQueryDto, partnerIds?: bigint[], ): Prisma.LogPartnerAnalyticsWhereInput { const where: Prisma.LogPartnerAnalyticsWhereInput = {}; if (partnerIds) where.partnerId = { in: partnerIds }; if (query.partnerAccountId) where.partnerAccountId = BigInt(query.partnerAccountId); const categoryEvents = query.eventName ? [query.eventName] : query.category ? eventNamesForPartnerLogCategory(query.category) : undefined; if (categoryEvents?.length) where.eventName = { in: categoryEvents }; if (query.from || query.to) { where.createdAt = { ...(query.from ? { gte: new Date(query.from) } : {}), ...(query.to ? { lte: new Date(query.to) } : {}), }; } return where; } private async resolvePartnerIds(query: AdminPartnerLogsQueryDto): Promise { if (query.partnerId) return [BigInt(query.partnerId)]; const partnerWhere: Prisma.PartnerWhereInput = {}; if (query.companyName) partnerWhere.companyName = { contains: query.companyName }; if (query.partnerAccountId || query.phone) { const accountWhere: Prisma.PartnerAccountWhereInput = {}; if (query.partnerAccountId) accountWhere.id = BigInt(query.partnerAccountId); if (query.phone) accountWhere.phone = { contains: query.phone }; const accounts = await this.prisma.partnerAccount.findMany({ where: accountWhere, select: { partnerId: true }, take: 100, }); if (accounts.length === 0) return []; const ids = [...new Set(accounts.map((a) => a.partnerId))]; if (query.companyName) { const partners = await this.prisma.partner.findMany({ where: { id: { in: ids }, ...partnerWhere }, select: { id: true }, }); return partners.map((p) => p.id); } return ids; } if (query.companyName) { const partners = await this.prisma.partner.findMany({ where: partnerWhere, select: { id: true }, take: 100, }); return partners.map((p) => p.id); } return undefined; } private async enrichRows( rows: Array<{ id: bigint; partnerAccountId: bigint | null; partnerId: bigint; eventName: string; clientApp: string | null; refType: string | null; refId: bigint | null; extraJson: unknown; createdAt: Date; }>, ) { const partnerIds = [...new Set(rows.map((r) => r.partnerId))]; const accountIds = [...new Set(rows.map((r) => r.partnerAccountId).filter((id): id is bigint => id != null))]; const [partners, accounts] = await Promise.all([ partnerIds.length ? this.prisma.partner.findMany({ where: { id: { in: partnerIds } }, select: { id: true, companyName: true }, }) : Promise.resolve([]), accountIds.length ? this.prisma.partnerAccount.findMany({ where: { id: { in: accountIds } }, select: { id: true, name: true, phone: true }, }) : Promise.resolve([]), ]); const partnerMap = new Map(partners.map((p) => [p.id.toString(), p] as const)); const accountMap = new Map(accounts.map((a) => [a.id.toString(), a] as const)); return rows.map((row) => { const partner = partnerMap.get(row.partnerId.toString()); const account = row.partnerAccountId ? accountMap.get(row.partnerAccountId.toString()) : undefined; return { id: row.id.toString(), partnerId: row.partnerId.toString(), partnerAccountId: row.partnerAccountId?.toString() ?? null, accountName: account?.name ?? null, accountPhone: account?.phone ?? null, companyName: partner?.companyName ?? null, category: resolvePartnerLogCategory(row.eventName), eventName: row.eventName, clientApp: row.clientApp, refType: row.refType, refId: row.refId?.toString() ?? null, extraJson: (row.extraJson as Record | null) ?? null, createdAt: row.createdAt, }; }); } }