init: AI HR Compliance Assistant
This commit is contained in:
@@ -0,0 +1,533 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express'
|
||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { auditLog } from '../middleware/auditLog'
|
||||
import prisma from '../lib/prisma'
|
||||
import { decrypt } from '../lib/crypto'
|
||||
|
||||
const router = Router()
|
||||
|
||||
function safeDecrypt(encrypted: string): number {
|
||||
try {
|
||||
if (!encrypted || !encrypted.includes(':')) return Number(encrypted) || 0
|
||||
return Number(decrypt(encrypted))
|
||||
} catch {
|
||||
return Number(encrypted) || 0
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 花名册聚合 API ==========
|
||||
|
||||
// 花名册列表(含汇总信息)
|
||||
router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const employees = await prisma.employee.findMany({
|
||||
where: { orgId: req.user!.orgId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
include: {
|
||||
contracts: { orderBy: { createdAt: 'desc' }, take: 1 },
|
||||
_count: {
|
||||
select: {
|
||||
disciplinaryRecords: true,
|
||||
attendanceRecords: true,
|
||||
trainingRecords: true,
|
||||
performanceRecords: true,
|
||||
payslips: true,
|
||||
overtimeRecords: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = employees.map((e) => ({
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
department: e.department,
|
||||
status: e.status,
|
||||
hireDate: e.hireDate,
|
||||
gender: e.gender,
|
||||
phone: e.phone,
|
||||
monthlySalary: safeDecrypt(e.monthlySalary),
|
||||
latestContract: e.contracts[0] || null,
|
||||
counts: e._count,
|
||||
}))
|
||||
res.json({ success: true, data: result })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 员工完整档案(花名册详情)
|
||||
router.get('/:id/profile', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const employee = await prisma.employee.findFirst({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId },
|
||||
include: {
|
||||
contracts: { orderBy: { createdAt: 'desc' } },
|
||||
payslips: { orderBy: { month: 'desc' } },
|
||||
overtimeRecords: { orderBy: { month: 'desc' } },
|
||||
disciplinaryRecords: { orderBy: { violationDate: 'desc' } },
|
||||
attendanceRecords: { orderBy: { date: 'desc' }, take: 90 },
|
||||
trainingRecords: { orderBy: { trainingDate: 'desc' } },
|
||||
performanceRecords: { orderBy: { period: 'desc' } },
|
||||
terminations: { orderBy: { createdAt: 'desc' } },
|
||||
attachments: true,
|
||||
},
|
||||
})
|
||||
if (!employee) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
|
||||
}
|
||||
const { monthlySalary, ...rest } = employee
|
||||
res.json({
|
||||
success: true,
|
||||
data: { ...rest, monthlySalary: safeDecrypt(monthlySalary) },
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 仲裁证据链导出
|
||||
router.get('/:id/evidence-chain', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const employee = await prisma.employee.findFirst({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId },
|
||||
include: {
|
||||
contracts: { orderBy: { createdAt: 'desc' } },
|
||||
payslips: { orderBy: { month: 'desc' } },
|
||||
overtimeRecords: { orderBy: { month: 'desc' } },
|
||||
disciplinaryRecords: { orderBy: { violationDate: 'desc' } },
|
||||
attendanceRecords: { orderBy: { date: 'desc' } },
|
||||
trainingRecords: { orderBy: { trainingDate: 'desc' } },
|
||||
performanceRecords: { orderBy: { period: 'desc' } },
|
||||
terminations: true,
|
||||
},
|
||||
})
|
||||
if (!employee) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
|
||||
}
|
||||
|
||||
const evidence: any[] = []
|
||||
const empName = employee.name
|
||||
const empDept = employee.department
|
||||
const hireDate = employee.hireDate.toISOString().slice(0, 10)
|
||||
|
||||
// 1. 劳动关系证据
|
||||
evidence.push({
|
||||
category: '劳动关系',
|
||||
title: '入职登记',
|
||||
date: hireDate,
|
||||
description: `${empName}于${hireDate}入职${empDept},建立劳动关系。`,
|
||||
evidenceType: 'EMPLOYMENT',
|
||||
})
|
||||
employee.contracts.forEach((c) => {
|
||||
evidence.push({
|
||||
category: '劳动关系',
|
||||
title: `劳动合同(${c.contractType === 'FIXED' ? '固定期限' : c.contractType === 'UNFIXED' ? '无固定期限' : '未签订'})`,
|
||||
date: c.signDate ? c.signDate.toISOString().slice(0, 10) : c.startDate.toISOString().slice(0, 10),
|
||||
description: `合同期限:${c.startDate.toISOString().slice(0, 10)} 至 ${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'},试用期${c.probationMonths}个月,试用期工资¥${c.probationSalary}。`,
|
||||
evidenceType: 'CONTRACT',
|
||||
signed: !!c.signDate,
|
||||
})
|
||||
})
|
||||
|
||||
// 2. 薪酬证据
|
||||
employee.payslips.forEach((p) => {
|
||||
evidence.push({
|
||||
category: '薪酬发放',
|
||||
title: `${p.month}月工资条`,
|
||||
date: p.month,
|
||||
description: `基本工资¥${p.baseSalary.toFixed(2)},加班费¥${p.overtimePay.toFixed(2)},津贴¥${p.allowance.toFixed(2)},扣款¥${p.deduction.toFixed(2)},应发合计¥${p.totalPay.toFixed(2)}。${p.confirmedAt ? '员工已确认。' : '员工未确认。'}`,
|
||||
evidenceType: 'PAYSLIP',
|
||||
confirmed: !!p.confirmedAt,
|
||||
})
|
||||
})
|
||||
employee.overtimeRecords.forEach((o) => {
|
||||
if (o.totalPay > 0) {
|
||||
evidence.push({
|
||||
category: '薪酬发放',
|
||||
title: `${o.month}月加班费记录`,
|
||||
date: o.month,
|
||||
description: `工作日加班${o.weekdayHours}h,休息日加班${o.weekendHours}h,节假日加班${o.holidayHours}h,加班费合计¥${o.totalPay.toFixed(2)}。`,
|
||||
evidenceType: 'OVERTIME',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 3. 考勤证据
|
||||
const abnormalAttendance = employee.attendanceRecords.filter((a) => a.status !== 'NORMAL')
|
||||
abnormalAttendance.forEach((a) => {
|
||||
const statusMap: Record<string, string> = { LATE: '迟到', EARLY_LEAVE: '早退', ABSENT: '旷工', LEAVE: '请假', BUSINESS_TRIP: '出差' }
|
||||
evidence.push({
|
||||
category: '考勤记录',
|
||||
title: `${a.date.toISOString().slice(0, 10)} 考勤异常`,
|
||||
date: a.date.toISOString().slice(0, 10),
|
||||
description: `状态:${statusMap[a.status] || a.status}${a.lateMinutes ? `,迟到${a.lateMinutes}分钟` : ''}${a.earlyMinutes ? `,早退${a.earlyMinutes}分钟` : ''}。${a.remark || ''}`,
|
||||
evidenceType: 'ATTENDANCE',
|
||||
})
|
||||
})
|
||||
|
||||
// 4. 违纪证据
|
||||
employee.disciplinaryRecords.forEach((d) => {
|
||||
const typeMap: Record<string, string> = { LATE: '迟到', ABSENT: '旷工', INSUBORDINATION: '不服从管理', MISCONDUCT: '违纪', VIOLATE_POLICY: '违反规章制度', OTHER: '其他' }
|
||||
const actionMap: Record<string, string> = { ORAL_WARNING: '口头警告', WRITTEN_WARNING: '书面警告', DEDUCTION: '扣款', DEMOTION: '降职', TERMINATION: '解除劳动合同' }
|
||||
evidence.push({
|
||||
category: '违纪处理',
|
||||
title: `${d.violationDate.toISOString().slice(0, 10)} ${typeMap[d.violationType] || d.violationType}`,
|
||||
date: d.violationDate.toISOString().slice(0, 10),
|
||||
description: `违纪事实:${d.description}。处理结果:${actionMap[d.action] || d.action}。${d.employeeAck ? `员工已签字确认(${d.ackDate ? d.ackDate.toISOString().slice(0, 10) : ''})。` : '员工未签字。'}${d.witness ? `见证人:${d.witness}。` : ''}`,
|
||||
evidenceType: 'DISCIPLINARY',
|
||||
acknowledged: d.employeeAck,
|
||||
})
|
||||
})
|
||||
|
||||
// 5. 培训签收证据
|
||||
employee.trainingRecords.forEach((t) => {
|
||||
const ackMap: Record<string, string> = { PENDING: '待签收', SIGNED: '已签收', REFUSED: '拒绝签收' }
|
||||
evidence.push({
|
||||
category: '培训签收',
|
||||
title: `${t.trainingDate.toISOString().slice(0, 10)} ${t.topic}`,
|
||||
date: t.trainingDate.toISOString().slice(0, 10),
|
||||
description: `培训主题:${t.topic}。时长:${t.duration}小时。${t.content ? `内容:${t.content}。` : ''}签收状态:${ackMap[t.ackStatus] || t.ackStatus}。`,
|
||||
evidenceType: 'TRAINING',
|
||||
acknowledged: t.ackStatus === 'SIGNED',
|
||||
})
|
||||
})
|
||||
|
||||
// 6. 绩效证据
|
||||
employee.performanceRecords.forEach((p) => {
|
||||
const resultMap: Record<string, string> = { EXCELLENT: '优秀', QUALIFIED: '合格', NEED_IMPROVE: '需改进', UNQUALIFIED: '不胜任' }
|
||||
evidence.push({
|
||||
category: '绩效考核',
|
||||
title: `${p.period} 绩效考核`,
|
||||
date: p.period,
|
||||
description: `得分:${p.score},等级:${p.grade},结果:${resultMap[p.result] || p.result}。${p.summary ? `评语:${p.summary}。` : ''}${p.improvementPlan ? `改进计划:${p.improvementPlan}。` : ''}${p.employeeAck ? '员工已签字确认。' : '员工未签字。'}`,
|
||||
evidenceType: 'PERFORMANCE',
|
||||
acknowledged: p.employeeAck,
|
||||
})
|
||||
})
|
||||
|
||||
// 7. 解聘证据
|
||||
employee.terminations.forEach((t) => {
|
||||
const reasonMap: Record<string, string> = { NEGOTIATED: '协商解除', FAULT: '员工过错', NONFAULT: '非过错解除', LAYOFF: '经济性裁员', EXPIRED: '合同到期' }
|
||||
evidence.push({
|
||||
category: '解聘记录',
|
||||
title: `${t.terminationDate.toISOString().slice(0, 10)} 解聘记录`,
|
||||
date: t.terminationDate.toISOString().slice(0, 10),
|
||||
description: `解聘原因:${reasonMap[t.reason] || t.reason}。经济补偿金:¥${t.compensation.toFixed(2)}。${t.remark || ''}`,
|
||||
evidenceType: 'TERMINATION',
|
||||
})
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
employee: {
|
||||
name: empName,
|
||||
department: empDept,
|
||||
hireDate,
|
||||
status: employee.status,
|
||||
gender: employee.gender,
|
||||
phone: employee.phone,
|
||||
},
|
||||
evidence,
|
||||
summary: {
|
||||
total: evidence.length,
|
||||
signed: evidence.filter((e) => e.acknowledged === true).length,
|
||||
unsigned: evidence.filter((e) => e.acknowledged === false).length,
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 违纪记录 CRUD ==========
|
||||
|
||||
router.get('/:employeeId/disciplinary', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const records = await prisma.disciplinaryRecord.findMany({
|
||||
where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
|
||||
orderBy: { violationDate: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.post('/:employeeId/disciplinary', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { violationDate, violationType, description, severity, action, actionDetail, employeeAck, ackDate, ackMethod, witness, attachmentUrl } = req.body
|
||||
const record = await prisma.disciplinaryRecord.create({
|
||||
data: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: req.params.employeeId,
|
||||
violationDate: new Date(violationDate),
|
||||
violationType,
|
||||
description,
|
||||
severity: severity || 'WARNING',
|
||||
action: action || 'ORAL_WARNING',
|
||||
actionDetail,
|
||||
employeeAck: employeeAck || false,
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
ackMethod,
|
||||
witness,
|
||||
attachmentUrl,
|
||||
createdBy: req.user!.id,
|
||||
},
|
||||
})
|
||||
await auditLog(req, 'CREATE', 'DISCIPLINARY', record.id, { employeeId: req.params.employeeId })
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('/:employeeId/disciplinary/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { violationDate, violationType, description, severity, action, actionDetail, employeeAck, ackDate, ackMethod, witness, attachmentUrl } = req.body
|
||||
const record = await prisma.disciplinaryRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
const updated = await prisma.disciplinaryRecord.update({
|
||||
where: { id: req.params.recordId },
|
||||
data: {
|
||||
violationDate: violationDate ? new Date(violationDate) : undefined,
|
||||
violationType,
|
||||
description,
|
||||
severity,
|
||||
action,
|
||||
actionDetail,
|
||||
employeeAck,
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
ackMethod,
|
||||
witness,
|
||||
attachmentUrl,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: updated })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.delete('/:employeeId/disciplinary/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const record = await prisma.disciplinaryRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
await prisma.disciplinaryRecord.delete({ where: { id: req.params.recordId } })
|
||||
res.json({ success: true })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 考勤记录 CRUD ==========
|
||||
|
||||
router.get('/:employeeId/attendance', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const records = await prisma.attendanceRecord.findMany({
|
||||
where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
|
||||
orderBy: { date: 'desc' },
|
||||
take: 90,
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.post('/:employeeId/attendance', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { date, checkInTime, checkOutTime, status, lateMinutes, earlyMinutes, workHours, overtimeHours, remark } = req.body
|
||||
const record = await prisma.attendanceRecord.upsert({
|
||||
where: { employeeId_date: { employeeId: req.params.employeeId, date: new Date(date) } },
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: req.params.employeeId,
|
||||
date: new Date(date),
|
||||
checkInTime,
|
||||
checkOutTime,
|
||||
status: status || 'NORMAL',
|
||||
lateMinutes: lateMinutes || 0,
|
||||
earlyMinutes: earlyMinutes || 0,
|
||||
workHours: workHours || 0,
|
||||
overtimeHours: overtimeHours || 0,
|
||||
remark,
|
||||
createdBy: req.user!.id,
|
||||
},
|
||||
update: {
|
||||
checkInTime,
|
||||
checkOutTime,
|
||||
status,
|
||||
lateMinutes,
|
||||
earlyMinutes,
|
||||
workHours,
|
||||
overtimeHours,
|
||||
remark,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.delete('/:employeeId/attendance/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const record = await prisma.attendanceRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
await prisma.attendanceRecord.delete({ where: { id: req.params.recordId } })
|
||||
res.json({ success: true })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 培训签收记录 CRUD ==========
|
||||
|
||||
router.get('/:employeeId/training', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const records = await prisma.trainingRecord.findMany({
|
||||
where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
|
||||
orderBy: { trainingDate: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.post('/:employeeId/training', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { trainingDate, topic, content, trainer, duration, ackStatus, ackDate, attachmentUrl, remark } = req.body
|
||||
const record = await prisma.trainingRecord.create({
|
||||
data: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: req.params.employeeId,
|
||||
trainingDate: new Date(trainingDate),
|
||||
topic,
|
||||
content,
|
||||
trainer,
|
||||
duration: duration || 0,
|
||||
ackStatus: ackStatus || 'PENDING',
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
attachmentUrl,
|
||||
remark,
|
||||
createdBy: req.user!.id,
|
||||
},
|
||||
})
|
||||
await auditLog(req, 'CREATE', 'TRAINING', record.id, { employeeId: req.params.employeeId })
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('/:employeeId/training/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { trainingDate, topic, content, trainer, duration, ackStatus, ackDate, attachmentUrl, remark } = req.body
|
||||
const record = await prisma.trainingRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
const updated = await prisma.trainingRecord.update({
|
||||
where: { id: req.params.recordId },
|
||||
data: {
|
||||
trainingDate: trainingDate ? new Date(trainingDate) : undefined,
|
||||
topic,
|
||||
content,
|
||||
trainer,
|
||||
duration,
|
||||
ackStatus,
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
attachmentUrl,
|
||||
remark,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: updated })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.delete('/:employeeId/training/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const record = await prisma.trainingRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
await prisma.trainingRecord.delete({ where: { id: req.params.recordId } })
|
||||
res.json({ success: true })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// ========== 绩效记录 CRUD ==========
|
||||
|
||||
router.get('/:employeeId/performance', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const records = await prisma.performanceRecord.findMany({
|
||||
where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
|
||||
orderBy: { period: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.post('/:employeeId/performance', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { period, score, grade, result, summary, improvementPlan, employeeAck, ackDate, reviewer } = req.body
|
||||
const record = await prisma.performanceRecord.upsert({
|
||||
where: { employeeId_period: { employeeId: req.params.employeeId, period } },
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: req.params.employeeId,
|
||||
period,
|
||||
score: score || 0,
|
||||
grade: grade || 'B',
|
||||
result: result || 'QUALIFIED',
|
||||
summary,
|
||||
improvementPlan,
|
||||
employeeAck: employeeAck || false,
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
reviewer,
|
||||
createdBy: req.user!.id,
|
||||
},
|
||||
update: {
|
||||
score,
|
||||
grade,
|
||||
result,
|
||||
summary,
|
||||
improvementPlan,
|
||||
employeeAck,
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
reviewer,
|
||||
},
|
||||
})
|
||||
await auditLog(req, 'CREATE', 'PERFORMANCE', record.id, { employeeId: req.params.employeeId })
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('/:employeeId/performance/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { period, score, grade, result, summary, improvementPlan, employeeAck, ackDate, reviewer } = req.body
|
||||
const record = await prisma.performanceRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
const updated = await prisma.performanceRecord.update({
|
||||
where: { id: req.params.recordId },
|
||||
data: {
|
||||
period,
|
||||
score,
|
||||
grade,
|
||||
result,
|
||||
summary,
|
||||
improvementPlan,
|
||||
employeeAck,
|
||||
ackDate: ackDate ? new Date(ackDate) : null,
|
||||
reviewer,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: updated })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.delete('/:employeeId/performance/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const record = await prisma.performanceRecord.findFirst({
|
||||
where: { id: req.params.recordId, orgId: req.user!.orgId },
|
||||
})
|
||||
if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
|
||||
await prisma.performanceRecord.delete({ where: { id: req.params.recordId } })
|
||||
res.json({ success: true })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user