diff --git a/backend/src/routes/employee.routes.ts b/backend/src/routes/employee.routes.ts index 9ef590f..ce2f5b6 100644 --- a/backend/src/routes/employee.routes.ts +++ b/backend/src/routes/employee.routes.ts @@ -56,10 +56,14 @@ router.get('/all-lite', authMiddleware, async (req: AuthRequest, res, next) => { status: { in: status }, ...(department && { department }), }, - select: { id: true, name: true, department: true, position: true, gender: true, status: true }, + select: { id: true, name: true, department: true, position: true, gender: true, status: true, idCardNumber: true }, orderBy: { name: 'asc' }, }) - res.json({ success: true, data: employees }) + const data = employees.map((e: any) => ({ + ...e, + idCardNumber: (() => { try { const v = e.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), + })) + res.json({ success: true, data }) } catch (err) { next(err) } @@ -301,6 +305,7 @@ router.post('/contracts/preview-renew', authMiddleware, async (req: AuthRequest, contractId: contract.id, employeeId: contract.employeeId, employeeName: employee.name, + employeeIdCardNumber: (() => { try { const v = employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), department: employee.department, currentContractType: contract.contractType, renewalCount, diff --git a/backend/src/routes/esign.routes.ts b/backend/src/routes/esign.routes.ts index 9805088..6030934 100644 --- a/backend/src/routes/esign.routes.ts +++ b/backend/src/routes/esign.routes.ts @@ -20,6 +20,7 @@ import { authMiddleware, AuthRequest } from '../middleware/auth' import { auditLog } from '../middleware/auditLog' import { createEvidence, appendEvidence } from '../services/evidence.service' import { renderTemplate, getTemplateById } from '../services/template.service' +import { decrypt } from '../lib/crypto' const router = Router() router.use(authMiddleware) @@ -69,11 +70,15 @@ router.get('/', async (req: AuthRequest, res: Response, next: NextFunction) => { ...(scene && { scene }), }, include: { - employee: { select: { id: true, name: true, department: true, phone: true } }, + employee: { select: { id: true, name: true, department: true, phone: true, idCardNumber: true } }, }, orderBy: { createdAt: 'desc' }, }) - res.json({ success: true, data: records }) + const recordsWithIdCard = records.map((r: any) => ({ + ...r, + employee: r.employee ? { ...r.employee, idCardNumber: (() => { try { const v = r.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })() } : r.employee, + })) + res.json({ success: true, data: recordsWithIdCard }) } catch (err) { next(err) } }) @@ -98,7 +103,7 @@ router.get('/pending', async (req: AuthRequest, res: Response, next: NextFunctio status: { in: ['PENDING', 'SIGNING'] }, }, include: { - employee: { select: { id: true, name: true, department: true, phone: true, status: true } }, + employee: { select: { id: true, name: true, department: true, phone: true, status: true, idCardNumber: true } }, }, orderBy: { createdAt: 'desc' }, }) @@ -111,7 +116,7 @@ router.get('/pending', async (req: AuthRequest, res: Response, next: NextFunctio employee: { status: 'ACTIVE' }, }, include: { - employee: { select: { id: true, name: true, department: true, phone: true, status: true } }, + employee: { select: { id: true, name: true, department: true, phone: true, status: true, idCardNumber: true } }, }, orderBy: { createdAt: 'desc' }, }) @@ -122,17 +127,19 @@ router.get('/pending', async (req: AuthRequest, res: Response, next: NextFunctio name: string department: string | null phone: string | null + idCardNumber: string | null pendingItems: any[] }>() // 辅助函数:添加员工到 map - const ensureEmployee = (emp: { id: string; name: string; department: string | null; phone: string | null }) => { + const ensureEmployee = (emp: { id: string; name: string; department: string | null; phone: string | null; idCardNumber?: string | null }) => { if (!employeeMap.has(emp.id)) { employeeMap.set(emp.id, { employeeId: emp.id, name: emp.name, department: emp.department, phone: emp.phone, + idCardNumber: (() => { try { const v = emp.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), pendingItems: [], }) } @@ -287,7 +294,7 @@ router.post('/remind', async (req: AuthRequest, res: Response, next: NextFunctio } const employee = await prisma.employee.findFirst({ where: { id: employeeId, orgId: req.user!.orgId, status: 'ACTIVE' }, - select: { id: true, name: true, phone: true, orgId: true }, + select: { id: true, name: true, phone: true, orgId: true, idCardNumber: true }, }) if (!employee) { return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在或已离职' } }) @@ -300,7 +307,7 @@ router.post('/remind', async (req: AuthRequest, res: Response, next: NextFunctio ) const url = `${process.env.PORTAL_BASE_URL || ''}/portal/auto-login?token=${token}&redirect=/portal/esign` await auditLog(req, 'REMIND', 'ESIGN', employeeId, { employeeName: employee.name }) - res.json({ success: true, data: { url, token, employeeName: employee.name, phone: employee.phone } }) + res.json({ success: true, data: { url, token, employeeName: employee.name, employeeIdCardNumber: (() => { try { const v = employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), phone: employee.phone } }) } catch (err) { next(err) } }) @@ -413,11 +420,15 @@ router.get('/:id', async (req: AuthRequest, res: Response, next: NextFunction) = const record = await prisma.eSignRecord.findFirst({ where: { id: req.params.id, orgId: req.user!.orgId }, include: { - employee: { select: { id: true, name: true, department: true, phone: true } }, + employee: { select: { id: true, name: true, department: true, phone: true, idCardNumber: true } }, }, }) if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在' } }) - res.json({ success: true, data: record }) + const recordWithIdCard = { + ...record, + employee: record.employee ? { ...record.employee, idCardNumber: (() => { try { const v = record.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })() } : record.employee, + } + res.json({ success: true, data: recordWithIdCard }) } catch (err) { next(err) } }) diff --git a/backend/src/routes/special-status.routes.ts b/backend/src/routes/special-status.routes.ts index ffce424..8a6a80c 100644 --- a/backend/src/routes/special-status.routes.ts +++ b/backend/src/routes/special-status.routes.ts @@ -5,6 +5,7 @@ import { Router, Response, NextFunction } from 'express' import { authMiddleware, AuthRequest } from '../middleware/auth' import prisma from '../lib/prisma' +import { decrypt } from '../lib/crypto' import { parsePagination } from '../lib/pagination' import { createSpecialStatus, @@ -49,7 +50,7 @@ router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: Ne take: pageSize, include: { employee: { - select: { id: true, name: true, department: true, phone: true, gender: true, status: true }, + select: { id: true, name: true, department: true, phone: true, gender: true, status: true, idCardNumber: true }, }, }, }), @@ -60,6 +61,7 @@ router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: Ne const listWithAlert = list.map((item: any) => ({ ...item, alertLevel: getAlertLevel(item.reminderDate, item.status), + employee: item.employee ? { ...item.employee, idCardNumber: (() => { try { const v = item.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })() } : item.employee, })) res.json({ @@ -80,7 +82,7 @@ router.get('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: where: { id: req.params.id, orgId: req.user!.orgId }, include: { employee: { - select: { id: true, name: true, department: true, phone: true, gender: true, hireDate: true }, + select: { id: true, name: true, department: true, phone: true, gender: true, hireDate: true, idCardNumber: true }, }, }, }) diff --git a/backend/src/services/risk.service.ts b/backend/src/services/risk.service.ts index d2fa739..f792449 100644 --- a/backend/src/services/risk.service.ts +++ b/backend/src/services/risk.service.ts @@ -1072,6 +1072,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: string title: string employeeName?: string + employeeIdCardNumber?: string | null employeeId?: string actionUrl?: string priority: 'high' | 'medium' | 'low' @@ -1091,6 +1092,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: 'CONTRACT_EXPIRY', title: `${c.employee.name} 合同到期`, employeeName: c.employee.name, + employeeIdCardNumber: (() => { try { const v = c.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), employeeId: c.employeeId, actionUrl: '/contracts', priority: 'high', @@ -1114,6 +1116,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: 'PROBATION_END', title: `${c.employee.name} 试用期到期`, employeeName: c.employee.name, + employeeIdCardNumber: (() => { try { const v = c.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), employeeId: c.employeeId, actionUrl: '/roster', priority: 'medium', @@ -1136,6 +1139,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: 'TERMINATION', title: `${t.employee.name} 离职/解聘日`, employeeName: t.employee.name, + employeeIdCardNumber: (() => { try { const v = t.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), employeeId: t.employeeId, actionUrl: '/termination', priority: 'high', @@ -1162,6 +1166,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: 'ANNIVERSARY', title: `${emp.name} 入职${years}周年`, employeeName: emp.name, + employeeIdCardNumber: (() => { try { const v = emp.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), employeeId: emp.id, actionUrl: '/roster', priority: 'low', @@ -1189,6 +1194,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: 'RISK_DEADLINE', title: `${r.title} 处理截止日`, employeeName: r.employee?.name, + employeeIdCardNumber: (() => { try { const v = r.employee?.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), employeeId: r.employeeId || undefined, actionUrl: r.actionUrl || '/', priority: r.level === 'HIGH' ? 'high' : 'medium', @@ -1217,6 +1223,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: 'RETIREMENT', title: `${emp.name} 达到法定退休年龄`, employeeName: emp.name, + employeeIdCardNumber: (() => { try { const v = emp.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), employeeId: emp.id, actionUrl: '/roster', priority: 'high', @@ -1251,7 +1258,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { orgId, date: { gte: monthStart, lte: monthEnd }, }, - include: { employee: { select: { name: true } } }, + include: { employee: { select: { name: true, idCardNumber: true } } }, }) for (const ev of customEvents) { events.push({ @@ -1259,6 +1266,7 @@ export async function getMonthlyCalendar(orgId: string, month: string) { type: ev.type, title: ev.title + (ev.employee ? ` — ${ev.employee.name}` : ''), employeeName: ev.employee?.name, + employeeIdCardNumber: (() => { try { const v = ev.employee?.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), actionUrl: '/dashboard', priority: ev.priority as 'high' | 'medium' | 'low', }) diff --git a/backend/src/services/termination.service.ts b/backend/src/services/termination.service.ts index 3845dfc..45c48bf 100644 --- a/backend/src/services/termination.service.ts +++ b/backend/src/services/termination.service.ts @@ -1,6 +1,7 @@ import prisma from '../lib/prisma' import { RiskAssessment, TerminationReason } from '@prisma/client' import { autoCreateEsignRecord } from './esign.service' +import { decrypt } from '../lib/crypto' function dateToMonth(date: Date): string { const y = date.getFullYear() @@ -400,6 +401,7 @@ export function calculateCompensation(hireDate: Date, leaveDate: Date, monthlyWa export interface BatchTerminatePreview { employeeId: string employeeName: string + employeeIdCardNumber: string | null department: string reason: string terminationDate: string @@ -423,6 +425,7 @@ export async function batchTerminatePreview( results.push({ employeeId: item.employeeId, employeeName: '(未找到)', + employeeIdCardNumber: null, department: '', reason: item.reason, terminationDate: item.terminationDate, @@ -437,6 +440,7 @@ export async function batchTerminatePreview( results.push({ employeeId: item.employeeId, employeeName: employee.name, + employeeIdCardNumber: (() => { try { const v = employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(), department: employee.department, reason: item.reason, terminationDate: item.terminationDate, diff --git a/frontend/src/pages/Calendar.tsx b/frontend/src/pages/Calendar.tsx index c535e99..009fcd5 100644 --- a/frontend/src/pages/Calendar.tsx +++ b/frontend/src/pages/Calendar.tsx @@ -377,6 +377,7 @@ export default function Calendar() {
{ev.title} {ev.employeeName && — {ev.employeeName}} + {ev.employeeIdCardNumber && {ev.employeeIdCardNumber}}
{ev.type === 'CONTRACT_EXPIRY' && ev.employeeName ? ( diff --git a/frontend/src/pages/ESign.tsx b/frontend/src/pages/ESign.tsx index e151f1c..5f00bd4 100644 --- a/frontend/src/pages/ESign.tsx +++ b/frontend/src/pages/ESign.tsx @@ -263,7 +263,12 @@ export default function ESign() { {r.remark &&
{r.remark}
} - {r.employee?.name || '—'} + +
+ {r.employee?.name || '—'} + {r.employee?.idCardNumber && {r.employee.idCardNumber}} +
+ {r.employee?.department || '—'} @@ -334,7 +339,7 @@ export default function ESign() { > {rosterData.map((emp: any) => ( - + ))} @@ -496,7 +501,7 @@ function PaperSignModal({ onClose, onSuccess, initialEmployeeId, initialScene, i > {rosterData.map((emp: any) => ( - + ))} @@ -654,7 +659,7 @@ function ESignDetail({ id, onBack }: { id: string; onBack: () => void }) { {(SIGN_METHOD_CONFIG[detail.signMethod] || SIGN_METHOD_CONFIG.ELECTRONIC).label} -
签署人:{detail.employee?.name} · {detail.employee?.department}
+
签署人:{detail.employee?.name} · {detail.employee?.department}{detail.employee?.idCardNumber && {detail.employee.idCardNumber}}
{statusCfg.icon}{statusCfg.label} @@ -868,6 +873,7 @@ function PendingTab({ pendingList, loading, onRemind, remindLoading, remindData, {emp.name} + {emp.idCardNumber && {emp.idCardNumber}} {emp.department || '—'} @@ -993,7 +999,7 @@ function PendingTab({ pendingList, loading, onRemind, remindLoading, remindData,

- 员工 {remindData.employeeName} 扫码后自动登录员工端签署页面 + 员工 {remindData.employeeName}{remindData.employeeIdCardNumber && {remindData.employeeIdCardNumber}} 扫码后自动登录员工端签署页面

链接 24 小时内有效

diff --git a/frontend/src/pages/Roster.tsx b/frontend/src/pages/Roster.tsx index 0e68d24..7679667 100644 --- a/frontend/src/pages/Roster.tsx +++ b/frontend/src/pages/Roster.tsx @@ -1378,7 +1378,7 @@ export default function Roster() { {previewData.results.map((r: any) => (
- {r.employeeName} + {r.employeeName}{r.employeeIdCardNumber ? {r.employeeIdCardNumber} : null} {r.department}
@@ -1477,7 +1477,7 @@ export default function Roster() { {terminatePreviewData.results.map((r: any) => (
0 ? 'bg-red-50 border border-red-200' : 'bg-gray-50'}`}>
- {r.employeeName} + {r.employeeName}{r.employeeIdCardNumber ? {r.employeeIdCardNumber} : null} {r.department}
diff --git a/frontend/src/pages/SpecialStatus.tsx b/frontend/src/pages/SpecialStatus.tsx index 0021779..d25efd8 100644 --- a/frontend/src/pages/SpecialStatus.tsx +++ b/frontend/src/pages/SpecialStatus.tsx @@ -38,7 +38,7 @@ interface SpecialStatus { reminderDate: string | null alertLevel: 'red' | 'yellow' | 'green' employee: { - id: string; name: string; department: string; phone: string; gender: string; status: string + id: string; name: string; department: string; phone: string; gender: string; status: string; idCardNumber?: string | null } } @@ -367,6 +367,7 @@ export default function SpecialStatus() { {/* 员工信息 */}
{item.employee.name} + {item.employee.idCardNumber && {item.employee.idCardNumber}} {item.employee.department} {item.employee.status === 'RESIGNED' && ( 已离职 @@ -458,7 +459,7 @@ export default function SpecialStatus() { }} disabled={!!editing}> {employees.map((emp) => ( - + ))}
@@ -620,7 +621,7 @@ export default function SpecialStatus() {

- 确定要删除 {deleteTarget.employee.name}{TYPE_LABELS[deleteTarget.type]} 记录吗? + 确定要删除 {deleteTarget.employee.name}{deleteTarget.employee.idCardNumber ? `(${deleteTarget.employee.idCardNumber})` : ''}{TYPE_LABELS[deleteTarget.type]} 记录吗?

diff --git a/frontend/src/pages/money/OvertimeTab.tsx b/frontend/src/pages/money/OvertimeTab.tsx index f6d5658..53246b1 100644 --- a/frontend/src/pages/money/OvertimeTab.tsx +++ b/frontend/src/pages/money/OvertimeTab.tsx @@ -323,7 +323,7 @@ export function OvertimeCalculator() { {previewData.map((d, i) => ( - {d.employeeName} + {d.employeeName}{d.idCardNumber ? {d.idCardNumber} : ''} {d.department} {d.weekdayHours} {d.weekendHours} diff --git a/frontend/src/pages/social-insurance/CommercialInsuranceTab.tsx b/frontend/src/pages/social-insurance/CommercialInsuranceTab.tsx index 51174fb..f8d4326 100644 --- a/frontend/src/pages/social-insurance/CommercialInsuranceTab.tsx +++ b/frontend/src/pages/social-insurance/CommercialInsuranceTab.tsx @@ -267,7 +267,7 @@ export default function CommercialInsuranceTab() { else setEnrollEmployeeIds(enrollEmployeeIds.filter((id) => id !== emp.id)) }} /> - {emp.name} + {emp.name}{emp.idCardNumber ? {emp.idCardNumber} : ''} {emp.department} 在职