admin web 修改订单状态
This commit is contained in:
@@ -10,21 +10,11 @@ import {
|
|||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Typography,
|
Typography,
|
||||||
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { request, type AdminOrderRow, type Paginated } from '../lib/api';
|
import { request, type AdminOrderRow, type Paginated } from '../lib/api';
|
||||||
|
import { ORDER_STATUS_LABELS } from '../lib/constants';
|
||||||
const STATUS_LABELS: Record<string, string> = {
|
|
||||||
PENDING_PAY: '待付款',
|
|
||||||
PENDING_SHIP: '待发货',
|
|
||||||
OUT_WAREHOUSE: '已出库',
|
|
||||||
SHIPPING: '配送中',
|
|
||||||
PENDING_RECEIVE: '待收货',
|
|
||||||
COMPLETED: '已完成',
|
|
||||||
CANCELLED: '已取消',
|
|
||||||
REFUNDING: '退款中',
|
|
||||||
REFUNDED: '已退款',
|
|
||||||
};
|
|
||||||
|
|
||||||
type OrderDetail = AdminOrderRow & {
|
type OrderDetail = AdminOrderRow & {
|
||||||
receiverAddress?: string;
|
receiverAddress?: string;
|
||||||
@@ -93,7 +83,7 @@ export default function OrdersPage() {
|
|||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (s) => <Tag>{STATUS_LABELS[s] || s}</Tag>,
|
render: (s) => <Tag>{ORDER_STATUS_LABELS[s] || s}</Tag>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '配送',
|
title: '配送',
|
||||||
@@ -140,7 +130,7 @@ export default function OrdersPage() {
|
|||||||
<Input placeholder="DK..." allowClear />
|
<Input placeholder="DK..." allowClear />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="status" label="状态">
|
<Form.Item name="status" label="状态">
|
||||||
<Select allowClear style={{ width: 120 }} options={Object.entries(STATUS_LABELS).map(([value, label]) => ({ value, label }))} />
|
<Select allowClear style={{ width: 120 }} options={Object.entries(ORDER_STATUS_LABELS).map(([value, label]) => ({ value, label }))} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="receiverPhone" label="收货手机">
|
<Form.Item name="receiverPhone" label="收货手机">
|
||||||
<Input allowClear />
|
<Input allowClear />
|
||||||
@@ -171,12 +161,37 @@ export default function OrdersPage() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Drawer title="订单详情" width={640} open={drawerOpen} onClose={() => setDrawerOpen(false)}>
|
<Drawer
|
||||||
|
title="订单详情"
|
||||||
|
width={640}
|
||||||
|
open={drawerOpen}
|
||||||
|
onClose={() => setDrawerOpen(false)}
|
||||||
|
extra={detail && (
|
||||||
|
<Space>
|
||||||
|
<Typography.Text type="secondary">调试改状态</Typography.Text>
|
||||||
|
<Select
|
||||||
|
value={detail.status}
|
||||||
|
style={{ width: 120 }}
|
||||||
|
options={Object.entries(ORDER_STATUS_LABELS).map(([value, label]) => ({ value, label }))}
|
||||||
|
onChange={async (status) => {
|
||||||
|
await request(`/admin/orders/${detail.id}/status`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify({ status }),
|
||||||
|
});
|
||||||
|
message.success('状态已更新(调试)');
|
||||||
|
const res = await request<OrderDetail>(`/admin/orders/${detail.id}`);
|
||||||
|
setDetail(res);
|
||||||
|
void load();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
>
|
||||||
{detail && (
|
{detail && (
|
||||||
<>
|
<>
|
||||||
<Descriptions column={1} bordered size="small" title="基本信息">
|
<Descriptions column={1} bordered size="small" title="基本信息">
|
||||||
<Descriptions.Item label="订单号">{detail.orderNo}</Descriptions.Item>
|
<Descriptions.Item label="订单号">{detail.orderNo}</Descriptions.Item>
|
||||||
<Descriptions.Item label="状态">{STATUS_LABELS[detail.status] || detail.status}</Descriptions.Item>
|
<Descriptions.Item label="状态">{ORDER_STATUS_LABELS[detail.status] || detail.status}</Descriptions.Item>
|
||||||
<Descriptions.Item label="用户">{detail.user?.userNo} / {detail.user?.nickname}</Descriptions.Item>
|
<Descriptions.Item label="用户">{detail.user?.userNo} / {detail.user?.nickname}</Descriptions.Item>
|
||||||
<Descriptions.Item label="商品额">¥{detail.productAmount}</Descriptions.Item>
|
<Descriptions.Item label="商品额">¥{detail.productAmount}</Descriptions.Item>
|
||||||
<Descriptions.Item label="实付">¥{detail.payAmount}</Descriptions.Item>
|
<Descriptions.Item label="实付">¥{detail.payAmount}</Descriptions.Item>
|
||||||
@@ -215,7 +230,7 @@ export default function OrdersPage() {
|
|||||||
dataSource={detail.statusLogs}
|
dataSource={detail.statusLogs}
|
||||||
columns={[
|
columns={[
|
||||||
{ title: '从', dataIndex: 'fromStatus', render: (v) => v || '—' },
|
{ title: '从', dataIndex: 'fromStatus', render: (v) => v || '—' },
|
||||||
{ title: '到', dataIndex: 'toStatus', render: (v) => STATUS_LABELS[v] || v },
|
{ title: '到', dataIndex: 'toStatus', render: (v) => ORDER_STATUS_LABELS[v] || v },
|
||||||
{ title: '时间', dataIndex: 'createdAt', render: (v) => new Date(v).toLocaleString('zh-CN') },
|
{ title: '时间', dataIndex: 'createdAt', render: (v) => new Date(v).toLocaleString('zh-CN') },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Get, Param, Put, Query, UseGuards } from '@nestjs/common';
|
||||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||||
import { AdminOrdersService } from './admin-orders.service';
|
import { AdminOrdersService } from './admin-orders.service';
|
||||||
|
import { UpdateOrderStatusDto } from './dto/admin-mutate.dto';
|
||||||
import { AdminOrdersQueryDto } from './dto/admin-query.dto';
|
import { AdminOrdersQueryDto } from './dto/admin-query.dto';
|
||||||
|
|
||||||
@Controller('admin/orders')
|
@Controller('admin/orders')
|
||||||
@@ -17,4 +18,10 @@ export class AdminOrdersController {
|
|||||||
detail(@Param('id') id: string) {
|
detail(@Param('id') id: string) {
|
||||||
return this.ordersService.detail(BigInt(id));
|
return this.ordersService.detail(BigInt(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** preV1 调试:直接改订单状态,不走业务校验 */
|
||||||
|
@Put(':id/status')
|
||||||
|
updateStatus(@Param('id') id: string, @Body() dto: UpdateOrderStatusDto) {
|
||||||
|
return this.ordersService.updateStatusDebug(BigInt(id), dto.status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import { PrismaService } from '../../common/prisma/prisma.module';
|
|||||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||||
import { orderStatusLogWhere } from '../../common/event/event.helpers';
|
import { orderStatusLogWhere } from '../../common/event/event.helpers';
|
||||||
import { mapOrderCompat, mapStatusLogCompat } from '../../common/compat/v31-compat';
|
import { mapOrderCompat, mapStatusLogCompat } from '../../common/compat/v31-compat';
|
||||||
|
import { TradeService } from '../trade/trade.service';
|
||||||
import type { AdminOrdersQueryDto } from './dto/admin-query.dto';
|
import type { AdminOrdersQueryDto } from './dto/admin-query.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminOrdersService {
|
export class AdminOrdersService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly tradeService: TradeService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async list(query: AdminOrdersQueryDto) {
|
async list(query: AdminOrdersQueryDto) {
|
||||||
const page = query.page ?? 1;
|
const page = query.page ?? 1;
|
||||||
@@ -76,4 +80,11 @@ export class AdminOrdersService {
|
|||||||
benefitCoupons: order.benefitCoupon ? [order.benefitCoupon] : [],
|
benefitCoupons: order.benefitCoupon ? [order.benefitCoupon] : [],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateStatusDebug(id: bigint, status: string) {
|
||||||
|
const order = await this.prisma.order.findUnique({ where: { id } });
|
||||||
|
if (!order) throw new NotFoundException('订单不存在');
|
||||||
|
await this.tradeService.applyStatusTransition(id, order.status, status, 'HQ_DEBUG');
|
||||||
|
return this.detail(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,6 +270,22 @@ export class UpdateStoreMediaDto {
|
|||||||
sortOrder?: number;
|
sortOrder?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class UpdateOrderStatusDto {
|
||||||
|
@IsString()
|
||||||
|
@IsIn([
|
||||||
|
'PENDING_PAY',
|
||||||
|
'PENDING_SHIP',
|
||||||
|
'OUT_WAREHOUSE',
|
||||||
|
'SHIPPING',
|
||||||
|
'PENDING_RECEIVE',
|
||||||
|
'COMPLETED',
|
||||||
|
'CANCELLED',
|
||||||
|
'REFUNDING',
|
||||||
|
'REFUNDED',
|
||||||
|
])
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class UpdateDeliveryDto {
|
export class UpdateDeliveryDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { IamModule } from '../iam/iam.module';
|
import { IamModule } from '../iam/iam.module';
|
||||||
|
import { TradeModule } from '../trade/trade.module';
|
||||||
import { AdminDashboardController } from './admin-dashboard.controller';
|
import { AdminDashboardController } from './admin-dashboard.controller';
|
||||||
import { AdminDashboardService } from './admin-dashboard.service';
|
import { AdminDashboardService } from './admin-dashboard.service';
|
||||||
import { AdminUsersController } from './admin-users.controller';
|
import { AdminUsersController } from './admin-users.controller';
|
||||||
@@ -23,7 +24,7 @@ import { AdminProductsService } from './admin-products.service';
|
|||||||
import { SuperAdminGuard } from '../../common/guards/super-admin.guard';
|
import { SuperAdminGuard } from '../../common/guards/super-admin.guard';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [IamModule],
|
imports: [IamModule, TradeModule],
|
||||||
controllers: [
|
controllers: [
|
||||||
AdminDashboardController,
|
AdminDashboardController,
|
||||||
AdminUsersController,
|
AdminUsersController,
|
||||||
|
|||||||
Reference in New Issue
Block a user