feat: 员工端新增个人资料页面,数据同步到花名册
- 后端新增 GET/PUT /portal/profile 接口,员工可查看和编辑个人资料 - 员工可编辑:紧急联系人、住址、银行卡、学历、参保城市 - 只读字段:姓名、性别、手机号、证件号码、部门、岗位、入职日期 - 数据直接写入 Employee 表(花名册),HR 管理端可见 - 更新操作记录证据链(PROFILE_UPDATE) - 前端新增 MyProfile.tsx 页面,PortalNav 添加导航入口
This commit is contained in:
@@ -10,6 +10,7 @@ import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { portalLoginSchema, portalSendCodeSchema, portalVerifyCodeSchema, onboardingSchema, contractConfirmSchema, contractSendCodeSchema } from '../schemas/portal.schema'
|
||||
import { setCode, getCode, deleteCode, updateCode, checkRateLimit } from '../lib/codeStore'
|
||||
import { createEvidence, appendEvidence } from '../services/evidence.service'
|
||||
import { encrypt, decrypt, sha256 } from '../lib/crypto'
|
||||
|
||||
const router = Router()
|
||||
|
||||
@@ -870,6 +871,91 @@ router.get('/onboarding/progress', portalAuth, async (req: any, res, next) => {
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 员工端:个人资料查看与编辑 ==========
|
||||
// 查看个人资料
|
||||
router.get('/profile', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const emp = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
|
||||
if (!emp) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
|
||||
|
||||
// 解密敏感字段
|
||||
let idCardNo = ''
|
||||
let bankAccount = ''
|
||||
try { if (emp.idCardNumber) idCardNo = decrypt(emp.idCardNumber) } catch {}
|
||||
try { if (emp.bankAccount) bankAccount = decrypt(emp.bankAccount) } catch {}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
name: emp.name,
|
||||
gender: emp.gender || '',
|
||||
phone: emp.phone || '',
|
||||
idCardNo,
|
||||
idType: emp.idType || 'ID_CARD',
|
||||
department: emp.department,
|
||||
position: emp.position || '',
|
||||
hireDate: emp.hireDate ? emp.hireDate.toISOString().slice(0, 10) : '',
|
||||
emergencyContact: emp.emergencyContact || '',
|
||||
emergencyPhone: emp.emergencyPhone || '',
|
||||
address: emp.address || '',
|
||||
bankAccount,
|
||||
bankName: emp.bankName || '',
|
||||
education: emp.education || '',
|
||||
city: emp.city || '',
|
||||
birthDate: emp.birthDate ? emp.birthDate.toISOString().slice(0, 10) : '',
|
||||
},
|
||||
})
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 更新个人资料(员工可编辑字段:紧急联系人、地址、银行卡、学历等)
|
||||
router.put('/profile', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const emp = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
|
||||
if (!emp) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
|
||||
|
||||
const {
|
||||
emergencyContact, emergencyPhone, address,
|
||||
bankAccount, bankName, education, city,
|
||||
} = req.body
|
||||
|
||||
// 构建更新数据(仅允许员工自行编辑的字段)
|
||||
const updateData: any = {}
|
||||
if (emergencyContact !== undefined) updateData.emergencyContact = emergencyContact || null
|
||||
if (emergencyPhone !== undefined) updateData.emergencyPhone = emergencyPhone || null
|
||||
if (address !== undefined) updateData.address = address || null
|
||||
if (bankName !== undefined) updateData.bankName = bankName || null
|
||||
if (education !== undefined) updateData.education = education || null
|
||||
if (city !== undefined) updateData.city = city || null
|
||||
// 银行卡号需要加密
|
||||
if (bankAccount !== undefined && bankAccount) {
|
||||
updateData.bankAccount = encrypt(bankAccount)
|
||||
}
|
||||
|
||||
await prisma.employee.update({ where: { id: employeeId }, data: updateData })
|
||||
|
||||
// 记录证据链
|
||||
await createEvidence({
|
||||
orgId,
|
||||
category: 'PROFILE_UPDATE',
|
||||
refId: employeeId,
|
||||
employeeId,
|
||||
events: [{
|
||||
action: '员工更新个人资料',
|
||||
timestamp: new Date().toISOString(),
|
||||
ip: req.ip,
|
||||
userAgent: req.headers['user-agent'],
|
||||
location: `更新字段:${Object.keys(updateData).join(', ')}`,
|
||||
}],
|
||||
createdBy: employeeId,
|
||||
}).catch(() => {})
|
||||
|
||||
res.json({ success: true, data: { message: '资料更新成功' } })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 员工端:离职申请 ==========
|
||||
// 提交离职申请
|
||||
router.post('/resignation/submit', portalAuth, async (req: any, res, next) => {
|
||||
|
||||
@@ -45,6 +45,7 @@ export type EvidenceCategory =
|
||||
| 'TERMINATION'
|
||||
| 'TRAINING'
|
||||
| 'PERFORMANCE'
|
||||
| 'PROFILE_UPDATE'
|
||||
|
||||
/**
|
||||
* 创建证据链记录
|
||||
|
||||
Reference in New Issue
Block a user