diff --git a/backend/src/routes/import.routes.ts b/backend/src/routes/import.routes.ts index 757d497..64d7375 100644 --- a/backend/src/routes/import.routes.ts +++ b/backend/src/routes/import.routes.ts @@ -260,6 +260,7 @@ router.post('/excel', authMiddleware, requireAdmin, upload.single('file'), async if (salary === '0') { result.skipped++; result.errors.push(`员工第${i + 2}行:月工资为空`); result.details.push({ sheet: '员工信息', row: i + 2, name, status: 'skipped', message: '月工资为空' }); continue } let idCard = val(getField(r, '身份证号')) + if (!idCard) { result.skipped++; result.errors.push(`员工第${i + 2}行:身份证号为空,跳过`); result.details.push({ sheet: '员工信息', row: i + 2, name, status: 'skipped', message: '身份证号为空' }); continue } if (idCard) { const idCheck = validateIdCard(idCard) if (!idCheck.valid) { result.skipped++; result.errors.push(`员工第${i + 2}行:${idCheck.error}`); result.details.push({ sheet: '员工信息', row: i + 2, name, status: 'skipped', message: idCheck.error }); continue } diff --git a/backend/src/schemas/contract.schema.ts b/backend/src/schemas/contract.schema.ts index d0a57f2..3bc172f 100644 --- a/backend/src/schemas/contract.schema.ts +++ b/backend/src/schemas/contract.schema.ts @@ -5,6 +5,7 @@ export const createEmployeeSchema = z.object({ department: z.string().min(1, '部门不能为空').max(50, '部门最多50个字'), hireDate: z.string().datetime(), monthlySalary: z.string().min(1, '月薪不能为空'), + idCardNumber: z.string().min(18, '身份证号不能为空且必须18位').max(18, '身份证号必须18位'), gender: z.enum(['男', '女']).optional(), femaleWorkerType: z.enum(['CADRE', 'WORKER']).optional(), phone: z.string().regex(/^1[3-9]\d{9}$/).optional(), @@ -30,6 +31,7 @@ export const updateEmployeeSchema = z.object({ department: z.string().min(1).max(50).optional(), hireDate: z.string().datetime().optional(), monthlySalary: z.string().min(1).optional(), + idCardNumber: z.string().min(18).max(18).optional(), gender: z.enum(['男', '女']).optional(), femaleWorkerType: z.enum(['CADRE', 'WORKER']).optional(), phone: z.string().regex(/^1[3-9]\d{9}$/).optional(), diff --git a/backend/src/services/contract.service.ts b/backend/src/services/contract.service.ts index 327ee63..e0352c9 100644 --- a/backend/src/services/contract.service.ts +++ b/backend/src/services/contract.service.ts @@ -1,6 +1,7 @@ import prisma from '../lib/prisma' import { encrypt, decrypt, sha256 } from '../lib/crypto' import { runRiskDetection } from './risk.service' +import { extractBirthDateFromIdCard, extractGenderFromIdCard } from './retirement.service' function daysBetween(a: Date, b: Date): number { return Math.floor((a.getTime() - b.getTime()) / (1000 * 60 * 60 * 24)) @@ -525,6 +526,15 @@ export async function updateEmployee(orgId: string, id: string, data: any) { if (data.gender !== undefined) updateData.gender = data.gender if (data.femaleWorkerType !== undefined) updateData.femaleWorkerType = data.femaleWorkerType if (data.phone !== undefined) updateData.phone = data.phone + if (data.idCardNumber !== undefined) { + updateData.idCardNumber = encrypt(data.idCardNumber) + updateData.idCardHash = sha256(data.idCardNumber) + updateData.birthDate = extractBirthDateFromIdCard(data.idCardNumber) + if (!updateData.gender) { + const gender = extractGenderFromIdCard(data.idCardNumber) + if (gender) updateData.gender = gender + } + } if (data.bankName !== undefined) updateData.bankName = data.bankName if (data.bankAccount !== undefined) updateData.bankAccount = encrypt(data.bankAccount) if (data.emergencyContact !== undefined) updateData.emergencyContact = data.emergencyContact diff --git a/backend/src/services/risk.service.ts b/backend/src/services/risk.service.ts index 808a5c0..a07db89 100644 --- a/backend/src/services/risk.service.ts +++ b/backend/src/services/risk.service.ts @@ -1674,8 +1674,8 @@ export async function getAnnualValueReport(orgId: string, year: number) { } for (const r of employeeRiskDetails) { - // 按身份证号分组,无身份证号时回退到 employeeId - const personKey = r.employee?.idCardHash || r.employeeId || '_unknown' + // 去重优先级:身份证号 > 姓名回退到姓名,避免同一人多条 Employee 记录被重复计算 + const personKey = r.employee?.idCardHash || r.employee?.name || r.employeeId || '_unknown' if (!personBreakdown[personKey]) { personBreakdown[personKey] = { personKey,