工单和发票

酒厂银行账户
This commit is contained in:
2026-07-23 14:51:20 +08:00
parent 75765bf9d4
commit 82c447b193
13 changed files with 575 additions and 21 deletions
@@ -170,6 +170,23 @@ export class CreateTicketDto {
extraJson?: Record<string, unknown>;
}
export class AdminCreateTicketDto {
@IsString()
@IsIn(['REFUND', 'RESHIPMENT', 'ALERT', 'DAMAGE_RETURN', 'RETURN_REFUND'])
ticketType: string;
@IsString()
@IsNotEmpty()
orderNo: string;
@IsOptional()
@IsString()
remark?: string;
@IsOptional()
evidenceUrls?: string[];
}
export class UpdateTicketStatusDto {
@IsString()
@IsNotEmpty()
@@ -2,8 +2,14 @@ import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/co
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
import { AuthUser } from '../../common/guards/jwt-auth.guard';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { HqOperation } from '../../common/hq-operation/hq-operation.decorator';
import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants';
import { TradeService } from '../trade/trade.service';
import { IssueInvoiceDto, RejectInvoiceDto } from '../trade/dto/after-sale.dto';
import {
AdminCreateInvoiceDto,
IssueInvoiceDto,
RejectInvoiceDto,
} from '../trade/dto/after-sale.dto';
@Controller('admin/invoices')
@UseGuards(HqAuthGuard)
@@ -23,12 +29,29 @@ export class AdminInvoicesController {
});
}
@Post()
@HqOperation({
action: HqOperationAction.INVOICE_CREATE,
refType: 'INVOICE',
batch: true,
includeBody: true,
})
create(@Body() body: AdminCreateInvoiceDto) {
return this.tradeService.adminCreateInvoice(body);
}
@Get(':id')
detail(@Param('id') id: string) {
return this.tradeService.adminGetInvoice(BigInt(id));
}
@Post(':id/issue')
@HqOperation({
action: HqOperationAction.INVOICE_ISSUE,
refType: 'INVOICE',
refIdParam: 'id',
includeBody: true,
})
issue(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@@ -38,6 +61,12 @@ export class AdminInvoicesController {
}
@Post(':id/reject')
@HqOperation({
action: HqOperationAction.INVOICE_REJECT,
refType: 'INVOICE',
refIdParam: 'id',
includeBody: true,
})
reject(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@@ -8,6 +8,7 @@ import { HqOperation } from '../../common/hq-operation/hq-operation.decorator';
import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants';
import { AdminTicketsService } from './admin-tickets.service';
import { TicketListQueryDto } from '../common/dto/common-query.dto';
import { AdminCreateTicketDto } from '../common/dto/common-mutate.dto';
@Controller('admin/tickets')
@UseGuards(HqAuthGuard)
@@ -19,6 +20,17 @@ export class AdminTicketsController {
return this.service.list(query);
}
@Post()
@HqOperation({
action: HqOperationAction.TICKET_CREATE,
refType: 'TICKET',
batch: true,
includeBody: true,
})
create(@Body() body: AdminCreateTicketDto) {
return this.service.createByHq(body);
}
@Get(':id')
detail(@Param('id') id: string) {
return this.service.detail(BigInt(id));
@@ -40,6 +40,43 @@ export class AdminTicketsService {
return this.ticketService.detail(id);
}
async createByHq(body: {
ticketType: string;
orderNo: string;
remark?: string;
evidenceUrls?: string[];
}) {
const orderNo = body.orderNo?.trim();
if (!orderNo) throw new BadRequestException('请填写订单号');
const order = await this.prisma.order.findFirst({ where: { orderNo } });
if (!order) throw new NotFoundException('订单不存在');
if (order.status === 'PENDING_PAY' || order.status === 'CANCELLED') {
throw new BadRequestException('当前订单不可创建工单');
}
if (['REFUNDING', 'REFUNDED'].includes(order.status) && body.ticketType !== 'ALERT') {
throw new BadRequestException('订单已在退款流程中');
}
const pending = await this.prisma.commonTicket.findFirst({
where: {
ticketType: body.ticketType as never,
refType: 'ORDER',
refId: order.id,
status: { in: ['PENDING', 'OPEN'] },
},
});
if (pending) throw new BadRequestException('该类型工单已在处理中');
const evidenceUrls = (body.evidenceUrls ?? []).filter((u) => typeof u === 'string' && u.trim());
return this.ticketService.create({
ticketType: body.ticketType,
refType: 'ORDER',
refId: order.id.toString(),
remark: body.remark ?? '',
extraJson: evidenceUrls.length ? { evidenceUrls } : undefined,
});
}
private parseExtra(raw: unknown): TicketCollabExtra {
if (!raw || typeof raw !== 'object') return {};
return raw as TicketCollabExtra;
@@ -64,6 +64,13 @@ export class CreateInvoiceDto {
remark?: string;
}
export class AdminCreateInvoiceDto extends CreateInvoiceDto {
@IsString()
@IsNotEmpty()
@MaxLength(32)
orderNo: string;
}
export class IssueInvoiceDto {
@IsString()
@IsNotEmpty()
@@ -754,6 +754,27 @@ export class TradeService {
return days;
}
async adminCreateInvoice(
body: {
orderNo: string;
titleType: string;
invoiceKind: string;
titleName: string;
taxNo?: string;
addressPhone?: string;
bankAccount?: string;
email: string;
phone: string;
remark?: string;
},
) {
const orderNo = body.orderNo?.trim();
if (!orderNo) throw new BadRequestException('请填写订单号');
const order = await this.prisma.order.findFirst({ where: { orderNo } });
if (!order) throw new NotFoundException('订单不存在');
return this.createInvoice(order.userId, order.id, body);
}
async adminListInvoices(query: { status?: string; page?: number; pageSize?: number }) {
const page = query.page ?? 1;
const pageSize = query.pageSize ?? 20;