/** * 审批流路由 * 提供审批流配置和实例管理 */ import { Router } from 'express' import { authMiddleware, AuthRequest } from '../middleware/auth' import prisma from '../lib/prisma' import { z } from 'zod' import { processApproval, cancelApproval } from '../services/approval.service' const router = Router() const flowSchema = z.object({ type: z.string(), // LEAVE / TERMINATION / SALARY_CHANGE / OTHER name: z.string().min(1), enabled: z.boolean().default(true), steps: z.array(z.object({ step: z.number().int().min(1).max(3), approverType: z.enum(['SUPERVISOR', 'DEPT_HEAD', 'PERSON']), approverId: z.string().optional(), name: z.string(), })).min(1, '至少一个审批步骤').max(3, '最多三个审批步骤'), }) /** 获取审批流配置列表 */ router.get('/', authMiddleware, async (req: AuthRequest, res, next) => { try { const flows = await prisma.approvalFlow.findMany({ where: { orgId: req.user!.orgId! }, orderBy: { createdAt: 'asc' }, }) res.json({ success: true, data: flows }) } catch (err) { next(err) } }) /** 创建/更新审批流配置(upsert by type) */ router.post('/', authMiddleware, async (req: AuthRequest, res, next) => { try { const data = flowSchema.parse(req.body) const existing = await prisma.approvalFlow.findFirst({ where: { orgId: req.user!.orgId!, type: data.type }, }) let flow if (existing) { flow = await prisma.approvalFlow.update({ where: { id: existing.id }, data: { ...data, createdBy: req.user!.id }, }) } else { flow = await prisma.approvalFlow.create({ data: { ...data, orgId: req.user!.orgId!, createdBy: req.user!.id, }, }) } res.json({ success: true, data: flow }) } catch (err) { next(err) } }) /** 获取待我审批的实例 */ router.get('/pending', authMiddleware, async (req: AuthRequest, res, next) => { try { const instances = await prisma.approvalInstance.findMany({ where: { orgId: req.user!.orgId!, status: 'PENDING' }, orderBy: { createdAt: 'desc' }, include: { employee: { select: { id: true, name: true, department: true } } }, }) res.json({ success: true, data: instances }) } catch (err) { next(err) } }) /** 处理审批 */ router.post('/:id/process', authMiddleware, async (req: AuthRequest, res, next) => { try { const { id } = req.params const { result, comment } = req.body as { result: 'APPROVED' | 'REJECTED'; comment?: string } if (!result || !['APPROVED', 'REJECTED'].includes(result)) { throw { code: 'VALIDATION_ERROR', message: 'result 必须为 APPROVED 或 REJECTED' } } const approverName = req.user!.id || '审批人' const outcome = await processApproval(req.user!.orgId!, id, req.user!.id, approverName, result, comment) res.json({ success: true, data: outcome }) } catch (err) { next(err) } }) /** 取消审批 */ router.post('/:id/cancel', authMiddleware, async (req: AuthRequest, res, next) => { try { const { id } = req.params await cancelApproval(req.user!.orgId!, id) res.json({ success: true }) } catch (err) { next(err) } }) export default router