c328172e7d
- 商业保险:从社公商保中拆出为独立页面,侧边栏新增「福利保障」分组 - 员工福利:新建完整模块(方案管理+批量参保+员工汇总),Prisma模型+后端路由+前端页面 - 电子签署:搭建易签宝对接框架(ESignRecord模型+创建/查询/取消/回调接口+前端签署管理页面) - 侧边栏新增「福利保障」分组:商业保险、员工福利、电子签署 - Prisma schema 新增6个模型:CommercialInsurancePlan/Enrollment, EmployeeBenefitPlan/Enrollment, ESignRecord
150 lines
4.9 KiB
TypeScript
150 lines
4.9 KiB
TypeScript
import { Router, Response, NextFunction } from 'express'
|
|
import { z } from 'zod'
|
|
import prisma from '../lib/prisma'
|
|
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
|
|
|
const router = Router()
|
|
router.use(authMiddleware)
|
|
|
|
const createSignSchema = z.object({
|
|
contractId: z.string().optional(),
|
|
employeeId: z.string().min(1),
|
|
documentTitle: z.string().min(1),
|
|
documentContent: z.string().optional(),
|
|
remark: z.string().optional(),
|
|
})
|
|
|
|
// 签署记录列表
|
|
router.get('/', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
try {
|
|
const status = req.query.status as string | undefined
|
|
const records = await prisma.eSignRecord.findMany({
|
|
where: {
|
|
orgId: req.user!.orgId,
|
|
...(status && { status }),
|
|
},
|
|
include: {
|
|
employee: { select: { id: true, name: true, department: true, phone: true } },
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
})
|
|
res.json({ success: true, data: records })
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
// 发起签署
|
|
// TODO: 对接易签宝 API — 此处为框架预留
|
|
// 1. 调用易签宝创建签署流程
|
|
// 2. 获取签署链接
|
|
// 3. 保存 flowId 和 signUrl
|
|
router.post('/create', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
try {
|
|
const data = createSignSchema.parse(req.body)
|
|
|
|
// 预留:调用易签宝 API 创建签署流程
|
|
// const esignResult = await esignApi.createFlow({
|
|
// title: data.documentTitle,
|
|
// signerPhone: employee.phone,
|
|
// signerName: employee.name,
|
|
// content: data.documentContent,
|
|
// })
|
|
// const flowId = esignResult.flowId
|
|
// const signUrl = esignResult.signUrl
|
|
|
|
const record = await prisma.eSignRecord.create({
|
|
data: {
|
|
orgId: req.user!.orgId,
|
|
contractId: data.contractId || null,
|
|
employeeId: data.employeeId,
|
|
documentTitle: data.documentTitle,
|
|
documentContent: data.documentContent || null,
|
|
// flowId: esignResult.flowId, // TODO: 易签宝对接后启用
|
|
// signUrl: esignResult.signUrl, // TODO: 易签宝对接后启用
|
|
status: 'PENDING',
|
|
initiatedBy: req.user!.id,
|
|
createdBy: req.user!.id,
|
|
remark: data.remark || null,
|
|
expiredAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天过期
|
|
},
|
|
})
|
|
|
|
res.json({
|
|
success: true,
|
|
data: record,
|
|
message: '签署记录已创建。对接易签宝API后,将自动生成签署链接并发送给员工。',
|
|
})
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
// 查询签署状态
|
|
// TODO: 对接易签宝 API — 查询流程状态并同步
|
|
router.get('/:id/status', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
try {
|
|
const record = await prisma.eSignRecord.findFirst({
|
|
where: { id: req.params.id, orgId: req.user!.orgId },
|
|
})
|
|
if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在' } })
|
|
|
|
// 预留:调用易签宝 API 查询流程状态
|
|
// const esignStatus = await esignApi.getFlowStatus(record.flowId)
|
|
// if (esignStatus !== record.status) {
|
|
// await prisma.eSignRecord.update({ where: { id: record.id }, data: { status: esignStatus } })
|
|
// }
|
|
|
|
res.json({ success: true, data: record })
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
// 易签宝回调接口(无需认证)
|
|
router.post('/callback', async (req, res: Response, next: NextFunction) => {
|
|
try {
|
|
const { flowId, status, signedPdfUrl, ...rest } = req.body
|
|
|
|
// TODO: 验证易签宝回调签名
|
|
// if (!verifyEsignCallback(req.headers, req.body)) {
|
|
// return res.status(401).json({ success: false, error: { message: '无效回调' } })
|
|
// }
|
|
|
|
if (flowId) {
|
|
const record = await prisma.eSignRecord.findFirst({ where: { flowId } })
|
|
if (record) {
|
|
await prisma.eSignRecord.update({
|
|
where: { id: record.id },
|
|
data: {
|
|
status: status || 'COMPLETED',
|
|
signedPdfUrl: signedPdfUrl || null,
|
|
completedAt: status === 'COMPLETED' ? new Date() : null,
|
|
callbackData: rest as any,
|
|
},
|
|
})
|
|
|
|
// 如果关联了合同,更新合同的签署方式和电子合同URL
|
|
if (record.contractId && status === 'COMPLETED') {
|
|
await prisma.laborContract.update({
|
|
where: { id: record.contractId },
|
|
data: {
|
|
signMethod: 'ELECTRONIC',
|
|
electronicContractUrl: signedPdfUrl || null,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
res.json({ success: true })
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
// 取消签署
|
|
router.post('/:id/cancel', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
try {
|
|
const record = await prisma.eSignRecord.update({
|
|
where: { id: req.params.id, orgId: req.user!.orgId },
|
|
data: { status: 'CANCELLED' },
|
|
})
|
|
res.json({ success: true, data: record })
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
export default router
|