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 partnerAccountIds = await this.resolvePartnerAccountIds(query); if (partnerAccountIds && partnerAccountIds.length === 0) { return { items: [], total: 0, page, pageSize }; } const where = this.buildWhere(query, partnerAccountIds); 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, partnerAccountIds?: bigint[], ): Prisma.LogPartnerAnalyticsWhereInput { const where: Prisma.LogPartnerAnalyticsWhereInput = {}; if (partnerAccountIds) where.partnerAccountId = { in: partnerAccountIds }; 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 expandPrimaryWithChildren(primaryIds: bigint[]): Promise { if (!primaryIds.length) return []; const children = await this.prisma.partnerAccount.findMany({ where: { parentAccountId: { in: primaryIds } }, select: { id: true }, }); return [...primaryIds, ...children.map((c) => c.id)]; } private async resolvePartnerAccountIds( query: AdminPartnerLogsQueryDto, ): Promise { if (query.partnerAccountId) return [BigInt(query.partnerAccountId)]; if (query.partnerId) { return this.expandPrimaryWithChildren([BigInt(query.partnerId)]); } if (query.phone) { const accounts = await this.prisma.partnerAccount.findMany({ where: { phone: { contains: query.phone } }, select: { id: true }, take: 200, }); return accounts.map((a) => a.id); } if (query.companyName) { const primaries = await this.prisma.partnerAccount.findMany({ where: { isPrimary: 1, companyName: { contains: query.companyName } }, select: { id: true }, take: 100, }); return this.expandPrimaryWithChildren(primaries.map((a) => a.id)); } return undefined; } private async enrichRows( rows: Array<{ id: bigint; partnerAccountId: bigint; eventName: string; clientApp: string | null; refType: string | null; refId: bigint | null; extraJson: unknown; createdAt: Date; }>, ) { const accountIds = [...new Set(rows.map((r) => r.partnerAccountId))]; const accounts = accountIds.length ? await this.prisma.partnerAccount.findMany({ where: { id: { in: accountIds } }, select: { id: true, name: true, phone: true, companyName: true, isPrimary: true, parentAccountId: true, staffRole: true, parent: { select: { id: true, companyName: true } }, }, }) : []; const accountMap = new Map(accounts.map((a) => [a.id.toString(), a] as const)); return rows.map((row) => { const account = accountMap.get(row.partnerAccountId.toString()); const isSubAccount = !!account?.parentAccountId; const primaryId = isSubAccount ? account?.parent?.id?.toString() ?? null : account?.id.toString() ?? null; return { id: row.id.toString(), partnerId: primaryId ?? row.partnerAccountId.toString(), partnerAccountId: row.partnerAccountId.toString(), accountName: account?.name ?? null, accountPhone: account?.phone ?? null, companyName: isSubAccount ? null : account?.companyName ?? null, isSubAccount, staffRole: account?.staffRole ?? 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, }; }); } }