fix: 全部员工显示改为姓名+完整身份证号,不再脱敏
- 花名册列表显示完整身份证号 - 社保公积金月度办理列表姓名下显示身份证号 - 员工详情页显示完整身份证号 - 商业保险列表显示完整身份证号 - 后端去掉 idCardMasked 脱敏逻辑
This commit is contained in:
@@ -2,6 +2,7 @@ import { Router, Response, NextFunction } from 'express'
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import prisma from '../lib/prisma'
|
import prisma from '../lib/prisma'
|
||||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||||
|
import { decrypt } from '../lib/crypto'
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
router.use(authMiddleware)
|
router.use(authMiddleware)
|
||||||
@@ -45,7 +46,7 @@ router.get('/plans/:planId/enrollments', async (req: AuthRequest, res: Response,
|
|||||||
employeeId: e.employeeId,
|
employeeId: e.employeeId,
|
||||||
name: e.employee.name,
|
name: e.employee.name,
|
||||||
department: e.employee.department,
|
department: e.employee.department,
|
||||||
idCardMasked: e.employee.idCardNumber ? e.employee.idCardNumber.slice(0, 3) + '****' + e.employee.idCardNumber.slice(-4) : null,
|
idCardNumber: e.employee.idCardNumber ? (() => { try { return e.employee.idCardNumber.includes(':') ? decrypt(e.employee.idCardNumber) : e.employee.idCardNumber } catch { return null } })() : null,
|
||||||
premium: e.premium,
|
premium: e.premium,
|
||||||
effectiveFrom: e.effectiveFrom,
|
effectiveFrom: e.effectiveFrom,
|
||||||
effectiveTo: e.effectiveTo,
|
effectiveTo: e.effectiveTo,
|
||||||
|
|||||||
@@ -190,18 +190,6 @@ router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
|
|||||||
const isResigned = e.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= today)
|
const isResigned = e.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= today)
|
||||||
const isPreHire = !isResigned && e.hireDate > todayEnd
|
const isPreHire = !isResigned && e.hireDate > todayEnd
|
||||||
const dynamicStatus = isResigned ? 'RESIGNED' : (isPreHire ? 'PRE_HIRE' : 'ACTIVE')
|
const dynamicStatus = isResigned ? 'RESIGNED' : (isPreHire ? 'PRE_HIRE' : 'ACTIVE')
|
||||||
// 证件号码脱敏显示
|
|
||||||
let idCardMasked: string | null = null
|
|
||||||
if (e.idCardNumber) {
|
|
||||||
try {
|
|
||||||
const idCard = decrypt(e.idCardNumber)
|
|
||||||
if (idCard.length >= 11) {
|
|
||||||
idCardMasked = idCard.slice(0, 3) + '****' + idCard.slice(-4)
|
|
||||||
} else {
|
|
||||||
idCardMasked = '****'
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
id: e.id,
|
id: e.id,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
@@ -217,7 +205,6 @@ router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
|
|||||||
hireDate: e.hireDate,
|
hireDate: e.hireDate,
|
||||||
gender: e.gender,
|
gender: e.gender,
|
||||||
phone: e.phone,
|
phone: e.phone,
|
||||||
idCardMasked,
|
|
||||||
idCardNumber: safeDecryptStr(e.idCardNumber),
|
idCardNumber: safeDecryptStr(e.idCardNumber),
|
||||||
monthlySalary: safeDecrypt(e.monthlySalary),
|
monthlySalary: safeDecrypt(e.monthlySalary),
|
||||||
baseSalary: e.baseSalary ? safeDecrypt(e.baseSalary) : null,
|
baseSalary: e.baseSalary ? safeDecrypt(e.baseSalary) : null,
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ import { decrypt } from '../lib/crypto'
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import OpenAI from 'openai'
|
import OpenAI from 'openai'
|
||||||
|
|
||||||
|
function decryptIdCard(encrypted: string | null): string | null {
|
||||||
|
if (!encrypted) return null
|
||||||
|
try {
|
||||||
|
if (!encrypted.includes(':')) return encrypted
|
||||||
|
return decrypt(encrypted)
|
||||||
|
} catch { return null }
|
||||||
|
}
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
router.use(authMiddleware)
|
router.use(authMiddleware)
|
||||||
|
|
||||||
@@ -1305,6 +1313,7 @@ router.get('/monthly-changes', async (req: AuthRequest, res: Response, next: Nex
|
|||||||
recordId: r.id,
|
recordId: r.id,
|
||||||
employeeId: r.employeeId,
|
employeeId: r.employeeId,
|
||||||
name: r.employee.name,
|
name: r.employee.name,
|
||||||
|
idCardNumber: decryptIdCard(r.employee.idCardNumber),
|
||||||
department: r.employee.department,
|
department: r.employee.department,
|
||||||
city: r.city,
|
city: r.city,
|
||||||
base: r.base,
|
base: r.base,
|
||||||
@@ -1376,6 +1385,7 @@ router.get('/housing/monthly-changes', async (req: AuthRequest, res: Response, n
|
|||||||
recordId: r.id,
|
recordId: r.id,
|
||||||
employeeId: r.employeeId,
|
employeeId: r.employeeId,
|
||||||
name: r.employee.name,
|
name: r.employee.name,
|
||||||
|
idCardNumber: decryptIdCard(r.employee.idCardNumber),
|
||||||
department: r.employee.department,
|
department: r.employee.department,
|
||||||
city: r.city,
|
city: r.city,
|
||||||
base: r.base,
|
base: r.base,
|
||||||
@@ -1442,7 +1452,8 @@ router.get('/active-declaration', async (req: AuthRequest, res: Response, next:
|
|||||||
recordId: r.id,
|
recordId: r.id,
|
||||||
employeeId: r.employeeId,
|
employeeId: r.employeeId,
|
||||||
name: r.employee.name,
|
name: r.employee.name,
|
||||||
department: r.employee.department,
|
idCardNumber: decryptIdCard((r as any).employee.idCardNumber),
|
||||||
|
department: (r as any).employee.department,
|
||||||
city: r.city,
|
city: r.city,
|
||||||
base: r.base,
|
base: r.base,
|
||||||
startMonth: r.startMonth,
|
startMonth: r.startMonth,
|
||||||
@@ -1505,7 +1516,8 @@ router.get('/housing/active-declaration', async (req: AuthRequest, res: Response
|
|||||||
recordId: r.id,
|
recordId: r.id,
|
||||||
employeeId: r.employeeId,
|
employeeId: r.employeeId,
|
||||||
name: r.employee.name,
|
name: r.employee.name,
|
||||||
department: r.employee.department,
|
idCardNumber: decryptIdCard((r as any).employee.idCardNumber),
|
||||||
|
department: (r as any).employee.department,
|
||||||
city: r.city,
|
city: r.city,
|
||||||
base: r.base,
|
base: r.base,
|
||||||
startMonth: r.startMonth,
|
startMonth: r.startMonth,
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ export default function Roster() {
|
|||||||
formData: {
|
formData: {
|
||||||
employeeId: id,
|
employeeId: id,
|
||||||
employeeName: emp.name,
|
employeeName: emp.name,
|
||||||
idCardNumber: emp.idCardMasked || '',
|
idCardNumber: emp.idCardNumber || '',
|
||||||
position: emp.position || '',
|
position: emp.position || '',
|
||||||
monthlyIncome: emp.monthlySalary ? `¥${emp.monthlySalary}` : '',
|
monthlyIncome: emp.monthlySalary ? `¥${emp.monthlySalary}` : '',
|
||||||
purpose: '批量开具',
|
purpose: '批量开具',
|
||||||
@@ -564,7 +564,7 @@ export default function Roster() {
|
|||||||
const [showColSettings, setShowColSettings] = useState(false)
|
const [showColSettings, setShowColSettings] = useState(false)
|
||||||
|
|
||||||
const filtered = employees?.filter((e: any) =>
|
const filtered = employees?.filter((e: any) =>
|
||||||
!search || e.name.includes(search) || e.department.includes(search) || (e.idCardMasked && e.idCardMasked.includes(search))
|
!search || e.name.includes(search) || e.department.includes(search) || (e.idCardNumber && e.idCardNumber.includes(search))
|
||||||
) || []
|
) || []
|
||||||
|
|
||||||
// 通讯录视图数据
|
// 通讯录视图数据
|
||||||
@@ -800,7 +800,7 @@ export default function Roster() {
|
|||||||
if (e.idCardNumber) {
|
if (e.idCardNumber) {
|
||||||
copyToClipboard(e.idCardNumber, '已复制证件号码')
|
copyToClipboard(e.idCardNumber, '已复制证件号码')
|
||||||
}
|
}
|
||||||
}}>{e.idCardMasked || '—'}</span>
|
}}>{e.idCardNumber || '—'}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
{colVisible('department') && <td className="px-4 py-2.5 text-gray-500">{e.department}</td>}
|
{colVisible('department') && <td className="px-4 py-2.5 text-gray-500">{e.department}</td>}
|
||||||
@@ -1213,7 +1213,7 @@ export default function Roster() {
|
|||||||
formData: {
|
formData: {
|
||||||
employeeId: certEmployee.id,
|
employeeId: certEmployee.id,
|
||||||
employeeName: certEmployee.name,
|
employeeName: certEmployee.name,
|
||||||
idCardNumber: certEmployee.idCardMasked || '',
|
idCardNumber: certEmployee.idCardNumber || '',
|
||||||
position: certEmployee.position || '',
|
position: certEmployee.position || '',
|
||||||
monthlyIncome: certEmployee.monthlySalary ? `¥${certEmployee.monthlySalary}` : '',
|
monthlyIncome: certEmployee.monthlySalary ? `¥${certEmployee.monthlySalary}` : '',
|
||||||
purpose: certPurpose || '通用',
|
purpose: certPurpose || '通用',
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ interface EmployeeSummary {
|
|||||||
phone?: string
|
phone?: string
|
||||||
email?: string
|
email?: string
|
||||||
city?: string
|
city?: string
|
||||||
idCardMasked?: string
|
idCardNumber?: string
|
||||||
probationInfo?: {
|
probationInfo?: {
|
||||||
isProbation: boolean
|
isProbation: boolean
|
||||||
isExpiring: boolean
|
isExpiring: boolean
|
||||||
@@ -175,8 +175,8 @@ export default function EmployeeProfileShell({
|
|||||||
<MapPin className="w-3 h-3" />{profile.city}
|
<MapPin className="w-3 h-3" />{profile.city}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{profile.idCardMasked && (
|
{profile.idCardNumber && (
|
||||||
<span className="font-mono">{profile.idCardMasked}</span>
|
<span className="font-mono">{profile.idCardNumber}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export default function CommercialInsuranceTab() {
|
|||||||
<tr key={e.id || e.employeeId} className="border-b last:border-0 hover:bg-gray-50">
|
<tr key={e.id || e.employeeId} className="border-b last:border-0 hover:bg-gray-50">
|
||||||
<td className="py-2 font-medium">{e.name}</td>
|
<td className="py-2 font-medium">{e.name}</td>
|
||||||
<td className="py-2 text-gray-500">{e.department}</td>
|
<td className="py-2 text-gray-500">{e.department}</td>
|
||||||
<td className="py-2 text-gray-400 font-mono text-xs">{e.idCardMasked || '—'}</td>
|
<td className="py-2 text-gray-400 font-mono text-xs">{e.idCardNumber || '—'}</td>
|
||||||
<td className="py-2 text-right">¥{fmt(e.premium || 0)}</td>
|
<td className="py-2 text-right">¥{fmt(e.premium || 0)}</td>
|
||||||
<td className="py-2 text-gray-500 text-xs">{e.effectiveFrom || '—'}</td>
|
<td className="py-2 text-gray-500 text-xs">{e.effectiveFrom || '—'}</td>
|
||||||
<td className="py-2">
|
<td className="py-2">
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ export function MonthlyRow({ item: i, type, onCorrected }: { item: any; type: 'a
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<tr className="border-b last:border-0 hover:bg-gray-50 cursor-pointer" onClick={() => setExpanded(!expanded)}>
|
<tr className="border-b last:border-0 hover:bg-gray-50 cursor-pointer" onClick={() => setExpanded(!expanded)}>
|
||||||
<td className="py-1.5">{i.name} {d && <span className="text-gray-300 text-xs">{expanded ? '▾' : '▸'}</span>}</td>
|
<td className="py-1.5">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span>{i.name} {d && <span className="text-gray-300 text-xs">{expanded ? '▾' : '▸'}</span>}</span>
|
||||||
|
{i.idCardNumber && <span className="text-gray-400 text-[11px] font-mono">{i.idCardNumber}</span>}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="py-1.5 text-gray-500">{i.department}</td>
|
<td className="py-1.5 text-gray-500">{i.department}</td>
|
||||||
<td className="py-1.5"><span className={`px-2 py-0.5 rounded text-xs ${typeClass}`}>{typeLabel}</span></td>
|
<td className="py-1.5"><span className={`px-2 py-0.5 rounded text-xs ${typeClass}`}>{typeLabel}</span></td>
|
||||||
<td className="py-1.5 text-right">
|
<td className="py-1.5 text-right">
|
||||||
@@ -121,7 +126,12 @@ export function MonthlyHousingRow({ item: i, type, onCorrected }: { item: any; t
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="border-b last:border-0 hover:bg-gray-50">
|
<tr className="border-b last:border-0 hover:bg-gray-50">
|
||||||
<td className="py-1.5">{i.name}</td>
|
<td className="py-1.5">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span>{i.name}</span>
|
||||||
|
{i.idCardNumber && <span className="text-gray-400 text-[11px] font-mono">{i.idCardNumber}</span>}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="py-1.5 text-gray-500">{i.department}</td>
|
<td className="py-1.5 text-gray-500">{i.department}</td>
|
||||||
<td className="py-1.5"><span className={`px-2 py-0.5 rounded text-xs ${typeClass}`}>{typeLabel}</span></td>
|
<td className="py-1.5"><span className={`px-2 py-0.5 rounded text-xs ${typeClass}`}>{typeLabel}</span></td>
|
||||||
<td className="py-1.5 text-right">
|
<td className="py-1.5 text-right">
|
||||||
|
|||||||
Reference in New Issue
Block a user