feat: 实现20260730优化方案全部功能
- AI文件审查:.docx上传提取文本,支持多种文档类型 - 用工办理工作流:WorkProcess页面+后端API,支持入职/续签/终止等流程 - 企业自建文本库:Templates页面Tab切换,企业模板CRUD+渲染+下载Word - 考勤发布:Attendance发布/取消发布按钮,员工端MyAttendance页面 - 工资条发布:Money发布/定时发送按钮+弹窗,portal端publishStatus过滤 - 合同到期弹窗:Dashboard合同到期预警可点击打开弹窗,支持续签/终止操作 - Prisma schema新增WorkProcess/EnterpriseTemplate/AttendancePublish模型 - 前后端编译验证全部通过
This commit is contained in:
@@ -870,4 +870,99 @@ router.get('/batches/:batchId/pre-check', async (req: AuthRequest, res: Response
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 工资条发布 ==========
|
||||
|
||||
// 发布工资条(将批次内所有工资条标记为 PUBLISHED)
|
||||
router.post('/batches/:batchId/publish', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { batchId } = req.params
|
||||
const orgId = req.user!.orgId
|
||||
const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
|
||||
if (!batch) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
|
||||
}
|
||||
// 查找该批次关联的所有工资条(通过 BatchEntry 关联的 employeeId + month)
|
||||
const entries = await prisma.batchEntry.findMany({
|
||||
where: { batchId, orgId },
|
||||
select: { employeeId: true },
|
||||
})
|
||||
const employeeIds = entries.map(e => e.employeeId)
|
||||
if (employeeIds.length === 0) {
|
||||
return res.status(400).json({ success: false, error: { code: 'EMPTY', message: '批次内无员工' } })
|
||||
}
|
||||
// 更新对应月份的工资条
|
||||
const result = await prisma.payslip.updateMany({
|
||||
where: { orgId, employeeId: { in: employeeIds }, month: batch.month },
|
||||
data: { publishStatus: 'PUBLISHED', publishedAt: new Date() },
|
||||
})
|
||||
res.json({ success: true, data: { published: result.count, month: batch.month } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 定时发送工资条
|
||||
router.post('/batches/:batchId/schedule', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { batchId } = req.params
|
||||
const { scheduledAt } = req.body
|
||||
if (!scheduledAt) {
|
||||
return res.status(400).json({ success: false, error: { code: 'MISSING_DATE', message: '请选择发送时间' } })
|
||||
}
|
||||
const orgId = req.user!.orgId
|
||||
const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
|
||||
if (!batch) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
|
||||
}
|
||||
const entries = await prisma.batchEntry.findMany({
|
||||
where: { batchId, orgId },
|
||||
select: { employeeId: true },
|
||||
})
|
||||
const employeeIds = entries.map(e => e.employeeId)
|
||||
if (employeeIds.length === 0) {
|
||||
return res.status(400).json({ success: false, error: { code: 'EMPTY', message: '批次内无员工' } })
|
||||
}
|
||||
const result = await prisma.payslip.updateMany({
|
||||
where: { orgId, employeeId: { in: employeeIds }, month: batch.month },
|
||||
data: { publishStatus: 'SCHEDULED', scheduledAt: new Date(scheduledAt) },
|
||||
})
|
||||
res.json({ success: true, data: { scheduled: result.count, scheduledAt } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 定时发送记录
|
||||
router.get('/schedule-records', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const records = await prisma.payslip.findMany({
|
||||
where: { orgId: req.user!.orgId, publishStatus: 'SCHEDULED' },
|
||||
include: { employee: { select: { name: true, department: true } } },
|
||||
orderBy: { scheduledAt: 'asc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 取消定时发送
|
||||
router.post('/schedule/:id/cancel', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const payslip = await prisma.payslip.findFirst({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId, publishStatus: 'SCHEDULED' },
|
||||
})
|
||||
if (!payslip) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '定时发送记录不存在' } })
|
||||
}
|
||||
const updated = await prisma.payslip.update({
|
||||
where: { id: payslip.id },
|
||||
data: { publishStatus: 'UNPUBLISHED', scheduledAt: null },
|
||||
})
|
||||
res.json({ success: true, data: updated })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user