feat(ops): partner proxy order list and HQ order delete
CI / verify (pull_request) Has been cancelled
CI / verify (pull_request) Has been cancelled
Add partner-facing proxy order list/detail; unlock SUPER_ADMIN order delete with cascade cleanup and type filter. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import {
|
||||
HQ_DANGEROUS_PERMISSION_KEYS,
|
||||
HQ_ROLE_DEFAULT_PERMISSIONS,
|
||||
expandHqPermissionKeys,
|
||||
hasAnySystemSettingsPermission,
|
||||
@@ -43,8 +44,12 @@ export class HqPermissionsResolver {
|
||||
const userKeys = userRows.map((r) => r.permissionKey);
|
||||
|
||||
if (account.adminRole === 'SUPER_ADMIN') {
|
||||
// 超管默认拥有业务/管理权限,但不含危险操作;危险权限需按用户显式勾选
|
||||
return expandHqPermissionKeys([...hqBasePermissionKeys(), ...userKeys]);
|
||||
// 超管含危险操作(删用户/订单/城市);其他角色仍需在权限分配中显式勾选
|
||||
return expandHqPermissionKeys([
|
||||
...hqBasePermissionKeys(),
|
||||
...HQ_DANGEROUS_PERMISSION_KEYS,
|
||||
...userKeys,
|
||||
]);
|
||||
}
|
||||
|
||||
const roleRows = await this.prisma.hqRolePermission.findMany({
|
||||
|
||||
@@ -22,6 +22,7 @@ export const HqOperationAction = {
|
||||
ORDER_SHIP: 'ORDER_SHIP',
|
||||
ORDER_STATUS_DEBUG: 'ORDER_STATUS_DEBUG',
|
||||
ORDER_BATCH_DELETE: 'ORDER_BATCH_DELETE',
|
||||
ORDER_DELETE: 'ORDER_DELETE',
|
||||
ORDER_PROXY_CREATE: 'ORDER_PROXY_CREATE',
|
||||
STORE_CREATE: 'STORE_CREATE',
|
||||
STORE_UPDATE: 'STORE_UPDATE',
|
||||
@@ -123,6 +124,7 @@ export const HQ_OPERATION_ACTION_LABELS: Record<string, string> = {
|
||||
[HqOperationAction.ORDER_SHIP]: '订单发货',
|
||||
[HqOperationAction.ORDER_STATUS_DEBUG]: '订单状态调试',
|
||||
[HqOperationAction.ORDER_BATCH_DELETE]: '批量删除订单',
|
||||
[HqOperationAction.ORDER_DELETE]: '删除订单',
|
||||
[HqOperationAction.ORDER_PROXY_CREATE]: '总部代下单',
|
||||
[HqOperationAction.STORE_CREATE]: '新增门店',
|
||||
[HqOperationAction.STORE_UPDATE]: '编辑门店',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
HQ_DANGEROUS_PERMISSION_KEYS,
|
||||
HQ_PERMISSION_CATALOG,
|
||||
HQ_ROLE_DEFAULT_PERMISSIONS,
|
||||
LEGACY_SYSTEM_SETTINGS_KEY,
|
||||
@@ -82,7 +83,10 @@ export class AdminHqPermissionsService {
|
||||
const userPermissionKeys = expandHqPermissionKeys(userPerms.map((p) => p.permissionKey));
|
||||
|
||||
if (account.adminRole === 'SUPER_ADMIN') {
|
||||
const rolePermissionKeys = hqBasePermissionKeys();
|
||||
const rolePermissionKeys = [
|
||||
...hqBasePermissionKeys(),
|
||||
...HQ_DANGEROUS_PERMISSION_KEYS,
|
||||
] as HqPermissionKey[];
|
||||
const effectivePermissionKeys = [
|
||||
...new Set([...rolePermissionKeys, ...userPermissionKeys]),
|
||||
] as HqPermissionKey[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import {
|
||||
HqPermissionGuard,
|
||||
@@ -43,6 +43,18 @@ export class AdminOrdersController {
|
||||
return this.ordersService.detail(BigInt(id));
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@UseGuards(HqPermissionGuard)
|
||||
@RequireHqPermissions('orders_delete')
|
||||
@HqOperation({
|
||||
action: HqOperationAction.ORDER_DELETE,
|
||||
refType: 'ORDER',
|
||||
refIdParam: 'id',
|
||||
})
|
||||
remove(@Param('id') id: string) {
|
||||
return this.ordersService.deleteOrder(BigInt(id));
|
||||
}
|
||||
|
||||
/** HQ 发货:调用小飞侠创建运单并更新配送信息 */
|
||||
@Post(':id/ship')
|
||||
@HqOperation({
|
||||
|
||||
@@ -31,6 +31,7 @@ export class AdminOrdersService {
|
||||
|
||||
if (query.orderNo) where.orderNo = { contains: query.orderNo };
|
||||
if (query.status) where.status = query.status as Prisma.EnumOrderStatusFilter['equals'];
|
||||
if (query.orderType) where.orderType = query.orderType as Prisma.EnumOrderTypeFilter['equals'];
|
||||
if (query.userId) where.userId = BigInt(query.userId);
|
||||
if (query.cityId) where.cityId = BigInt(query.cityId);
|
||||
if (query.receiverPhone) where.receiverPhone = { contains: query.receiverPhone };
|
||||
@@ -335,6 +336,25 @@ export class AdminOrdersService {
|
||||
};
|
||||
}
|
||||
|
||||
async deleteOrder(id: bigint) {
|
||||
const order = await this.prisma.order.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, orderNo: true },
|
||||
});
|
||||
if (!order) throw new NotFoundException('订单不存在');
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await this.deleteOrdersInTx(tx, [id]);
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
deleted: 1,
|
||||
orderNo: order.orderNo,
|
||||
message: `订单 ${order.orderNo} 及关联业务数据已删除`,
|
||||
};
|
||||
}
|
||||
|
||||
private async deleteOrdersInTx(tx: Prisma.TransactionClient, orderIds: bigint[]) {
|
||||
if (!orderIds.length) return;
|
||||
|
||||
@@ -368,6 +388,10 @@ export class AdminOrdersService {
|
||||
await tx.benefitCoupon.deleteMany({ where: { id: { in: couponIds } } });
|
||||
}
|
||||
|
||||
await tx.userInvoice.deleteMany({ where: { orderId: { in: orderIds } } });
|
||||
await tx.wineryBillItem.deleteMany({ where: { orderId: { in: orderIds } } });
|
||||
await tx.logisticsBillItem.deleteMany({ where: { orderId: { in: orderIds } } });
|
||||
|
||||
await tx.order.updateMany({
|
||||
where: { originOrderId: { in: orderIds } },
|
||||
data: { originOrderId: null },
|
||||
|
||||
@@ -48,6 +48,10 @@ export class AdminOrdersQueryDto extends PaginationQueryDto {
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['NORMAL', 'PROXY'])
|
||||
orderType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
userId?: string;
|
||||
|
||||
@@ -208,6 +208,26 @@ export class PartnerProxyOrderController {
|
||||
return this.tradeService.getPartnerProxyOrderOptions(user.actorId);
|
||||
}
|
||||
|
||||
@Get()
|
||||
list(
|
||||
@CurrentUser() user: AuthUser,
|
||||
@Query('page') page = '1',
|
||||
@Query('pageSize') pageSize = '20',
|
||||
@Query('keyword') keyword?: string,
|
||||
) {
|
||||
return this.tradeService.listPartnerProxyOrders(
|
||||
user.actorId,
|
||||
Number(page),
|
||||
Number(pageSize),
|
||||
keyword,
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@CurrentUser() user: AuthUser, @Param('id') id: string) {
|
||||
return this.tradeService.getPartnerProxyOrder(user.actorId, BigInt(id));
|
||||
}
|
||||
|
||||
@Post('preview')
|
||||
preview(@CurrentUser() user: AuthUser, @Body() dto: PartnerProxyOrderPreviewDto) {
|
||||
return this.tradeService.previewPartnerProxyOrderForPartner(user.actorId, dto);
|
||||
|
||||
@@ -1359,7 +1359,76 @@ export class TradeService {
|
||||
},
|
||||
});
|
||||
|
||||
return this.getPartnerOrder(partnerAccountId, order.id);
|
||||
return serializeBigInt(mapOrderCompat(order));
|
||||
}
|
||||
|
||||
/** 合伙人代下单列表:按 proxyPartnerAccountId 归属,与管仓订单无关 */
|
||||
async listPartnerProxyOrders(
|
||||
partnerAccountId: bigint,
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
keyword?: string,
|
||||
) {
|
||||
const primary = await this.partnerCityService.resolvePrimaryAccount(partnerAccountId);
|
||||
const kw = keyword?.trim();
|
||||
const where = {
|
||||
orderType: 'PROXY' as const,
|
||||
proxyPartnerAccountId: primary.id,
|
||||
...(kw
|
||||
? {
|
||||
OR: [
|
||||
{ orderNo: { contains: kw } },
|
||||
{ receiverPhone: { contains: kw } },
|
||||
{ receiverName: { contains: kw } },
|
||||
{ productName: { contains: kw } },
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const take = Math.min(Math.max(pageSize, 1), 50);
|
||||
const skip = (Math.max(page, 1) - 1) * take;
|
||||
const [list, total] = await Promise.all([
|
||||
this.prisma.order.findMany({
|
||||
where,
|
||||
include: {
|
||||
delivery: true,
|
||||
imageResource: true,
|
||||
user: { select: { phone: true, nickname: true } },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip,
|
||||
take,
|
||||
}),
|
||||
this.prisma.order.count({ where }),
|
||||
]);
|
||||
return {
|
||||
list: serializeBigInt(list.map(mapOrderCompat)),
|
||||
total,
|
||||
page: Math.max(page, 1),
|
||||
pageSize: take,
|
||||
};
|
||||
}
|
||||
|
||||
async getPartnerProxyOrder(partnerAccountId: bigint, orderId: bigint) {
|
||||
const primary = await this.partnerCityService.resolvePrimaryAccount(partnerAccountId);
|
||||
const order = await this.prisma.order.findFirst({
|
||||
where: {
|
||||
id: orderId,
|
||||
orderType: 'PROXY',
|
||||
proxyPartnerAccountId: primary.id,
|
||||
},
|
||||
include: {
|
||||
delivery: true,
|
||||
user: true,
|
||||
imageResource: true,
|
||||
},
|
||||
});
|
||||
if (!order) throw new NotFoundException('代下单不存在');
|
||||
const statusLogs = await this.prisma.commonEvent.findMany({
|
||||
where: orderStatusLogWhere(orderId),
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
return serializeBigInt(mapOrderCompat({ ...order, statusLogs: mapStatusLogCompat(statusLogs) }));
|
||||
}
|
||||
|
||||
/** HQ 代下单:商品/推广码选项(运营侧可看白名单测试酒) */
|
||||
|
||||
Reference in New Issue
Block a user