feat: add api http conventions and booking state rules

This commit is contained in:
2026-09-18 17:17:47 +08:00
parent 0d4caaba00
commit 0e8b8e1591
5 changed files with 61 additions and 0 deletions

View File

@@ -132,6 +132,15 @@ export class BookingService {
) {
const booking = await this.prisma.booking.findFirst({ where: { id, brandId } });
if (!booking) throw new NotFoundException("订单不存在");
const allowed: Record<string, string[]> = {
received: ["contacting", "cancelled"],
contacting: ["confirmed", "cancelled"],
confirmed: ["completed", "cancelled"],
completed: [],
cancelled: [],
};
if (status !== booking.status && !allowed[booking.status]?.includes(status))
throw new BadRequestException("不允许的订单状态流转");
return this.prisma.booking.update({
where: { id },
data: { status, ...(adminNote !== undefined ? { adminNote } : {}), isRead: true },

32
apps/api/src/http.ts Normal file
View File

@@ -0,0 +1,32 @@
import { Catch, HttpException, Injectable } from "@nestjs/common";
import type { ArgumentsHost, ExceptionFilter } from "@nestjs/common";
import type { FastifyReply, FastifyRequest } from "fastify";
import { randomUUID } from "node:crypto";
@Injectable()
export class RequestIdMiddleware {
use(request: FastifyRequest, reply: FastifyReply, next: () => void): void {
const requestId = request.headers["x-request-id"]?.toString() ?? randomUUID();
reply.header("x-request-id", requestId);
next();
}
}
@Catch()
export class ApiExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost): void {
const context = host.switchToHttp();
const response = context.getResponse<FastifyReply>();
const request = context.getRequest<FastifyRequest>();
const status = exception instanceof HttpException ? exception.getStatus() : 500;
const detail = exception instanceof HttpException ? exception.getResponse() : "服务器内部错误";
response.status(status).send({
data: null,
error: {
code: `HTTP_${status}`,
message: typeof detail === "string" ? detail : "请求失败",
},
requestId: request.headers["x-request-id"] ?? null,
});
}
}

View File

@@ -23,6 +23,7 @@ import { AdminAuthService } from "./admin-auth.service.js";
import { AdminLoginDto } from "./admin-auth.dto.js";
import type { FastifyRequest } from "fastify";
import { readIdentity, signIdentity } from "./auth.js";
import { ApiExceptionFilter, RequestIdMiddleware } from "./http.js";
@Controller("health")
export class HealthController {
@@ -250,6 +251,8 @@ export async function createApp(): Promise<NestFastifyApplication> {
app.useGlobalPipes(
new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }),
);
app.useGlobalFilters(new ApiExceptionFilter());
app.use(new RequestIdMiddleware().use.bind(new RequestIdMiddleware()));
app.enableCors();
return app;
}