webadmin端
批量删除用户 模板上传图片数量限制改成30 小飞侠接口配置
This commit is contained in:
@@ -1,17 +1,23 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { orderStatusLogWhere } from '../../common/event/event.helpers';
|
||||
import { mapOrderCompat, mapStatusLogCompat } from '../../common/compat/v31-compat';
|
||||
import { TradeService } from '../trade/trade.service';
|
||||
import { AdminXiaofeixiaService } from './admin-xiaofeixia.service';
|
||||
import type { AdminOrdersQueryDto } from './dto/admin-query.dto';
|
||||
import type { AdminShipOrderDto } from './dto/admin-mutate.dto';
|
||||
import type { XiaofeixiaCreateShipmentDto } from './dto/admin-courier.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AdminOrdersService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly tradeService: TradeService,
|
||||
private readonly xiaofeixiaService: AdminXiaofeixiaService,
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
|
||||
async list(query: AdminOrdersQueryDto) {
|
||||
@@ -87,4 +93,156 @@ export class AdminOrdersService {
|
||||
await this.tradeService.applyStatusTransition(id, order.status, status, 'HQ_DEBUG');
|
||||
return this.detail(id);
|
||||
}
|
||||
|
||||
getShipDefaults() {
|
||||
return {
|
||||
provider: 'XFX',
|
||||
providerLabel: '小飞侠',
|
||||
fromName: this.config.get<string>('SHIP_FROM_NAME') || '杜康仓库',
|
||||
fromMobile: this.config.get<string>('SHIP_FROM_MOBILE') || '13800000000',
|
||||
fromAddress: this.config.get<string>('SHIP_FROM_ADDRESS') || '河南省郑州市金水区',
|
||||
fromAddressDetail: this.config.get<string>('SHIP_FROM_ADDRESS_DETAIL') || '杜康酒业仓',
|
||||
fromLng: Number(this.config.get<string>('SHIP_FROM_LNG') || 113.665),
|
||||
fromLat: Number(this.config.get<string>('SHIP_FROM_LAT') || 34.757),
|
||||
weight: 2,
|
||||
payMode: '1',
|
||||
};
|
||||
}
|
||||
|
||||
async shipOrder(id: bigint, dto: AdminShipOrderDto) {
|
||||
if (dto.provider !== 'XFX') {
|
||||
throw new BadRequestException('暂仅支持小飞侠配送');
|
||||
}
|
||||
|
||||
const order = await this.prisma.order.findUnique({
|
||||
where: { id },
|
||||
include: { delivery: true },
|
||||
});
|
||||
if (!order) throw new NotFoundException('订单不存在');
|
||||
|
||||
if (!['PENDING_SHIP', 'OUT_WAREHOUSE'].includes(order.status)) {
|
||||
throw new BadRequestException('当前订单状态不可发货');
|
||||
}
|
||||
if (order.delivery?.trackingNo) {
|
||||
throw new BadRequestException('该订单已有运单号,请勿重复发货');
|
||||
}
|
||||
|
||||
const defaults = this.getShipDefaults();
|
||||
const shipmentDto: XiaofeixiaCreateShipmentDto = {
|
||||
outNumber: order.orderNo,
|
||||
fromName: dto.fromName || defaults.fromName,
|
||||
fromMobile: dto.fromMobile || defaults.fromMobile,
|
||||
fromAddress: dto.fromAddress || defaults.fromAddress,
|
||||
fromAddressDetail: dto.fromAddressDetail || defaults.fromAddressDetail,
|
||||
fromLng: dto.fromLng ?? defaults.fromLng,
|
||||
fromLat: dto.fromLat ?? defaults.fromLat,
|
||||
toName: order.receiverName,
|
||||
toMobile: order.receiverPhone,
|
||||
toAddress: `${order.receiverProvince}${order.receiverCity}${order.receiverDistrict}`,
|
||||
toAddressDetail: order.receiverAddress,
|
||||
goodsName: order.productName,
|
||||
goodsNum: order.quantity,
|
||||
weight: dto.weight ?? defaults.weight,
|
||||
payMode: dto.payMode || defaults.payMode,
|
||||
remark: dto.remark || `HQ发货 ${order.orderNo}`,
|
||||
};
|
||||
|
||||
const result = await this.xiaofeixiaService.createShipment(shipmentDto);
|
||||
if (!result.ok || !result.data) {
|
||||
throw new BadRequestException(result.error || '小飞侠创建运单失败');
|
||||
}
|
||||
|
||||
const { providerShipmentId, trackingNumber } = result.data;
|
||||
const now = new Date();
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
if (order.delivery) {
|
||||
await tx.orderDelivery.update({
|
||||
where: { orderId: id },
|
||||
data: {
|
||||
provider: 'XFX',
|
||||
trackingNo: trackingNumber,
|
||||
providerOrderNo: String(providerShipmentId),
|
||||
shippingAt: now,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await tx.orderDelivery.create({
|
||||
data: {
|
||||
orderId: id,
|
||||
provider: 'XFX',
|
||||
trackingNo: trackingNumber,
|
||||
providerOrderNo: String(providerShipmentId),
|
||||
shippingAt: now,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await this.tradeService.applyStatusTransition(id, order.status, 'SHIPPING', 'HQ_SHIP');
|
||||
return this.detail(id);
|
||||
}
|
||||
|
||||
async batchDeleteOrders(ids: bigint[]) {
|
||||
const uniqueIds = [...new Set(ids)];
|
||||
if (!uniqueIds.length) {
|
||||
return { ok: true, deleted: 0, message: '未选择订单' };
|
||||
}
|
||||
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: { id: { in: uniqueIds } },
|
||||
select: { id: true, orderNo: true },
|
||||
});
|
||||
if (!orders.length) throw new NotFoundException('订单不存在');
|
||||
|
||||
const orderIds = orders.map((o) => o.id);
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await this.deleteOrdersInTx(tx, orderIds);
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
deleted: orderIds.length,
|
||||
orderNos: orders.map((o) => o.orderNo),
|
||||
message: '订单及关联业务数据已删除,状态流转等业务日志已保留',
|
||||
};
|
||||
}
|
||||
|
||||
private async deleteOrdersInTx(tx: Prisma.TransactionClient, orderIds: bigint[]) {
|
||||
if (!orderIds.length) return;
|
||||
|
||||
const couponIds = (
|
||||
await tx.benefitCoupon.findMany({
|
||||
where: { orderId: { in: orderIds } },
|
||||
select: { id: true },
|
||||
})
|
||||
).map((c) => c.id);
|
||||
|
||||
if (couponIds.length) {
|
||||
const redeemIds = (
|
||||
await tx.redeemRecord.findMany({
|
||||
where: { couponId: { in: couponIds } },
|
||||
select: { id: true },
|
||||
})
|
||||
).map((r) => r.id);
|
||||
|
||||
if (redeemIds.length) {
|
||||
await tx.storePayout.deleteMany({ where: { redeemRecordId: { in: redeemIds } } });
|
||||
await tx.storeRating.deleteMany({ where: { redeemRecordId: { in: redeemIds } } });
|
||||
await tx.redeemRecord.deleteMany({ where: { id: { in: redeemIds } } });
|
||||
}
|
||||
|
||||
await tx.benefitCoupon.deleteMany({ where: { id: { in: couponIds } } });
|
||||
}
|
||||
|
||||
await tx.order.updateMany({
|
||||
where: { originOrderId: { in: orderIds } },
|
||||
data: { originOrderId: null },
|
||||
});
|
||||
await tx.commonTicket.deleteMany({
|
||||
where: { refType: 'ORDER', refId: { in: orderIds } },
|
||||
});
|
||||
await tx.order.deleteMany({ where: { id: { in: orderIds } } });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user