From 94b85d5c3612f347483956ff7d5637be6255771b Mon Sep 17 00:00:00 2001 From: selfrelease Date: Sun, 16 Aug 2026 10:59:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=90=8C=E4=BC=81=E4=B8=9A=E5=86=85?= =?UTF-8?q?=E5=91=98=E5=B7=A5=E8=BA=AB=E4=BB=BD=E8=AF=81=E5=92=8C=E6=89=8B?= =?UTF-8?q?=E6=9C=BA=E5=8F=B7=E5=94=AF=E4=B8=80=E6=80=A7=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充以下场景的查重: 1. updateEmployee:编辑员工信息时手机号/身份证查重 (排除自身,同组织内不可与其他员工重复) 2. import.routes.ts:Excel 批量导入时身份证和手机号查重 (重复则跳过并记录错误日志) 至此同企业内身份证和手机号唯一性校验覆盖全部入口: - createEmployee(新增员工) - updateEmployee(编辑员工) - Excel 导入(批量导入) - work-process(走 createEmployee) Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- backend/src/routes/import.routes.ts | 28 ++++++++++++++++++++++++ backend/src/services/contract.service.ts | 21 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/backend/src/routes/import.routes.ts b/backend/src/routes/import.routes.ts index 4b797fa..6da98a2 100644 --- a/backend/src/routes/import.routes.ts +++ b/backend/src/routes/import.routes.ts @@ -267,6 +267,34 @@ router.post('/excel', authMiddleware, requireAdmin, upload.single('file'), async if (idCheck.upgraded) idCard = idCheck.upgraded } + // 同企业内身份证查重 + if (idCard) { + const idCardExists = await prisma.employee.findFirst({ + where: { orgId, idCardHash: sha256(idCard) }, + select: { id: true, name: true }, + }) + if (idCardExists) { + result.skipped++ + result.errors.push(`员工第${i + 2}行:证件号码已存在(${idCardExists.name}),跳过`) + result.details.push({ sheet: '员工信息', row: i + 2, name, status: 'skipped', message: '证件号码已存在' }) + continue + } + } + // 同企业内手机号查重 + const importPhone = val(getField(r, '手机号')) + if (importPhone) { + const phoneExists = await prisma.employee.findFirst({ + where: { orgId, phone: importPhone }, + select: { id: true, name: true }, + }) + if (phoneExists) { + result.skipped++ + result.errors.push(`员工第${i + 2}行:手机号已存在(${phoneExists.name}),跳过`) + result.details.push({ sheet: '员工信息', row: i + 2, name, status: 'skipped', message: '手机号已存在' }) + continue + } + } + // 社保基数:值为 0 或「无」表示不参保 const socialInsBaseVal = num(getField(r, '社保基数')) const socialInsOptOut = val(getField(r, '社保基数')) === '无' || val(getField(r, '社保基数')) === '不缴' diff --git a/backend/src/services/contract.service.ts b/backend/src/services/contract.service.ts index 68362a5..808deeb 100644 --- a/backend/src/services/contract.service.ts +++ b/backend/src/services/contract.service.ts @@ -536,6 +536,27 @@ export async function updateEmployee(orgId: string, id: string, data: any) { throw { code: 'NOT_FOUND', message: '员工不存在' } } + // 手机号查重(同组织内排除自身,phone 唯一影响员工端登录) + if (data.phone !== undefined && data.phone) { + const phoneExists = await prisma.employee.findFirst({ + where: { orgId, phone: data.phone, NOT: { id } }, + select: { id: true, name: true, department: true, status: true }, + }) + if (phoneExists) { + throw { code: 'DUPLICATE_PHONE', message: `手机号已存在:${phoneExists.name}(${phoneExists.department}),同企业内手机号不可重复` } + } + } + // 身份证查重(同组织内排除自身) + if (data.idCardNumber !== undefined && data.idCardNumber) { + const idCardExists = await prisma.employee.findFirst({ + where: { orgId, idCardHash: sha256(data.idCardNumber), NOT: { id } }, + select: { id: true, name: true, department: true, status: true }, + }) + if (idCardExists) { + throw { code: 'DUPLICATE_ID_CARD', message: `证件号码已存在:${idCardExists.name}(${idCardExists.department}),同企业内身份证号不可重复` } + } + } + const updateData: any = {} if (data.name !== undefined) updateData.name = data.name if (data.department !== undefined) updateData.department = data.department