feat: 退休提醒功能 - 身份证提取出生日期、渐进式退休年龄计算、AI流式获取政策、用户确认生效
This commit is contained in:
@@ -4,6 +4,7 @@ import prisma from '../lib/prisma'
|
||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { requireAdmin } from '../middleware/rbac'
|
||||
import { z } from 'zod'
|
||||
import { checkAndUpdateRetirementPolicy, confirmRetirementPolicy } from '../services/retirement.service'
|
||||
|
||||
const router = Router()
|
||||
router.use(authMiddleware)
|
||||
@@ -27,7 +28,7 @@ router.get('/org', async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const org = await prisma.organization.findUnique({
|
||||
where: { id: req.user!.orgId },
|
||||
select: { id: true, name: true, plan: true, maxEmployees: true, city: true, contactName: true, contactPhone: true, payrollFrequency: true, createdAt: true },
|
||||
select: { id: true, name: true, plan: true, maxEmployees: true, city: true, contactName: true, contactPhone: true, payrollFrequency: true, retirementReminderEnabled: true, createdAt: true },
|
||||
})
|
||||
res.json({ success: true, data: org })
|
||||
} catch (err) {
|
||||
@@ -38,17 +39,18 @@ router.get('/org', async (req: AuthRequest, res, next) => {
|
||||
// 更新企业信息
|
||||
router.put('/org', requireAdmin, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { name, payrollFrequency, city, contactName, contactPhone } = req.body as { name?: string; payrollFrequency?: number; city?: string; contactName?: string; contactPhone?: string }
|
||||
const { name, payrollFrequency, city, contactName, contactPhone, retirementReminderEnabled } = req.body as { name?: string; payrollFrequency?: number; city?: string; contactName?: string; contactPhone?: string; retirementReminderEnabled?: boolean }
|
||||
const updateData: any = {}
|
||||
if (name) updateData.name = name
|
||||
if (payrollFrequency !== undefined) updateData.payrollFrequency = payrollFrequency
|
||||
if (city !== undefined) updateData.city = city
|
||||
if (contactName !== undefined) updateData.contactName = contactName
|
||||
if (contactPhone !== undefined) updateData.contactPhone = contactPhone
|
||||
if (retirementReminderEnabled !== undefined) updateData.retirementReminderEnabled = retirementReminderEnabled
|
||||
const org = await prisma.organization.update({
|
||||
where: { id: req.user!.orgId },
|
||||
data: updateData,
|
||||
select: { id: true, name: true, plan: true, maxEmployees: true, city: true, contactName: true, contactPhone: true, payrollFrequency: true },
|
||||
select: { id: true, name: true, plan: true, maxEmployees: true, city: true, contactName: true, contactPhone: true, payrollFrequency: true, retirementReminderEnabled: true },
|
||||
})
|
||||
res.json({ success: true, data: org })
|
||||
} catch (err) {
|
||||
@@ -190,3 +192,106 @@ router.get('/usage', async (req: AuthRequest, res, next) => {
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
// ========== 退休政策 ==========
|
||||
|
||||
// 获取当前生效的退休政策和待确认政策
|
||||
router.get('/retirement-policy', async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const org = await prisma.organization.findUnique({ where: { id: orgId }, select: { retirementReminderEnabled: true } })
|
||||
|
||||
// 如果开启了退休提醒,懒加载检查是否需要获取新政策
|
||||
if (org?.retirementReminderEnabled) {
|
||||
await checkAndUpdateRetirementPolicy(orgId).catch(() => {})
|
||||
}
|
||||
|
||||
// 查询当前生效的政策
|
||||
const confirmedPolicy = await prisma.retirementPolicy.findFirst({
|
||||
where: { orgId, status: 'CONFIRMED' },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
|
||||
// 查询待确认的政策
|
||||
const pendingPolicy = await prisma.retirementPolicy.findFirst({
|
||||
where: { orgId, status: 'PENDING' },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
enabled: org?.retirementReminderEnabled || false,
|
||||
confirmed: confirmedPolicy,
|
||||
pending: pendingPolicy,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 确认退休政策
|
||||
router.post('/retirement-policy/:id/confirm', requireAdmin, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
await confirmRetirementPolicy(req.user!.orgId, req.params.id)
|
||||
res.json({ success: true, message: '退休政策已确认生效' })
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: err.message || '确认失败' } })
|
||||
}
|
||||
})
|
||||
|
||||
// 手动触发获取最新退休政策(SSE 流式)
|
||||
router.post('/retirement-policy/refresh', requireAdmin, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
res.setHeader('Content-Type', 'text/event-stream')
|
||||
res.setHeader('Cache-Control', 'no-cache')
|
||||
res.setHeader('Connection', 'keep-alive')
|
||||
|
||||
const { fetchRetirementPolicyStream } = await import('../services/ai.service')
|
||||
const crypto = await import('crypto')
|
||||
|
||||
let fullContent = ''
|
||||
for await (const chunk of fetchRetirementPolicyStream()) {
|
||||
fullContent += chunk
|
||||
res.write(`data: ${JSON.stringify({ type: 'chunk', content: chunk })}\n\n`)
|
||||
}
|
||||
|
||||
// 解析 JSON
|
||||
let maleRetireAge = 60
|
||||
let femaleRetireAge = 55
|
||||
let femaleWorkerAge = 50
|
||||
const jsonMatch = fullContent.match(/<JSON>([\s\S]*?)<\/JSON>/)
|
||||
if (jsonMatch) {
|
||||
try {
|
||||
const parsed = JSON.parse(jsonMatch[1].trim())
|
||||
if (parsed.maleRetireAge) maleRetireAge = Number(parsed.maleRetireAge)
|
||||
if (parsed.femaleRetireAge) femaleRetireAge = Number(parsed.femaleRetireAge)
|
||||
if (parsed.femaleWorkerAge) femaleWorkerAge = Number(parsed.femaleWorkerAge)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const contentHash = crypto.createHash('sha256').update(fullContent).digest('hex')
|
||||
const latestPolicy = await prisma.retirementPolicy.findFirst({ where: { orgId }, orderBy: { createdAt: 'desc' } })
|
||||
|
||||
if (latestPolicy && latestPolicy.contentHash === contentHash) {
|
||||
res.write(`data: ${JSON.stringify({ type: 'done', message: '政策内容未变化' })}\n\n`)
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
const version = latestPolicy ? latestPolicy.version + 1 : 1
|
||||
const policy = await prisma.retirementPolicy.create({
|
||||
data: {
|
||||
orgId, version, content: fullContent, contentHash, status: 'PENDING',
|
||||
maleRetireAge, femaleRetireAge, femaleWorkerAge,
|
||||
},
|
||||
})
|
||||
res.write(`data: ${JSON.stringify({ type: 'done', data: policy, message: '已获取新政策,待确认' })}\n\n`)
|
||||
res.end()
|
||||
} catch (err) {
|
||||
res.write(`data: ${JSON.stringify({ type: 'error', message: '获取失败' })}\n\n`)
|
||||
res.end()
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user