208 lines
7.3 KiB
TypeScript
208 lines
7.3 KiB
TypeScript
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<CreateCityWarehouseInput>;
|
|
|
|
@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(),
|
|
});
|
|
}
|
|
}
|