v3数据表更改

This commit is contained in:
2026-07-01 14:36:50 +08:00
parent 638898b71e
commit aeb4ecfc84
46 changed files with 4553 additions and 918 deletions
@@ -4,6 +4,7 @@ import {
Injectable,
NotFoundException,
} from '@nestjs/common';
import type { FreightPayType } from '@prisma/client';
import {
calcBenefitAmount,
generateOrderNo,
@@ -19,6 +20,8 @@ import { IDeliveryProvider } from '../../integrations/delivery/delivery.interfac
import { IpGeoService } from '../../common/geo/ip-geo.service';
import { buildOrderClientLocationSnapshot } from '../../common/geo/client-location.util';
import { extractClientIp } from '../../common/geo/client-ip.util';
import { buildOrderStatusEvent, orderStatusLogWhere } from '../../common/event/event.helpers';
import { mapOrderCompat, mapStatusLogCompat } from '../../common/compat/v31-compat';
import type { Request } from 'express';
@Injectable()
@@ -32,13 +35,13 @@ export class TradeService {
) {}
async preview(userId: bigint, body: { productId: string; quantity: number; addressId?: string }) {
const product = await this.prisma.product.findUnique({
const product = await this.prisma.commonProductItem.findUnique({
where: { id: BigInt(body.productId) },
});
if (!product || product.status !== 'ON_SALE') {
throw new BadRequestException('商品不可购买');
}
const city = await this.prisma.city.findFirst({ where: { status: 'ACTIVE' } });
const city = await this.prisma.commonCity.findFirst({ where: { status: 'ACTIVE' } });
if (!city) throw new BadRequestException('暂无开城城市');
let deliveryType: 'LOCAL' | 'CROSS_CITY' = 'LOCAL';
@@ -66,13 +69,15 @@ export class TradeService {
benefitAmount: product.benefitAmount ? Number(product.benefitAmount) : null,
});
const freightPayType: FreightPayType | null = deliveryType === 'CROSS_CITY' ? 'COD' : null;
return {
product: serializeBigInt(product),
quantity: body.quantity,
deliveryType,
productAmount,
freightAmount: deliveryType === 'CROSS_CITY' ? 0 : 0,
freightPayType: deliveryType === 'CROSS_CITY' ? 'COD' : null,
freightPayType,
payAmount: productAmount,
benefitAmount: benefitPerUnit * body.quantity,
city: serializeBigInt(city),
@@ -95,10 +100,10 @@ export class TradeService {
});
if (!address) throw new BadRequestException('请选择收货地址');
const product = await this.prisma.product.findUniqueOrThrow({
const product = await this.prisma.commonProductItem.findUniqueOrThrow({
where: { id: BigInt(body.productId) },
});
const city = await this.prisma.city.findFirstOrThrow({ where: { status: 'ACTIVE' } });
const city = await this.prisma.commonCity.findFirstOrThrow({ where: { status: 'ACTIVE' } });
const orderNo = generateOrderNo();
const payExpireAt = new Date(Date.now() + 30 * 60 * 1000);
@@ -114,7 +119,17 @@ export class TradeService {
userId,
cityId: city.id,
status: 'PENDING_PAY',
payStatus: 'UNPAID',
deliveryType: preview.deliveryType as 'LOCAL' | 'CROSS_CITY',
productId: product.id,
barcode69: product.barcode69,
productName: product.name,
productSpec: product.spec,
imageResourceId: product.coverResourceId,
quantity: body.quantity,
listUnitPrice: product.price,
listAmount: preview.productAmount,
productAmount: preview.productAmount,
receiverName: address.receiverName,
receiverPhone: address.phone,
receiverAddress: `${address.province}${address.city}${address.district}${address.detail}`,
@@ -131,41 +146,21 @@ export class TradeService {
gpsLatitude: location.gpsLatitude,
gpsLongitude: location.gpsLongitude,
gpsAddress: location.gpsAddress,
productAmount: preview.productAmount,
freightAmount: preview.freightAmount,
freightPayType: preview.freightPayType,
payAmount: preview.payAmount,
benefitAmount: preview.benefitAmount,
payExpireAt,
items: {
create: {
productId: product.id,
productName: product.name,
productSpec: product.spec,
productImage: product.mainImageUrl,
unitPrice: product.price,
quantity: body.quantity,
subtotal: preview.productAmount,
},
},
payment: {
create: {
paymentNo: `PAY${orderNo}`,
amount: preview.payAmount,
status: 'PENDING',
},
},
},
include: { items: true, payment: true },
include: { product: true, imageResource: true },
});
return serializeBigInt(order);
return serializeBigInt(mapOrderCompat(order));
}
async payOrder(userId: bigint, orderId: bigint) {
const order = await this.prisma.order.findFirst({
where: { id: orderId, userId },
include: { items: true, payment: true },
});
if (!order) throw new NotFoundException('订单不存在');
if (order.status !== 'PENDING_PAY') {
@@ -176,28 +171,36 @@ export class TradeService {
const now = new Date();
await this.prisma.$transaction(async (tx) => {
await tx.payment.update({
where: { orderId: order.id },
data: {
status: 'SUCCESS',
paidAt: now,
wxTransactionId: externalNo,
},
});
await tx.order.update({
where: { id: order.id },
data: { status: 'PENDING_SHIP', paidAt: now },
});
await tx.orderStatusLog.create({
data: {
status: 'PENDING_SHIP',
payStatus: 'PAID',
paidAt: now,
payExternalNo: externalNo,
},
});
await tx.logThirdParty.create({
data: {
provider: 'WECHAT_PAY',
scene: 'ORDER_PAY',
refType: 'ORDER',
refId: order.id,
externalNo,
amount: order.payAmount,
status: 'SUCCESS',
},
});
await tx.commonEvent.create({
data: buildOrderStatusEvent({
orderId: order.id,
fromStatus: 'PENDING_PAY',
toStatus: 'PENDING_SHIP',
operator: 'MOCK_PAY',
},
}),
});
await tx.orderDelivery.create({
data: { orderId: order.id, provider: 'MOCK' },
data: { orderId: order.id, provider: 'MANUAL' },
});
});
@@ -216,29 +219,32 @@ export class TradeService {
const [list, total] = await Promise.all([
this.prisma.order.findMany({
where,
include: { items: true, benefitCoupons: true },
include: { benefitCoupon: true, imageResource: true },
orderBy: { createdAt: 'desc' },
skip: (page - 1) * pageSize,
take: pageSize,
}),
this.prisma.order.count({ where }),
]);
return { list: serializeBigInt(list), total, page, pageSize };
return { list: serializeBigInt(list.map(mapOrderCompat)), total, page, pageSize };
}
async getOrder(userId: bigint, orderId: bigint) {
const order = await this.prisma.order.findFirst({
where: { id: orderId, userId },
include: {
items: true,
delivery: true,
payment: true,
benefitCoupons: true,
statusLogs: { orderBy: { createdAt: 'desc' } },
benefitCoupon: true,
imageResource: true,
product: true,
},
});
if (!order) throw new NotFoundException('订单不存在');
return serializeBigInt(order);
const statusLogs = await this.prisma.commonEvent.findMany({
where: orderStatusLogWhere(orderId),
orderBy: { createdAt: 'desc' },
});
return serializeBigInt(mapOrderCompat({ ...order, statusLogs: mapStatusLogCompat(statusLogs) }));
}
async updateAddress(userId: bigint, orderId: bigint, body: Record<string, unknown>) {
@@ -258,14 +264,14 @@ export class TradeService {
receiverAddress: String(body.receiverAddress ?? order.receiverAddress),
},
});
await this.prisma.orderStatusLog.create({
data: {
await this.prisma.commonEvent.create({
data: buildOrderStatusEvent({
orderId,
fromStatus: order.status,
toStatus: order.status,
operator: 'USER',
remark: '修改收货地址',
},
}),
});
return serializeBigInt(updated);
}
@@ -284,33 +290,37 @@ export class TradeService {
const account = await this.prisma.partnerAccount.findUniqueOrThrow({
where: { id: partnerAccountId },
});
const cities = await this.prisma.city.findMany({ where: { partnerId: account.partnerId } });
const cities = await this.prisma.commonCity.findMany({ where: { partnerId: account.partnerId } });
const cityIds = cities.map((c) => c.id);
const where = { cityId: { in: cityIds } };
const [list, total] = await Promise.all([
this.prisma.order.findMany({
where,
include: { items: true, delivery: true, user: { select: { phone: true, nickname: true } } },
include: { delivery: true, imageResource: true, user: { select: { phone: true, nickname: true } } },
orderBy: { createdAt: 'desc' },
skip: (page - 1) * pageSize,
take: pageSize,
}),
this.prisma.order.count({ where }),
]);
return { list: serializeBigInt(list), total, page, pageSize };
return { list: serializeBigInt(list.map(mapOrderCompat)), total, page, pageSize };
}
async getPartnerOrder(partnerAccountId: bigint, orderId: bigint) {
const account = await this.prisma.partnerAccount.findUniqueOrThrow({
where: { id: partnerAccountId },
});
const cities = await this.prisma.city.findMany({ where: { partnerId: account.partnerId } });
const cities = await this.prisma.commonCity.findMany({ where: { partnerId: account.partnerId } });
const order = await this.prisma.order.findFirst({
where: { id: orderId, cityId: { in: cities.map((c) => c.id) } },
include: { items: true, delivery: true, statusLogs: true, user: true },
include: { delivery: true, user: true, imageResource: true },
});
if (!order) throw new NotFoundException('订单不存在');
return serializeBigInt(order);
const statusLogs = await this.prisma.commonEvent.findMany({
where: orderStatusLogWhere(orderId),
orderBy: { createdAt: 'desc' },
});
return serializeBigInt(mapOrderCompat({ ...order, statusLogs: mapStatusLogCompat(statusLogs) }));
}
async advanceDelivery(partnerAccountId: bigint, orderId: bigint, targetStatus: string) {
@@ -357,13 +367,13 @@ export class TradeService {
if (Object.keys(deliveryData).length) {
await tx.orderDelivery.update({ where: { orderId }, data: deliveryData as never });
}
await tx.orderStatusLog.create({
data: {
await tx.commonEvent.create({
data: buildOrderStatusEvent({
orderId,
fromStatus: currentStatus,
toStatus: targetStatus,
operator,
},
}),
});
});
}