feat(fulfillment): 同城运费与路由修复,配送单展示商品用户地址

同城 MANUAL/ZZXFX 按小飞侠价规计费,路由查询回退仓配凭证;HQ 配送单补商品、用户和收货地址。门店核销回跳与小程序核销码一并带上。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-25 21:12:36 +08:00
parent 52a7d3789d
commit cc0c0a6ef8
44 changed files with 1029 additions and 218 deletions
@@ -4,6 +4,8 @@ import { Prisma } from '@prisma/client';
import { PrismaService } from '../../common/prisma/prisma.module';
import { loadStorePrimaryBank } from '../../common/store/store-bank.util';
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
import { FulfillmentProviderService } from '../fulfillment/fulfillment-provider.service';
import { calcDeliveryFreightAmount } from '../fulfillment/delivery-freight.util';
import type { AdminDeliveriesQueryDto, AdminRedeemRecordsQueryDto } from './dto/admin-query.dto';
import type { UpdateDeliveryDto } from './dto/admin-mutate.dto';
@@ -334,18 +336,45 @@ export class AdminRedeemService {
}
}
const deliveryOrderSelect = {
id: true,
orderNo: true,
status: true,
deliveryType: true,
productName: true,
productSpec: true,
barcode69: true,
quantity: true,
saleUnit: true,
bottlesPerUnit: true,
payAmount: true,
receiverName: true,
receiverPhone: true,
receiverAddress: true,
receiverProvince: true,
receiverCity: true,
receiverDistrict: true,
user: { select: { id: true, userNo: true, phone: true, nickname: true } },
imageResource: { select: { url: true } },
} satisfies Prisma.OrderSelect;
@Injectable()
export class AdminDeliveriesService {
constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly fulfillmentProviderService: FulfillmentProviderService,
) {}
async list(query: AdminDeliveriesQueryDto) {
const page = query.page ?? 1;
const pageSize = query.pageSize ?? 20;
const where: Prisma.OrderDeliveryWhereInput = {};
const where: Prisma.OrderDeliveryWhereInput = {
order: { deliveryType: { not: 'ON_SITE_PICKUP' } },
};
if (query.provider) where.provider = query.provider as DeliveryProvider;
if (query.trackingNo) where.trackingNo = { contains: query.trackingNo };
if (query.orderNo) {
where.order = { orderNo: { contains: query.orderNo } };
where.order = { deliveryType: { not: 'ON_SITE_PICKUP' }, orderNo: { contains: query.orderNo } };
}
const [items, total] = await Promise.all([
@@ -355,39 +384,53 @@ export class AdminDeliveriesService {
skip: (page - 1) * pageSize,
take: pageSize,
include: {
order: {
select: {
id: true,
orderNo: true,
status: true,
receiverName: true,
receiverPhone: true,
deliveryType: true,
productName: true,
quantity: true,
},
},
order: { select: deliveryOrderSelect },
fulfillmentProvider: { select: { code: true, pricingRulesJson: true } },
},
}),
this.prisma.orderDelivery.count({ where }),
]);
return serializeBigInt({ items, total, page, pageSize });
return serializeBigInt({
items: items.map((row) => this.withLogisticsFee(row)),
total,
page,
pageSize,
});
}
async detail(id: bigint) {
const delivery = await this.prisma.orderDelivery.findUnique({
where: { id },
include: {
order: {
include: {
user: { select: { id: true, userNo: true, phone: true } },
imageResource: { select: { url: true } },
},
},
order: { select: deliveryOrderSelect },
fulfillmentProvider: { select: { code: true, pricingRulesJson: true } },
},
});
if (!delivery) throw new NotFoundException('配送单不存在');
return serializeBigInt(delivery);
return serializeBigInt(this.withLogisticsFee(delivery));
}
private withLogisticsFee<
T extends {
provider: string;
order: { quantity: number; bottlesPerUnit: number; deliveryType: string };
fulfillmentProvider?: { code: string; pricingRulesJson: string | null } | null;
},
>(row: T) {
const { fulfillmentProvider, ...rest } = row;
return {
...rest,
logisticsFee: calcDeliveryFreightAmount({
quantity: row.order.quantity,
bottlesPerUnit: row.order.bottlesPerUnit,
deliveryType: row.order.deliveryType,
provider: row.provider,
providerCode: fulfillmentProvider?.code,
pricing: this.fulfillmentProviderService.parsePricingRules(
fulfillmentProvider?.pricingRulesJson ?? null,
),
}),
};
}
async update(id: bigint, dto: UpdateDeliveryDto) {