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,164 @@
|
||||
import prisma from '../lib/prisma'
|
||||
|
||||
/**
|
||||
* 规章制度民主程序服务
|
||||
*/
|
||||
|
||||
const DEMOCRACY_STEPS = [
|
||||
{ step: 1, name: '起草制度', description: 'HR 起草规章制度文本' },
|
||||
{ step: 2, name: '职工讨论', description: '提交职工代表大会或全体职工讨论,提出方案和意见' },
|
||||
{ step: 3, name: '平等协商', description: '与工会或职工代表平等协商确定' },
|
||||
{ step: 4, name: '公示告知', description: '向全体员工公示告知(公告栏/邮件/培训签收等)' },
|
||||
]
|
||||
|
||||
/**
|
||||
* 初始化民主程序进度
|
||||
*/
|
||||
export function initDemocracyProgress() {
|
||||
return {
|
||||
currentStep: 1,
|
||||
steps: DEMOCRACY_STEPS.map(s => ({
|
||||
...s,
|
||||
status: s.step === 1 ? 'IN_PROGRESS' : 'PENDING',
|
||||
date: s.step === 1 ? new Date().toISOString().slice(0, 10) : null,
|
||||
note: null,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新民主程序步骤
|
||||
*/
|
||||
export function updateDemocracyStep(progress: any, targetStep: number, note?: string) {
|
||||
const steps = progress.steps.map((s: any) => {
|
||||
if (s.step < targetStep) {
|
||||
return { ...s, status: 'COMPLETED' }
|
||||
} else if (s.step === targetStep) {
|
||||
return { ...s, status: 'COMPLETED', date: new Date().toISOString().slice(0, 10), note: note || s.note }
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// 设置下一步为进行中
|
||||
if (targetStep < DEMOCRACY_STEPS.length) {
|
||||
const nextIdx = steps.findIndex((s: any) => s.step === targetStep + 1)
|
||||
if (nextIdx >= 0) {
|
||||
steps[nextIdx] = { ...steps[nextIdx], status: 'IN_PROGRESS', date: new Date().toISOString().slice(0, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentStep: targetStep >= DEMOCRACY_STEPS.length ? DEMOCRACY_STEPS.length : targetStep + 1,
|
||||
steps,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建制度文档
|
||||
*/
|
||||
export async function createPolicy(orgId: string, userId: string, data: { title: string; content: string; type?: string }) {
|
||||
return prisma.policyDocument.create({
|
||||
data: {
|
||||
orgId,
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
type: data.type || 'RULES',
|
||||
status: 'DRAFT',
|
||||
democracyProgress: initDemocracyProgress(),
|
||||
createdBy: userId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取制度列表(含阅读签收统计)
|
||||
*/
|
||||
export async function getPolicies(orgId: string, status?: string) {
|
||||
const where: any = { orgId }
|
||||
if (status) where.status = status
|
||||
const [policies, totalEmployees] = await Promise.all([
|
||||
prisma.policyDocument.findMany({
|
||||
where,
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
include: {
|
||||
_count: { select: { readRecords: true } },
|
||||
},
|
||||
}),
|
||||
prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
|
||||
])
|
||||
return policies.map(p => ({
|
||||
...p,
|
||||
readCount: p._count?.readRecords || 0,
|
||||
totalEmployees,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取制度详情
|
||||
*/
|
||||
export async function getPolicyDetail(orgId: string, id: string) {
|
||||
const policy = await prisma.policyDocument.findFirst({ where: { id, orgId } })
|
||||
if (!policy) {
|
||||
throw { code: 'NOT_FOUND', message: '制度文档不存在' }
|
||||
}
|
||||
return policy
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新制度
|
||||
*/
|
||||
export async function updatePolicy(orgId: string, id: string, data: { title?: string; content?: string; type?: string }) {
|
||||
const policy = await prisma.policyDocument.findFirst({ where: { id, orgId } })
|
||||
if (!policy) {
|
||||
throw { code: 'NOT_FOUND', message: '制度文档不存在' }
|
||||
}
|
||||
if (policy.status === 'PUBLISHED') {
|
||||
throw { code: 'CONFLICT', message: '已公示的制度不可编辑' }
|
||||
}
|
||||
|
||||
const updateData: any = {}
|
||||
if (data.title !== undefined) updateData.title = data.title
|
||||
if (data.content !== undefined) updateData.content = data.content
|
||||
if (data.type !== undefined) updateData.type = data.type
|
||||
|
||||
return prisma.policyDocument.update({ where: { id }, data: updateData })
|
||||
}
|
||||
|
||||
/**
|
||||
* 推进民主程序步骤
|
||||
*/
|
||||
export async function advanceDemocracyStep(orgId: string, id: string, step: number, note?: string) {
|
||||
const policy = await prisma.policyDocument.findFirst({ where: { id, orgId } })
|
||||
if (!policy) {
|
||||
throw { code: 'NOT_FOUND', message: '制度文档不存在' }
|
||||
}
|
||||
if (policy.status === 'PUBLISHED') {
|
||||
throw { code: 'CONFLICT', message: '已公示的制度不可修改' }
|
||||
}
|
||||
|
||||
const progress = updateDemocracyStep(policy.democracyProgress, step, note)
|
||||
const status = step >= 4 ? 'PUBLISHED' : step >= 3 ? 'CONSULTING' : step >= 2 ? 'DISCUSSING' : 'DRAFT'
|
||||
|
||||
return prisma.policyDocument.update({
|
||||
where: { id },
|
||||
data: {
|
||||
democracyProgress: progress,
|
||||
status,
|
||||
publishedAt: step >= 4 ? new Date() : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除制度
|
||||
*/
|
||||
export async function deletePolicy(orgId: string, id: string) {
|
||||
const policy = await prisma.policyDocument.findFirst({ where: { id, orgId } })
|
||||
if (!policy) {
|
||||
throw { code: 'NOT_FOUND', message: '制度文档不存在' }
|
||||
}
|
||||
if (policy.status === 'PUBLISHED') {
|
||||
throw { code: 'CONFLICT', message: '已公示的制度不可删除' }
|
||||
}
|
||||
return prisma.policyDocument.delete({ where: { id } })
|
||||
}
|
||||
Reference in New Issue
Block a user