feat: add admin order tags and read state
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { IsIn, IsOptional, IsString } from "class-validator";
|
||||
import { IsArray, IsIn, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class UpdateBookingStatusDto {
|
||||
@IsIn(["received", "contacting", "confirmed", "completed", "cancelled"])
|
||||
@@ -8,3 +8,11 @@ export class UpdateBookingStatusDto {
|
||||
@IsString()
|
||||
adminNote?: string;
|
||||
}
|
||||
|
||||
export class CreateTagDto {
|
||||
@IsString() name!: string;
|
||||
}
|
||||
|
||||
export class UpdateBookingTagsDto {
|
||||
@IsArray() @IsString({ each: true }) tagIds!: string[];
|
||||
}
|
||||
|
||||
@@ -137,4 +137,43 @@ export class BookingService {
|
||||
data: { status, ...(adminNote !== undefined ? { adminNote } : {}), isRead: true },
|
||||
});
|
||||
}
|
||||
|
||||
async markRead(brandId: string, id: string) {
|
||||
const booking = await this.prisma.booking.findFirst({ where: { id, brandId } });
|
||||
if (!booking) throw new NotFoundException("订单不存在");
|
||||
return this.prisma.booking.update({ where: { id }, data: { isRead: true } });
|
||||
}
|
||||
|
||||
async listTags(brandId: string) {
|
||||
return this.prisma.tag.findMany({
|
||||
where: { brandId, isEnabled: true },
|
||||
orderBy: [{ isPreset: "desc" }, { name: "asc" }],
|
||||
});
|
||||
}
|
||||
|
||||
async createTag(brandId: string, name: string) {
|
||||
const normalized = name.trim();
|
||||
if (!normalized || normalized.length > 24) throw new BadRequestException("标签名称长度无效");
|
||||
return this.prisma.tag.create({ data: { brandId, name: normalized } });
|
||||
}
|
||||
|
||||
async updateTags(brandId: string, bookingId: string, tagIds: string[]) {
|
||||
const booking = await this.prisma.booking.findFirst({ where: { id: bookingId, brandId } });
|
||||
if (!booking) throw new NotFoundException("订单不存在");
|
||||
const tags = await this.prisma.tag.findMany({
|
||||
where: { id: { in: tagIds }, brandId, isEnabled: true },
|
||||
select: { id: true },
|
||||
});
|
||||
if (tags.length !== new Set(tagIds).size) throw new BadRequestException("存在无效标签");
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.bookingTag.deleteMany({ where: { bookingId } }),
|
||||
this.prisma.bookingTag.createMany({
|
||||
data: tags.map((tag) => ({ bookingId, tagId: tag.id })),
|
||||
}),
|
||||
]);
|
||||
return this.prisma.booking.findUnique({
|
||||
where: { id: bookingId },
|
||||
include: { tags: { include: { tag: true } } },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { PrismaService } from "./prisma.service.js";
|
||||
import { WechatAuthService } from "./wechat-auth.js";
|
||||
import { BookingService } from "./booking.service.js";
|
||||
import { CreateBookingDto } from "./booking.dto.js";
|
||||
import { UpdateBookingStatusDto } from "./admin.dto.js";
|
||||
import { CreateTagDto, UpdateBookingStatusDto, UpdateBookingTagsDto } from "./admin.dto.js";
|
||||
import { AdminAuthService } from "./admin-auth.service.js";
|
||||
import { AdminLoginDto } from "./admin-auth.dto.js";
|
||||
import type { FastifyRequest } from "fastify";
|
||||
@@ -183,6 +183,46 @@ export class AdminBookingController {
|
||||
);
|
||||
}
|
||||
|
||||
@Post(":id/read")
|
||||
markRead(@Req() request: FastifyRequest, @Param("id") id: string): Promise<unknown> {
|
||||
this.requireAdmin(request);
|
||||
return this.bookings.markRead(
|
||||
process.env.DEFAULT_BRAND_ID ?? "00000000-0000-0000-0000-000000000001",
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
@Post(":id/tags")
|
||||
updateTags(
|
||||
@Req() request: FastifyRequest,
|
||||
@Param("id") id: string,
|
||||
@Body() body: UpdateBookingTagsDto,
|
||||
): Promise<unknown> {
|
||||
this.requireAdmin(request);
|
||||
return this.bookings.updateTags(
|
||||
process.env.DEFAULT_BRAND_ID ?? "00000000-0000-0000-0000-000000000001",
|
||||
id,
|
||||
body.tagIds,
|
||||
);
|
||||
}
|
||||
|
||||
@Get("/tags")
|
||||
listTags(@Req() request: FastifyRequest): Promise<unknown> {
|
||||
this.requireAdmin(request);
|
||||
return this.bookings.listTags(
|
||||
process.env.DEFAULT_BRAND_ID ?? "00000000-0000-0000-0000-000000000001",
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/tags")
|
||||
createTag(@Req() request: FastifyRequest, @Body() body: CreateTagDto): Promise<unknown> {
|
||||
this.requireAdmin(request);
|
||||
return this.bookings.createTag(
|
||||
process.env.DEFAULT_BRAND_ID ?? "00000000-0000-0000-0000-000000000001",
|
||||
body.name,
|
||||
);
|
||||
}
|
||||
|
||||
private requireAdmin(request: FastifyRequest): void {
|
||||
const token = request.headers.authorization?.replace(/^Bearer\s+/i, "");
|
||||
if (!token) throw new BadRequestException("缺少管理员登录态");
|
||||
|
||||
Reference in New Issue
Block a user