import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { Prisma, WarehouseManagerType, WarehouseStatus } from '@prisma/client'; import { PrismaService } from '../../common/prisma/prisma.module'; import { serializeBigInt } from '../../common/decorators/current-user.decorator'; import { PartnerCityService } from './partner-city.service'; import type { AdminCityWarehousesQueryDto } from '../ops/dto/admin-query.dto'; export type CreateCityWarehouseInput = { name: string; address: string; contactName: string; contactPhone: string; managerType: WarehouseManagerType; partnerAccountId?: bigint; status?: WarehouseStatus; }; export type UpdateCityWarehouseInput = Partial; @Injectable() export class CityWarehouseService { constructor( private readonly prisma: PrismaService, private readonly partnerCityService: PartnerCityService, ) {} async listByCity(cityId: bigint) { const rows = await this.prisma.cityWarehouse.findMany({ where: { cityId }, include: { partnerAccount: { select: { id: true, companyName: true } }, }, orderBy: { createdAt: 'desc' }, }); return rows.map((row) => this.toDto(row)); } async listAll(query: AdminCityWarehousesQueryDto) { const page = query.page ?? 1; const pageSize = query.pageSize ?? 20; const where: Prisma.CityWarehouseWhereInput = {}; if (query.cityId) where.cityId = BigInt(query.cityId); if (query.name) where.name = { contains: query.name }; if (query.managerType) where.managerType = query.managerType as Prisma.EnumWarehouseManagerTypeFilter['equals']; if (query.status) where.status = query.status as Prisma.EnumWarehouseStatusFilter['equals']; const [rows, total] = await Promise.all([ this.prisma.cityWarehouse.findMany({ where, skip: (page - 1) * pageSize, take: pageSize, include: { partnerAccount: { select: { id: true, companyName: true } }, city: { select: { id: true, name: true, code: true } }, }, orderBy: { createdAt: 'desc' }, }), this.prisma.cityWarehouse.count({ where }), ]); return serializeBigInt({ items: rows.map((row) => ({ ...this.toDto(row), cityName: row.city.name, cityCode: row.city.code, })), total, page, pageSize, }); } async create(cityId: bigint, input: CreateCityWarehouseInput) { await this.assertCityExists(cityId); await this.validateManager(input.managerType, input.partnerAccountId, cityId); const row = await this.prisma.cityWarehouse.create({ data: { cityId, name: input.name.trim(), address: input.address.trim(), contactName: input.contactName.trim(), contactPhone: input.contactPhone.trim(), managerType: input.managerType, partnerAccountId: input.managerType === 'PARTNER' ? input.partnerAccountId : null, status: input.status ?? 'ACTIVE', }, include: { partnerAccount: { select: { id: true, companyName: true } }, }, }); await this.syncManagedWarehouse(row.id, row.managerType as WarehouseManagerType, row.partnerAccountId); return this.toDto(row); } async update(id: bigint, input: UpdateCityWarehouseInput) { const current = await this.prisma.cityWarehouse.findUnique({ where: { id } }); if (!current) throw new NotFoundException('仓库不存在'); const managerType = input.managerType ?? (current.managerType as WarehouseManagerType); const partnerAccountId = managerType === 'PARTNER' ? input.partnerAccountId ?? current.partnerAccountId ?? undefined : null; await this.validateManager(managerType, partnerAccountId ?? undefined, current.cityId); const row = await this.prisma.cityWarehouse.update({ where: { id }, data: { ...(input.name !== undefined ? { name: input.name.trim() } : {}), ...(input.address !== undefined ? { address: input.address.trim() } : {}), ...(input.contactName !== undefined ? { contactName: input.contactName.trim() } : {}), ...(input.contactPhone !== undefined ? { contactPhone: input.contactPhone.trim() } : {}), ...(input.managerType !== undefined ? { managerType: input.managerType } : {}), ...(input.managerType !== undefined || input.partnerAccountId !== undefined ? { partnerAccountId: managerType === 'PARTNER' ? partnerAccountId : null } : {}), ...(input.status !== undefined ? { status: input.status } : {}), }, include: { partnerAccount: { select: { id: true, companyName: true } }, }, }); await this.syncManagedWarehouse(row.id, row.managerType as WarehouseManagerType, row.partnerAccountId); return this.toDto(row); } async remove(id: bigint) { const current = await this.prisma.cityWarehouse.findUnique({ where: { id } }); if (!current) throw new NotFoundException('仓库不存在'); await this.prisma.partnerAccount.updateMany({ where: { managedWarehouseId: id }, data: { managedWarehouseId: null }, }); await this.prisma.cityWarehouse.delete({ where: { id } }); return { ok: true, id: id.toString() }; } private async syncManagedWarehouse( warehouseId: bigint, managerType: WarehouseManagerType, partnerAccountId: bigint | null, ) { await this.prisma.partnerAccount.updateMany({ where: { managedWarehouseId: warehouseId }, data: { managedWarehouseId: null }, }); if (managerType === 'PARTNER' && partnerAccountId) { await this.prisma.partnerAccount.updateMany({ where: { id: partnerAccountId, managedWarehouseId: { not: warehouseId } }, data: { managedWarehouseId: null }, }); await this.prisma.partnerAccount.update({ where: { id: partnerAccountId }, data: { managedWarehouseId: warehouseId }, }); } } private validateManager( managerType: WarehouseManagerType, partnerAccountId: bigint | undefined, cityId: bigint, ) { if (managerType === 'PARTNER') { if (!partnerAccountId) throw new BadRequestException('合伙人管仓须指定合伙人'); return this.partnerCityService.assertPartnerAccountBoundToCity(partnerAccountId, cityId); } } private async assertCityExists(cityId: bigint) { const city = await this.prisma.commonCity.findUnique({ where: { id: cityId } }); if (!city) throw new NotFoundException('开城城市不存在'); } private toDto(row: { id: bigint; cityId: bigint; name: string; address: string; contactName: string; contactPhone: string; managerType: string; partnerAccountId: bigint | null; status: string; createdAt: Date; updatedAt: Date; partnerAccount?: { id: bigint; companyName: string | null } | null; }) { return serializeBigInt({ id: row.id.toString(), cityId: row.cityId.toString(), name: row.name, address: row.address, contactName: row.contactName, contactPhone: row.contactPhone, managerType: row.managerType, partnerAccountId: row.partnerAccountId?.toString() ?? null, partnerCompanyName: row.partnerAccount?.companyName ?? null, status: row.status, createdAt: row.createdAt.toISOString(), updatedAt: row.updatedAt.toISOString(), }); } }