feat: 培训/绩效/违纪员工端签收+管理端签字状态只读化
- 后端: portal新增培训/绩效/违纪列表查看+签收接口,签收时创建证据链 - 管理端: 弹窗去掉HR手动签字勾选,改为只读显示签字状态 - 绩效列表新增签字状态列 - 员工端: 新增MyRecords页面,支持培训签收/拒绝、绩效签字、违纪签字 - 员工端导航新增'我的记录'入口 - EvidenceCategory新增TRAINING/PERFORMANCE类型
This commit is contained in:
@@ -1007,4 +1007,142 @@ router.post('/esign/:id/sign', portalAuth, async (req: any, res, next) => {
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 员工端:培训签收 ==========
|
||||
// 查看自己的培训记录
|
||||
router.get('/training', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const records = await prisma.trainingRecord.findMany({
|
||||
where: { employeeId, orgId },
|
||||
orderBy: { trainingDate: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 培训签收
|
||||
router.post('/training/:id/sign', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const record = await prisma.trainingRecord.findFirst({
|
||||
where: { id: req.params.id, employeeId, orgId, ackStatus: 'PENDING' },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已签收' } })
|
||||
|
||||
const updated = await prisma.trainingRecord.update({
|
||||
where: { id: record.id },
|
||||
data: { ackStatus: 'SIGNED', ackDate: new Date() },
|
||||
})
|
||||
|
||||
await createEvidence({
|
||||
orgId,
|
||||
category: 'TRAINING',
|
||||
refId: record.id,
|
||||
employeeId,
|
||||
events: [{ action: `培训签收:${record.topic}`, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
|
||||
createdBy: employeeId,
|
||||
}).catch(() => {})
|
||||
|
||||
res.json({ success: true, data: updated, message: '签收成功' })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 培训拒绝签收
|
||||
router.post('/training/:id/refuse', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const record = await prisma.trainingRecord.findFirst({
|
||||
where: { id: req.params.id, employeeId, orgId, ackStatus: 'PENDING' },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已处理' } })
|
||||
|
||||
const updated = await prisma.trainingRecord.update({
|
||||
where: { id: record.id },
|
||||
data: { ackStatus: 'REFUSED', ackDate: new Date() },
|
||||
})
|
||||
|
||||
res.json({ success: true, data: updated, message: '已拒绝签收' })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 员工端:绩效签字 ==========
|
||||
// 查看自己的绩效记录
|
||||
router.get('/performance', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const records = await prisma.performanceRecord.findMany({
|
||||
where: { employeeId, orgId },
|
||||
orderBy: { period: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 绩效签字确认
|
||||
router.post('/performance/:id/sign', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const record = await prisma.performanceRecord.findFirst({
|
||||
where: { id: req.params.id, employeeId, orgId, employeeAck: false },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已签字' } })
|
||||
|
||||
const updated = await prisma.performanceRecord.update({
|
||||
where: { id: record.id },
|
||||
data: { employeeAck: true, ackDate: new Date() },
|
||||
})
|
||||
|
||||
await createEvidence({
|
||||
orgId,
|
||||
category: 'PERFORMANCE',
|
||||
refId: record.id,
|
||||
employeeId,
|
||||
events: [{ action: `绩效签字确认:${record.period}`, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
|
||||
createdBy: employeeId,
|
||||
}).catch(() => {})
|
||||
|
||||
res.json({ success: true, data: updated, message: '签字成功' })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 员工端:违纪签字 ==========
|
||||
// 查看自己的违纪记录
|
||||
router.get('/disciplinary', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const records = await prisma.disciplinaryRecord.findMany({
|
||||
where: { employeeId, orgId },
|
||||
orderBy: { violationDate: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 违纪签字确认
|
||||
router.post('/disciplinary/:id/sign', portalAuth, async (req: any, res, next) => {
|
||||
try {
|
||||
const { id: employeeId, orgId } = req.employee
|
||||
const record = await prisma.disciplinaryRecord.findFirst({
|
||||
where: { id: req.params.id, employeeId, orgId, employeeAck: false },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已签字' } })
|
||||
|
||||
const updated = await prisma.disciplinaryRecord.update({
|
||||
where: { id: record.id },
|
||||
data: { employeeAck: true, ackDate: new Date(), ackMethod: 'SIGN' },
|
||||
})
|
||||
|
||||
await createEvidence({
|
||||
orgId,
|
||||
category: 'DISCIPLINARY',
|
||||
refId: record.id,
|
||||
employeeId,
|
||||
events: [{ action: `违纪签字确认:${record.violationType}`, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
|
||||
createdBy: employeeId,
|
||||
}).catch(() => {})
|
||||
|
||||
res.json({ success: true, data: updated, message: '签字成功' })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -12,6 +12,8 @@ export type EvidenceCategory =
|
||||
| 'DISCIPLINARY'
|
||||
| 'ATTENDANCE'
|
||||
| 'TERMINATION'
|
||||
| 'TRAINING'
|
||||
| 'PERFORMANCE'
|
||||
|
||||
/**
|
||||
* 创建证据链记录
|
||||
|
||||
Reference in New Issue
Block a user