fix: 创建员工时从身份证解析性别和出生日期

- contract.service.ts createEmployee: gender 为空时从身份证第17位解析,birthDate 从第7-14位提取
- portal.routes.ts GET /profile: gender/birthDate 为空时从身份证 fallback 解析
- gender 统一使用中文 男/女(与数据库和其他服务一致)
- 前端 MyProfile.tsx 性别显示适配中文
This commit is contained in:
freedakgmail
2026-08-17 19:04:22 +08:00
parent a6189d209d
commit 4eb882b1e2
3 changed files with 23 additions and 4 deletions
+20 -2
View File
@@ -885,11 +885,29 @@ router.get('/profile', portalAuth, async (req: any, res, next) => {
try { if (emp.idCardNumber) idCardNo = decrypt(emp.idCardNumber) } catch {} try { if (emp.idCardNumber) idCardNo = decrypt(emp.idCardNumber) } catch {}
try { if (emp.bankAccount) bankAccount = decrypt(emp.bankAccount) } catch {} try { if (emp.bankAccount) bankAccount = decrypt(emp.bankAccount) } catch {}
// 从身份证号解析性别和出生日期(数据库字段为空时)
let gender = emp.gender || ''
let birthDate = emp.birthDate ? emp.birthDate.toISOString().slice(0, 10) : ''
if (idCardNo && idCardNo.length === 18) {
// 第17位:奇数=男,偶数=女
if (!gender) {
const genderDigit = parseInt(idCardNo[16], 10)
gender = genderDigit % 2 === 1 ? '男' : '女'
}
// 第7-14位:出生日期 YYYYMMDD
if (!birthDate) {
const y = idCardNo.slice(6, 10)
const m = idCardNo.slice(10, 12)
const d = idCardNo.slice(12, 14)
birthDate = `${y}-${m}-${d}`
}
}
res.json({ res.json({
success: true, success: true,
data: { data: {
name: emp.name, name: emp.name,
gender: emp.gender || '', gender,
phone: emp.phone || '', phone: emp.phone || '',
idCardNo, idCardNo,
idType: emp.idType || 'ID_CARD', idType: emp.idType || 'ID_CARD',
@@ -903,7 +921,7 @@ router.get('/profile', portalAuth, async (req: any, res, next) => {
bankName: emp.bankName || '', bankName: emp.bankName || '',
education: emp.education || '', education: emp.education || '',
city: emp.city || '', city: emp.city || '',
birthDate: emp.birthDate ? emp.birthDate.toISOString().slice(0, 10) : '', birthDate,
}, },
}) })
} catch (err) { next(err) } } catch (err) { next(err) }
+2 -1
View File
@@ -410,12 +410,13 @@ export async function createEmployee(orgId: string, userId: string, data: any) {
monthlySalary: encrypt(data.monthlySalary), monthlySalary: encrypt(data.monthlySalary),
baseSalary: encrypt(String(baseSalaryNum)), baseSalary: encrypt(String(baseSalaryNum)),
performanceSalary: encrypt(String(performanceSalaryNum)), performanceSalary: encrypt(String(performanceSalaryNum)),
gender: data.gender, gender: data.gender || (data.idCardNumber ? extractGenderFromIdCard(data.idCardNumber) : null),
femaleWorkerType: data.femaleWorkerType, femaleWorkerType: data.femaleWorkerType,
phone: data.phone, phone: data.phone,
passwordHash, passwordHash,
idCardNumber: data.idCardNumber ? encrypt(data.idCardNumber) : null, idCardNumber: data.idCardNumber ? encrypt(data.idCardNumber) : null,
idCardHash: data.idCardNumber ? sha256(data.idCardNumber) : null, idCardHash: data.idCardNumber ? sha256(data.idCardNumber) : null,
birthDate: data.idCardNumber ? extractBirthDateFromIdCard(data.idCardNumber) : null,
isPregnant: data.isPregnant || false, isPregnant: data.isPregnant || false,
isInMedicalPeriod: data.isInMedicalPeriod || false, isInMedicalPeriod: data.isInMedicalPeriod || false,
isWorkInjured: data.isWorkInjured || false, isWorkInjured: data.isWorkInjured || false,
+1 -1
View File
@@ -75,7 +75,7 @@ export default function MyProfile() {
const readOnlyFields = [ const readOnlyFields = [
{ label: '姓名', value: profile.name, icon: User }, { label: '姓名', value: profile.name, icon: User },
{ label: '性别', value: profile.gender === 'M' ? '男' : profile.gender === 'F' ? '女' : '未填写', icon: User }, { label: '性别', value: profile.gender === '' ? '男' : profile.gender === '' ? '女' : '未填写', icon: User },
{ label: '手机号', value: profile.phone || '未填写', icon: Phone }, { label: '手机号', value: profile.phone || '未填写', icon: Phone },
{ label: '证件号码', value: profile.idCardNo || '未填写', icon: IdCard }, { label: '证件号码', value: profile.idCardNo || '未填写', icon: IdCard },
{ label: '部门', value: profile.department || '未填写', icon: User }, { label: '部门', value: profile.department || '未填写', icon: User },