c6b01def4c
CI / verify (push) Waiting to run
拦截伪区县写入与下单;推单失败挂 fulfillmentHold,HQ 可见超区/推单失败。 Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
|
import { validateShippingAddress } from '@dukang/domain';
|
|
import { PrismaService } from '../../common/prisma/prisma.module';
|
|
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
|
|
|
@Injectable()
|
|
export class UserAddressService {
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
async list(userId: bigint) {
|
|
const list = await this.prisma.userAddress.findMany({
|
|
where: { userId },
|
|
orderBy: [{ isDefault: 'desc' }, { updatedAt: 'desc' }],
|
|
});
|
|
return serializeBigInt(list);
|
|
}
|
|
|
|
async normalizeDefaultAddress(userId: bigint) {
|
|
const defaults = await this.prisma.userAddress.findMany({
|
|
where: { userId, isDefault: 1 },
|
|
orderBy: { updatedAt: 'desc' },
|
|
});
|
|
if (defaults.length <= 1) return;
|
|
const keep = defaults[0];
|
|
await this.prisma.$transaction(async (tx) => {
|
|
await tx.userAddress.updateMany({ where: { userId }, data: { isDefault: 0 } });
|
|
await tx.userAddress.update({ where: { id: keep.id }, data: { isDefault: 1 } });
|
|
});
|
|
}
|
|
|
|
private assertShippingFields(body: Record<string, unknown>, existing?: {
|
|
province: string;
|
|
city: string;
|
|
district: string;
|
|
detail: string;
|
|
}) {
|
|
const check = validateShippingAddress({
|
|
province: body.province != null ? String(body.province) : existing?.province,
|
|
city: body.city != null ? String(body.city) : existing?.city,
|
|
district: body.district != null ? String(body.district) : existing?.district,
|
|
detail: body.detail != null ? String(body.detail) : existing?.detail,
|
|
});
|
|
if (!check.ok) {
|
|
throw new BadRequestException(check.message || '收货地址不完整');
|
|
}
|
|
}
|
|
|
|
async create(userId: bigint, body: Record<string, unknown>) {
|
|
this.assertShippingFields(body);
|
|
const isDefault = body.isDefault ? 1 : 0;
|
|
const address = await this.prisma.$transaction(async (tx) => {
|
|
if (isDefault) {
|
|
await tx.userAddress.updateMany({ where: { userId }, data: { isDefault: 0 } });
|
|
}
|
|
return tx.userAddress.create({
|
|
data: {
|
|
userId,
|
|
receiverName: String(body.receiverName),
|
|
phone: String(body.phone),
|
|
province: String(body.province),
|
|
city: String(body.city),
|
|
district: String(body.district),
|
|
detail: String(body.detail),
|
|
isDefault,
|
|
},
|
|
});
|
|
});
|
|
return serializeBigInt(address);
|
|
}
|
|
|
|
async update(userId: bigint, id: bigint, body: Record<string, unknown>) {
|
|
const existing = await this.prisma.userAddress.findFirst({ where: { id, userId } });
|
|
if (!existing) throw new NotFoundException('地址不存在');
|
|
this.assertShippingFields(body, existing);
|
|
const address = await this.prisma.$transaction(async (tx) => {
|
|
if (body.isDefault) {
|
|
await tx.userAddress.updateMany({ where: { userId }, data: { isDefault: 0 } });
|
|
}
|
|
return tx.userAddress.update({
|
|
where: { id },
|
|
data: {
|
|
receiverName: body.receiverName ? String(body.receiverName) : undefined,
|
|
phone: body.phone ? String(body.phone) : undefined,
|
|
province: body.province ? String(body.province) : undefined,
|
|
city: body.city ? String(body.city) : undefined,
|
|
district: body.district ? String(body.district) : undefined,
|
|
detail: body.detail ? String(body.detail) : undefined,
|
|
isDefault: body.isDefault ? 1 : undefined,
|
|
},
|
|
});
|
|
});
|
|
return serializeBigInt(address);
|
|
}
|
|
|
|
async remove(userId: bigint, id: bigint) {
|
|
const existing = await this.prisma.userAddress.findFirst({ where: { id, userId } });
|
|
if (!existing) throw new NotFoundException('地址不存在');
|
|
await this.prisma.userAddress.delete({ where: { id } });
|
|
return { deleted: true };
|
|
}
|
|
}
|