import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { PrismaService } from '../../common/prisma/prisma.module'; import { serializeBigInt } from '../../common/decorators/current-user.decorator'; import type { AdminPartnerAccountsQueryDto, AdminPartnersQueryDto } from './dto/admin-query.dto'; import type { CreatePartnerAccountDto, CreatePartnerDto, UpdatePartnerAccountDto, UpdatePartnerDto, } from './dto/admin-mutate.dto'; @Injectable() export class AdminPartnersService { constructor(private readonly prisma: PrismaService) {} async listPartners(query: AdminPartnersQueryDto) { const page = query.page ?? 1; const pageSize = query.pageSize ?? 20; const where: Prisma.PartnerWhereInput = {}; if (query.companyName) where.companyName = { contains: query.companyName }; if (query.contactPhone) where.contactPhone = { contains: query.contactPhone }; const [items, total] = await Promise.all([ this.prisma.partner.findMany({ where, orderBy: { createdAt: 'desc' }, skip: (page - 1) * pageSize, take: pageSize, include: { _count: { select: { stores: true, accounts: true, cities: true } }, }, }), this.prisma.partner.count({ where }), ]); return serializeBigInt({ items: items.map((p) => ({ ...p, storeCount: p._count.stores, accountCount: p._count.accounts, cityCount: p._count.cities, _count: undefined, })), total, page, pageSize, }); } async detailPartner(id: bigint) { const partner = await this.prisma.partner.findUnique({ where: { id }, include: { cities: { select: { id: true, code: true, name: true, status: true } }, accounts: { select: { id: true, phone: true, name: true, isPrimary: true, status: true } }, stores: { select: { id: true, name: true, status: true }, take: 10, orderBy: { createdAt: 'desc' } }, _count: { select: { stores: true, accounts: true } }, }, }); if (!partner) throw new NotFoundException('开城合伙人不存在'); return serializeBigInt(partner); } async createPartner(dto: CreatePartnerDto) { const partner = await this.prisma.partner.create({ data: dto }); return serializeBigInt(partner); } async updatePartner(id: bigint, dto: UpdatePartnerDto) { const partner = await this.prisma.partner.update({ where: { id }, data: dto }); return serializeBigInt(partner); } async listPartnerAccounts(query: AdminPartnerAccountsQueryDto) { const page = query.page ?? 1; const pageSize = query.pageSize ?? 20; const where: Prisma.PartnerAccountWhereInput = {}; if (query.phone) where.phone = { contains: query.phone }; if (query.partnerId) where.partnerId = BigInt(query.partnerId); if (query.status) where.status = query.status as Prisma.EnumAccountStatusFilter['equals']; const [items, total] = await Promise.all([ this.prisma.partnerAccount.findMany({ where, orderBy: { createdAt: 'desc' }, skip: (page - 1) * pageSize, take: pageSize, include: { partner: { select: { id: true, companyName: true } }, }, }), this.prisma.partnerAccount.count({ where }), ]); return serializeBigInt({ items, total, page, pageSize }); } async detailPartnerAccount(id: bigint) { const account = await this.prisma.partnerAccount.findUnique({ where: { id }, include: { partner: true }, }); if (!account) throw new NotFoundException('开城合伙人账号不存在'); const [bills, orders] = await Promise.all([ this.prisma.partnerBill.findMany({ where: { partnerId: account.partnerId }, orderBy: { createdAt: 'desc' }, take: 50, }), this.prisma.order.findMany({ where: { city: { partnerId: account.partnerId } }, orderBy: { createdAt: 'desc' }, take: 50, select: { id: true, orderNo: true, status: true, payAmount: true, createdAt: true, user: { select: { userNo: true, phone: true } }, }, }), ]); return serializeBigInt({ ...account, bills, orders }); } async createPartnerAccount(dto: CreatePartnerAccountDto) { const partner = await this.prisma.partner.findUnique({ where: { id: BigInt(dto.partnerId) } }); if (!partner) throw new BadRequestException('开城合伙人不存在'); const account = await this.prisma.partnerAccount.create({ data: { partnerId: partner.id, phone: dto.phone, name: dto.name, staffRole: dto.staffRole ? (dto.staffRole as 'PARTNER' | 'INTERNAL' | 'PROMOTER') : undefined, isPrimary: 0, }, }); return serializeBigInt(account); } async updatePartnerAccount(id: bigint, dto: UpdatePartnerAccountDto) { const existing = await this.prisma.partnerAccount.findUnique({ where: { id } }); if (!existing) throw new NotFoundException('开城合伙人账号不存在'); const data: Prisma.PartnerAccountUpdateInput = {}; if (dto.name !== undefined) data.name = dto.name; if (dto.status !== undefined) data.status = dto.status as 'ACTIVE' | 'DISABLED'; if (dto.phone !== undefined) { const phone = dto.phone.trim(); if (!/^1[3-9]\d{9}$/.test(phone)) { throw new BadRequestException('请输入正确的手机号码'); } const phoneTaken = await this.prisma.partnerAccount.findUnique({ where: { phone } }); if (phoneTaken && phoneTaken.id !== id) { throw new BadRequestException('该手机号已被使用'); } data.phone = phone; } const account = await this.prisma.partnerAccount.update({ where: { id }, data }); return serializeBigInt(account); } }