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(), scene: z.string().optional(), }) // 签署记录列表 router.get('/', async (req: AuthRequest, res: Response, next: NextFunction) => { try { const status = req.query.status as string | undefined const scene = req.query.scene as string | undefined const records = await prisma.eSignRecord.findMany({ where: { orgId: req.user!.orgId, ...(status && { status }), ...(scene && { scene }), }, 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, scene: data.scene || 'CONTRACT', 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