import { QUOTE_ID_PREFIX } from "@/lib/constants/quote"; import { prisma } from "@/lib/prisma"; import { writeAudit } from "@/modules/audit/service"; import { QuoteIdConflictError } from "@/modules/quote/types"; function formatDatePart(date: Date): string { const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, "0"); const d = String(date.getDate()).padStart(2, "0"); return `${y}${m}${d}`; } /** QTE_YYYYMMDD_NNNN 格式,DB 序号保证唯一性 */ export async function generateQuoteId(): Promise { const datePart = formatDatePart(new Date()); const prefix = `${QUOTE_ID_PREFIX}_${datePart}_`; const latest = await prisma.quoteRecord.findFirst({ where: { quoteId: { startsWith: prefix } }, orderBy: { quoteId: "desc" }, select: { quoteId: true }, }); let seq = 1; if (latest?.quoteId) { const suffix = latest.quoteId.slice(prefix.length); const parsed = Number.parseInt(suffix, 10); if (!Number.isNaN(parsed)) { seq = parsed + 1; } } return `${prefix}${String(seq).padStart(4, "0")}`; } /** task-109:quote_id 唯一冲突 → 写 audit + 抛 INTERNAL_ERROR */ export async function handleQuoteIdConflict( quoteId: string, actorId: string, ): Promise { await writeAudit("QUOTE_ID_CONFLICT", actorId, quoteId, { quoteId, message: "quote_id 唯一键冲突", }); throw new QuoteIdConflictError("系统繁忙,请稍后重试"); } /** 检测 Prisma P2002 是否为 quote_id 冲突 */ export function isQuoteIdUniqueConflict(error: unknown): boolean { const prismaError = error as { code?: string; meta?: { target?: string[] } }; if (prismaError.code !== "P2002") { return false; } const target = prismaError.meta?.target; return ( Array.isArray(target) && (target.includes("quote_id") || target.includes("quoteId")) ); }