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:
freedakgmail
2026-07-30 10:21:22 +08:00
parent 38b8849332
commit 42e0c650a4
24 changed files with 3639 additions and 35 deletions
+39
View File
@@ -5,6 +5,9 @@ import { seedKnowledgeBase, addKnowledge, searchKnowledge, ensureRAGTable, searc
import prisma from '../lib/prisma'
import { z } from 'zod'
import { decrypt } from '../lib/crypto'
import multer from 'multer'
import path from 'path'
import mammoth from 'mammoth'
const router = Router()
@@ -947,6 +950,42 @@ ${expiringContracts.length > 0 ? expiringContracts.join('\n') : '无'}`
}
})
// ========== AI 文件审查上传 ==========
const reviewUpload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 100 * 1024 * 1024 },
fileFilter: (_req, file, cb) => {
const ext = path.extname(file.originalname).toLowerCase()
if (ext !== '.docx' && ext !== '.doc') {
return cb(null, false)
}
cb(null, true)
},
})
router.post('/review/upload', authMiddleware, reviewUpload.single('file'), async (req: AuthRequest, res, next) => {
try {
if (!req.file) {
return res.status(400).json({ success: false, error: { code: 'NO_FILE', message: '请上传 .docx 文件' } })
}
const ext = path.extname(req.file.originalname).toLowerCase()
let text = ''
if (ext === '.docx') {
const result = await mammoth.extractRawText({ buffer: req.file.buffer })
text = result.value
} else {
return res.status(400).json({ success: false, error: { code: 'UNSUPPORTED', message: '暂不支持 .doc 格式,请将文件另存为 .docx 后上传' } })
}
if (text.length > 50000) {
text = text.slice(0, 50000) + '\n\n[文本过长,已截断]'
}
res.json({ success: true, data: { text, fileName: req.file.originalname } })
} catch (err) {
next(err)
}
})
// ========== 人工咨询服务 ==========
router.post('/consultation', authMiddleware, async (req: AuthRequest, res, next) => {
+73
View File
@@ -21,6 +21,7 @@ import {
deleteLeaveRecord,
} from '../services/attendance.service'
import { createEvidence } from '../services/evidence.service'
import prisma from '../lib/prisma'
const router = Router()
@@ -238,4 +239,76 @@ router.delete('/leaves/:id', authMiddleware, async (req: AuthRequest, res: Respo
} catch (err) { next(err) }
})
// ========== 考勤发布 ==========
// 发布考勤表
router.post('/publish', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const { month, title } = req.body
if (!month) {
return res.status(400).json({ success: false, error: { code: 'MISSING_MONTH', message: '请选择月份' } })
}
// 检查是否已发布且未取消
const existing = await (prisma as any).attendancePublish.findFirst({
where: { orgId: req.user!.orgId, month, status: 'PUBLISHED' },
})
if (existing) {
return res.status(400).json({ success: false, error: { code: 'ALREADY_PUBLISHED', message: `${month}月考勤表已发布` } })
}
// 如果有已取消的记录,删除后重新创建
const cancelled = await (prisma as any).attendancePublish.findFirst({
where: { orgId: req.user!.orgId, month, status: 'CANCELLED' },
})
if (cancelled) {
await (prisma as any).attendancePublish.delete({ where: { id: cancelled.id } })
}
const record = await (prisma as any).attendancePublish.create({
data: {
orgId: req.user!.orgId,
month,
title: title || `${month}月考勤表`,
createdBy: req.user!.id,
},
})
res.json({ success: true, data: record })
} catch (err) {
next(err)
}
})
// 发布记录列表
router.get('/publish-records', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const records = await (prisma as any).attendancePublish.findMany({
where: { orgId: req.user!.orgId },
orderBy: { createdAt: 'desc' },
})
res.json({ success: true, data: records })
} catch (err) {
next(err)
}
})
// 取消发布
router.post('/publish/:id/cancel', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const record = await (prisma as any).attendancePublish.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!record) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '发布记录不存在' } })
}
if (record.status !== 'PUBLISHED') {
return res.status(400).json({ success: false, error: { code: 'NOT_PUBLISHED', message: '仅已发布状态可取消' } })
}
const updated = await (prisma as any).attendancePublish.update({
where: { id: record.id },
data: { status: 'CANCELLED' },
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
export default router
@@ -0,0 +1,152 @@
import { Router, Response, NextFunction } from 'express'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import prisma from '../lib/prisma'
import { renderTemplate } from '../services/template.service'
const router = Router()
// 提取变量名
function extractVariables(content: string): string[] {
const matches = content.match(/\{\{(\w+)\}\}/g) || []
return [...new Set(matches.map(m => m.replace(/\{\{|\}\}/g, '')))]
}
// 列表
router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const { category, search } = req.query
const where: any = { orgId: req.user!.orgId, status: 'ACTIVE' }
if (category) where.category = category
if (search) where.name = { contains: String(search) }
const items = await (prisma as any).enterpriseTemplate.findMany({
where,
orderBy: { createdAt: 'desc' },
})
res.json({ success: true, data: items })
} catch (err) {
next(err)
}
})
// 新建
router.post('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const { name, category, description, content } = req.body
if (!name || !category || !content) {
return res.status(400).json({ success: false, error: { code: 'MISSING_FIELDS', message: '名称、分类、内容为必填' } })
}
const variables = extractVariables(content)
const template = await (prisma as any).enterpriseTemplate.create({
data: {
orgId: req.user!.orgId,
name,
category,
description: description || null,
content,
variables,
createdBy: req.user!.id,
},
})
res.json({ success: true, data: template })
} catch (err) {
next(err)
}
})
// 详情
router.get('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const template = await (prisma as any).enterpriseTemplate.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!template) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
}
res.json({ success: true, data: template })
} catch (err) {
next(err)
}
})
// 更新
router.put('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const existing = await (prisma as any).enterpriseTemplate.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!existing) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
}
const { name, category, description, content, status } = req.body
const variables = content ? extractVariables(content) : existing.variables
const updated = await (prisma as any).enterpriseTemplate.update({
where: { id: req.params.id },
data: {
...(name !== undefined && { name }),
...(category !== undefined && { category }),
...(description !== undefined && { description }),
...(content !== undefined && { content, variables }),
...(status !== undefined && { status }),
},
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
// 删除
router.delete('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const existing = await (prisma as any).enterpriseTemplate.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!existing) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
}
await (prisma as any).enterpriseTemplate.delete({ where: { id: req.params.id } })
res.json({ success: true, data: { message: '已删除' } })
} catch (err) {
next(err)
}
})
// 渲染
router.post('/:id/render', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const template = await (prisma as any).enterpriseTemplate.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!template) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
}
const { variables } = req.body as { variables: Record<string, string> }
let content = template.content
for (const [key, value] of Object.entries(variables || {})) {
content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value)
}
res.json({ success: true, data: { content } })
} catch (err) {
next(err)
}
})
// 下载 Word
router.get('/:id/download', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const template = await (prisma as any).enterpriseTemplate.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!template) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
}
const encoded = encodeURIComponent(template.name + '.doc')
res.setHeader('Content-Type', 'application/msword')
res.setHeader('Content-Disposition', `attachment; filename="${encoded}"; filename*=UTF-8''${encoded}`)
res.send(template.content)
} catch (err) {
next(err)
}
})
export default router
+95
View File
@@ -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
+31 -2
View File
@@ -110,7 +110,7 @@ router.get('/payslip', portalAuth, async (req: any, res, next) => {
try {
const month = req.query.month as string || new Date().toISOString().slice(0, 7)
const payslip = await prisma.payslip.findFirst({
where: { employeeId: req.employee.id, orgId: req.employee.orgId, month },
where: { employeeId: req.employee.id, orgId: req.employee.orgId, month, publishStatus: 'PUBLISHED' },
})
if (!payslip) {
return res.json({ success: true, data: null })
@@ -125,7 +125,7 @@ router.get('/payslip', portalAuth, async (req: any, res, next) => {
router.get('/payslip/history', portalAuth, async (req: any, res, next) => {
try {
const payslips = await prisma.payslip.findMany({
where: { employeeId: req.employee.id, orgId: req.employee.orgId },
where: { employeeId: req.employee.id, orgId: req.employee.orgId, publishStatus: 'PUBLISHED' },
orderBy: { month: 'desc' },
take: 6,
})
@@ -611,4 +611,33 @@ router.get('/auto-login', async (req, res, next) => {
}
})
// 员工端查看自己的月度考勤
router.get('/attendance', portalAuth, async (req: any, res, next) => {
try {
const month = req.query.month as string || new Date().toISOString().slice(0, 7)
// 检查该月份是否已发布
const publish = await (prisma as any).attendancePublish.findFirst({
where: { orgId: req.employee.orgId, month, status: 'PUBLISHED' },
})
if (!publish) {
return res.json({ success: true, data: { published: false, records: [] } })
}
// 查询该月考勤记录
const startDate = new Date(`${month}-01`)
const endDate = new Date(startDate)
endDate.setMonth(endDate.getMonth() + 1)
const records = await prisma.attendanceRecord.findMany({
where: {
employeeId: req.employee.id,
orgId: req.employee.orgId,
date: { gte: startDate, lt: endDate },
},
orderBy: { date: 'asc' },
})
res.json({ success: true, data: { published: true, records, title: publish.title } })
} catch (err) {
next(err)
}
})
export default router
+267
View File
@@ -0,0 +1,267 @@
import { Router, Response, NextFunction } from 'express'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import prisma from '../lib/prisma'
import { executeWorkProcess, generateDocument, PROCESS_TYPES, PROCESS_STATUS } from '../services/work-process.service'
const router = Router()
// 创建办理(含草稿)
router.post('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const { type, title, employeeId, formData, status = 'DRAFT', remark } = req.body
if (!type || !PROCESS_TYPES[type]) {
return res.status(400).json({ success: false, error: { code: 'INVALID_TYPE', message: '无效的流程类型' } })
}
const process = await (prisma as any).workProcess.create({
data: {
orgId: req.user!.orgId,
type,
title: title || PROCESS_TYPES[type].label,
employeeId: employeeId || null,
formData: formData || {},
status,
remark: remark || null,
createdBy: req.user!.id,
},
})
res.json({ success: true, data: process })
} catch (err) {
next(err)
}
})
// 列表查询
router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const { type, status, page = '1', pageSize = '20' } = req.query
const where: any = { orgId: req.user!.orgId }
if (type) where.type = type
if (status) where.status = status
const total = await (prisma as any).workProcess.count({ where })
const items = await (prisma as any).workProcess.findMany({
where,
include: { employee: { select: { id: true, name: true, department: true } } },
orderBy: { createdAt: 'desc' },
skip: (Number(page) - 1) * Number(pageSize),
take: Number(pageSize),
})
res.json({ success: true, data: { items, total, page: Number(page), pageSize: Number(pageSize) } })
} catch (err) {
next(err)
}
})
// 详情
router.get('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
include: { employee: { select: { id: true, name: true, department: true, status: true } } },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
res.json({ success: true, data: process })
} catch (err) {
next(err)
}
})
// 更新草稿
router.patch('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const existing = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!existing) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
if (existing.status !== 'DRAFT') {
return res.status(400).json({ success: false, error: { code: 'NOT_DRAFT', message: '仅草稿状态可编辑' } })
}
const { title, employeeId, formData, remark } = req.body
const updated = await (prisma as any).workProcess.update({
where: { id: req.params.id },
data: {
...(title !== undefined && { title }),
...(employeeId !== undefined && { employeeId: employeeId || null }),
...(formData !== undefined && { formData }),
...(remark !== undefined && { remark }),
},
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
// 提交办理
router.post('/:id/submit', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
if (process.status !== 'DRAFT') {
return res.status(400).json({ success: false, error: { code: 'NOT_DRAFT', message: '仅草稿状态可提交' } })
}
// 执行业务联动
let execResult: any = {}
try {
execResult = await executeWorkProcess(process.id, process.type, process.formData, req.user!.orgId, req.user!.id)
} catch (execErr: any) {
return res.status(400).json({ success: false, error: { code: 'EXEC_FAILED', message: `执行失败:${execErr?.message || '未知错误'}` } })
}
// 生成文书
const org = await prisma.organization.findUnique({ where: { id: req.user!.orgId } })
const doc = generateDocument(process.type, process.formData, org?.name || '')
const documents = doc.content ? [doc] : []
const updated = await (prisma as any).workProcess.update({
where: { id: process.id },
data: {
status: 'COMPLETED',
documents: documents.length > 0 ? documents : null,
...(execResult.employeeId && !process.employeeId && { employeeId: execResult.employeeId }),
},
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
// 审批通过
router.post('/:id/approve', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
if (process.status !== 'PENDING_APPROVAL') {
return res.status(400).json({ success: false, error: { code: 'NOT_PENDING', message: '仅待审批状态可审批' } })
}
let execResult: any = {}
try {
execResult = await executeWorkProcess(process.id, process.type, process.formData, req.user!.orgId, req.user!.id)
} catch (execErr: any) {
return res.status(400).json({ success: false, error: { code: 'EXEC_FAILED', message: `执行失败:${execErr?.message || '未知错误'}` } })
}
const org = await prisma.organization.findUnique({ where: { id: req.user!.orgId } })
const doc = generateDocument(process.type, process.formData, org?.name || '')
const documents = doc.content ? [doc] : []
const updated = await (prisma as any).workProcess.update({
where: { id: process.id },
data: {
status: 'COMPLETED',
approverId: req.user!.id,
approvedAt: new Date(),
documents: documents.length > 0 ? documents : null,
...(execResult.employeeId && !process.employeeId && { employeeId: execResult.employeeId }),
},
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
// 驳回
router.post('/:id/reject', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
if (process.status !== 'PENDING_APPROVAL') {
return res.status(400).json({ success: false, error: { code: 'NOT_PENDING', message: '仅待审批状态可驳回' } })
}
const updated = await (prisma as any).workProcess.update({
where: { id: process.id },
data: {
status: 'REJECTED',
approverId: req.user!.id,
approvedAt: new Date(),
remark: req.body.reason || '驳回',
},
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
// 撤销
router.post('/:id/cancel', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
if (['COMPLETED', 'CANCELLED'].includes(process.status)) {
return res.status(400).json({ success: false, error: { code: 'INVALID_STATUS', message: '已完成或已撤销的记录不可撤销' } })
}
const updated = await (prisma as any).workProcess.update({
where: { id: process.id },
data: { status: 'CANCELLED' },
})
res.json({ success: true, data: updated })
} catch (err) {
next(err)
}
})
// 删除草稿
router.delete('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
if (process.status !== 'DRAFT') {
return res.status(400).json({ success: false, error: { code: 'NOT_DRAFT', message: '仅草稿状态可删除' } })
}
await (prisma as any).workProcess.delete({ where: { id: process.id } })
res.json({ success: true, data: { message: '已删除' } })
} catch (err) {
next(err)
}
})
// 预览文书
router.get('/:id/preview', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const process = await (prisma as any).workProcess.findFirst({
where: { id: req.params.id, orgId: req.user!.orgId },
})
if (!process) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '办理记录不存在' } })
}
const org = await prisma.organization.findUnique({ where: { id: req.user!.orgId } })
const doc = generateDocument(process.type, process.formData, org?.name || '')
res.json({ success: true, data: doc })
} catch (err) {
next(err)
}
})
// 获取流程类型列表
router.get('/meta/types', authMiddleware, (_req: AuthRequest, res: Response) => {
res.json({ success: true, data: PROCESS_TYPES })
})
// 获取状态列表
router.get('/meta/statuses', authMiddleware, (_req: AuthRequest, res: Response) => {
res.json({ success: true, data: PROCESS_STATUS })
})
export default router