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,123 @@
|
||||
import { Router, Response, NextFunction } from 'express'
|
||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { z } from 'zod'
|
||||
import prisma from '../lib/prisma'
|
||||
import {
|
||||
createPolicy, getPolicies, getPolicyDetail, updatePolicy,
|
||||
advanceDemocracyStep, deletePolicy,
|
||||
} from '../services/policy.service'
|
||||
|
||||
const router = Router()
|
||||
|
||||
/** 获取制度列表 */
|
||||
router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const status = req.query.status as string | undefined
|
||||
const policies = await getPolicies(req.user!.orgId, status)
|
||||
res.json({ success: true, data: policies })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/** 获取制度详情 */
|
||||
router.get('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const policy = await getPolicyDetail(req.user!.orgId, req.params.id)
|
||||
res.json({ success: true, data: policy })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/** 创建制度 */
|
||||
router.post('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const schema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
type: z.string().optional(),
|
||||
})
|
||||
const data = schema.parse(req.body)
|
||||
const policy = await createPolicy(req.user!.orgId, req.user!.id, data)
|
||||
res.json({ success: true, data: { id: policy.id } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/** 更新制度 */
|
||||
router.put('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const schema = z.object({
|
||||
title: z.string().optional(),
|
||||
content: z.string().optional(),
|
||||
type: z.string().optional(),
|
||||
})
|
||||
const data = schema.parse(req.body)
|
||||
await updatePolicy(req.user!.orgId, req.params.id, data)
|
||||
res.json({ success: true })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/** 推进民主程序步骤 */
|
||||
router.post('/:id/advance-step', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const schema = z.object({ step: z.number().int().min(1).max(4), note: z.string().optional() })
|
||||
const { step, note } = schema.parse(req.body)
|
||||
await advanceDemocracyStep(req.user!.orgId, req.params.id, step, note)
|
||||
res.json({ success: true })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/** 删除制度 */
|
||||
router.delete('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
await deletePolicy(req.user!.orgId, req.params.id)
|
||||
res.json({ success: true })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/** 获取制度的阅读签收统计 */
|
||||
router.get('/:id/read-stats', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const policy = await prisma.policyDocument.findFirst({ where: { id: req.params.id, orgId }, select: { id: true } })
|
||||
if (!policy) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '制度不存在' } })
|
||||
}
|
||||
const [totalEmployees, readRecords] = await Promise.all([
|
||||
prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
|
||||
prisma.policyReadRecord.findMany({
|
||||
where: { policyId: req.params.id, orgId },
|
||||
include: { employee: { select: { id: true, name: true, department: true } } },
|
||||
orderBy: { readAt: 'desc' },
|
||||
}),
|
||||
])
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
total: totalEmployees,
|
||||
readCount: readRecords.length,
|
||||
unreadCount: totalEmployees - readRecords.length,
|
||||
records: readRecords.map(r => ({
|
||||
employeeId: r.employeeId,
|
||||
employeeName: r.employee.name,
|
||||
department: r.employee.department,
|
||||
readAt: r.readAt.toISOString(),
|
||||
ip: r.ip,
|
||||
})),
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user