feat: 系统优化Phase2 - 面包屑导航/侧边栏间距/制度公示阅读签收/模板变量中文化/通知类型补全
- 面包屑导航组件,集成至TopNav header - 侧边栏菜单分组间距增大,分组间分隔线 - 制度公示员工阅读签收:PolicyReadRecord模型、portal路由、管理端阅读统计 - 修复Policies.tsx民主程序推进bug(字段名/API路径/参数) - 用工文本模板变量名英文转中文显示 - 通知类型TYPE_LABELS补全(RISK_ALERT/SOCIAL_INS/OVERTIME_ALERT/PAYSLIP_READY) - 通知示例数据补充 - h2标题统一为text-sm font-medium - 新增run.md
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import prisma from '../lib/prisma'
|
||||
|
||||
/**
|
||||
* 考勤确认服务
|
||||
*/
|
||||
|
||||
/**
|
||||
* 创建月度考勤确认记录
|
||||
*/
|
||||
export async function createAttendanceConfirmation(orgId: string, userId: string, data: {
|
||||
employeeId: string
|
||||
month: string
|
||||
workDays: number
|
||||
weekdayHours: number
|
||||
weekendHours: number
|
||||
holidayHours: number
|
||||
overtimePay: number
|
||||
}) {
|
||||
const existing = await prisma.attendanceConfirmation.findUnique({
|
||||
where: { orgId_employeeId_month: { orgId, employeeId: data.employeeId, month: data.month } },
|
||||
})
|
||||
if (existing) {
|
||||
throw { code: 'CONFLICT', message: '该月考勤确认记录已存在' }
|
||||
}
|
||||
|
||||
return prisma.attendanceConfirmation.create({
|
||||
data: {
|
||||
orgId,
|
||||
employeeId: data.employeeId,
|
||||
month: data.month,
|
||||
workDays: data.workDays,
|
||||
weekdayHours: data.weekdayHours,
|
||||
weekendHours: data.weekendHours,
|
||||
holidayHours: data.holidayHours,
|
||||
overtimePay: data.overtimePay,
|
||||
createdBy: userId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建月度考勤确认记录
|
||||
*/
|
||||
export async function batchCreateAttendanceConfirmations(orgId: string, userId: string, month: string, items: Array<{
|
||||
employeeId: string
|
||||
workDays: number
|
||||
weekdayHours: number
|
||||
weekendHours: number
|
||||
holidayHours: number
|
||||
overtimePay: number
|
||||
}>) {
|
||||
const results: Array<{ employeeId: string; success: boolean; error?: string }> = []
|
||||
|
||||
for (const item of items) {
|
||||
try {
|
||||
const existing = await prisma.attendanceConfirmation.findUnique({
|
||||
where: { orgId_employeeId_month: { orgId, employeeId: item.employeeId, month } },
|
||||
})
|
||||
if (existing) {
|
||||
// 更新已有记录
|
||||
await prisma.attendanceConfirmation.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
workDays: item.workDays,
|
||||
weekdayHours: item.weekdayHours,
|
||||
weekendHours: item.weekendHours,
|
||||
holidayHours: item.holidayHours,
|
||||
overtimePay: item.overtimePay,
|
||||
status: 'PENDING',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
await prisma.attendanceConfirmation.create({
|
||||
data: {
|
||||
orgId,
|
||||
employeeId: item.employeeId,
|
||||
month,
|
||||
workDays: item.workDays,
|
||||
weekdayHours: item.weekdayHours,
|
||||
weekendHours: item.weekendHours,
|
||||
holidayHours: item.holidayHours,
|
||||
overtimePay: item.overtimePay,
|
||||
createdBy: userId,
|
||||
},
|
||||
})
|
||||
}
|
||||
results.push({ employeeId: item.employeeId, success: true })
|
||||
} catch (err: any) {
|
||||
results.push({ employeeId: item.employeeId, success: false, error: err.message })
|
||||
}
|
||||
}
|
||||
|
||||
return { total: items.length, success: results.filter(r => r.success).length, results }
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月度考勤确认列表
|
||||
*/
|
||||
export async function getAttendanceConfirmations(orgId: string, month: string, status?: string) {
|
||||
const where: any = { orgId, month }
|
||||
if (status) where.status = status
|
||||
|
||||
return prisma.attendanceConfirmation.findMany({
|
||||
where,
|
||||
include: { employee: { select: { id: true, name: true, department: true } } },
|
||||
orderBy: { employee: { name: 'asc' } },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 员工确认考勤(员工端)
|
||||
*/
|
||||
export async function confirmAttendance(orgId: string, employeeId: string, month: string, ip: string, disputeNote?: string) {
|
||||
const record = await prisma.attendanceConfirmation.findUnique({
|
||||
where: { orgId_employeeId_month: { orgId, employeeId, month } },
|
||||
})
|
||||
if (!record) {
|
||||
throw { code: 'NOT_FOUND', message: '考勤确认记录不存在' }
|
||||
}
|
||||
|
||||
if (disputeNote) {
|
||||
// 有异议
|
||||
return prisma.attendanceConfirmation.update({
|
||||
where: { id: record.id },
|
||||
data: { status: 'DISPUTED', disputeNote, confirmIp: ip },
|
||||
})
|
||||
}
|
||||
|
||||
// 确认无误
|
||||
return prisma.attendanceConfirmation.update({
|
||||
where: { id: record.id },
|
||||
data: { status: 'CONFIRMED', confirmedAt: new Date(), confirmIp: ip },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取考勤确认统计
|
||||
*/
|
||||
export async function getAttendanceStats(orgId: string, month: string) {
|
||||
const records = await prisma.attendanceConfirmation.findMany({
|
||||
where: { orgId, month },
|
||||
})
|
||||
|
||||
return {
|
||||
total: records.length,
|
||||
pending: records.filter(r => r.status === 'PENDING').length,
|
||||
confirmed: records.filter(r => r.status === 'CONFIRMED').length,
|
||||
disputed: records.filter(r => r.status === 'DISPUTED').length,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user