From e7f2d69def9a5f5b9602ed864dbde09f82491220 Mon Sep 17 00:00:00 2001 From: hailin Date: Thu, 25 Dec 2025 00:56:53 -0800 Subject: [PATCH] =?UTF-8?q?fix(planting-service):=20=E6=B7=BB=E5=8A=A0=20P?= =?UTF-8?q?2002=20=E9=94=99=E8=AF=AF=E6=8D=95=E8=8E=B7=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=B9=B6=E5=8F=91=E5=88=9B=E5=BB=BA=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL 的 upsert 在高并发下仍可能出现唯一约束错误, 添加 try-catch 捕获 P2002 错误,发生冲突时直接查询返回已存在的记录。 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../contract-signing-task.repository.impl.ts | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/backend/services/planting-service/src/infrastructure/persistence/repositories/contract-signing-task.repository.impl.ts b/backend/services/planting-service/src/infrastructure/persistence/repositories/contract-signing-task.repository.impl.ts index ca138357..e44c7c37 100644 --- a/backend/services/planting-service/src/infrastructure/persistence/repositories/contract-signing-task.repository.impl.ts +++ b/backend/services/planting-service/src/infrastructure/persistence/repositories/contract-signing-task.repository.impl.ts @@ -43,31 +43,51 @@ export class ContractSigningTaskRepositoryImpl implements IContractSigningTaskRe } else { // 创建 - 使用 upsert 处理并发幂等性 // 如果 orderNo 已存在,则只返回现有记录(不更新) - const result = await this.prisma.contractSigningTask.upsert({ - where: { orderNo: task.orderNo }, - create: { - orderNo: task.orderNo, - userId: task.userId, - accountSequence: task.accountSequence, - templateId: task.templateId, - contractVersion: task.contractVersion, - contractContent: task.contractContent, - userPhoneNumber: task.userPhoneNumber, - userRealName: task.userRealName, - userIdCardNumber: task.userIdCardNumber, - treeCount: task.treeCount, - totalAmount: new Prisma.Decimal(task.totalAmount), - provinceCode: task.provinceCode, - provinceName: task.provinceName, - cityCode: task.cityCode, - cityName: task.cityName, - status: task.status, - expiresAt: task.expiresAt, - }, - // 如果已存在,不更新任何字段,只返回现有记录 - update: {}, - }); - return this.mapToDomain(result); + // 额外捕获 P2002 错误以处理极端并发情况 + try { + const result = await this.prisma.contractSigningTask.upsert({ + where: { orderNo: task.orderNo }, + create: { + orderNo: task.orderNo, + userId: task.userId, + accountSequence: task.accountSequence, + templateId: task.templateId, + contractVersion: task.contractVersion, + contractContent: task.contractContent, + userPhoneNumber: task.userPhoneNumber, + userRealName: task.userRealName, + userIdCardNumber: task.userIdCardNumber, + treeCount: task.treeCount, + totalAmount: new Prisma.Decimal(task.totalAmount), + provinceCode: task.provinceCode, + provinceName: task.provinceName, + cityCode: task.cityCode, + cityName: task.cityName, + status: task.status, + expiresAt: task.expiresAt, + }, + // 如果已存在,不更新任何字段,只返回现有记录 + update: {}, + }); + return this.mapToDomain(result); + } catch (error: unknown) { + // 处理并发创建时的唯一约束冲突 (Prisma P2002) + if ( + error && + typeof error === 'object' && + 'code' in error && + error.code === 'P2002' + ) { + // 记录已被其他并发请求创建,直接查询返回 + const existing = await this.prisma.contractSigningTask.findUnique({ + where: { orderNo: task.orderNo }, + }); + if (existing) { + return this.mapToDomain(existing); + } + } + throw error; + } } }