feat: 20260815 系统优化 - 全部31项问题修复(P0×6+P1×14+P2×9+P3×2)

P0紧急修复(6项):
- 草稿保存完整恢复所有字段(含socialAvgWage)
- 补偿金批次从compensationBreakdown读取
- 违法解除风险确认UI
- 合同结束日期前后校验(前后端双保险)

P1高优先级(14项):
- 离职日期联动社保/公积金截止月(15号规则)
- 合规检查+工作交接改为软阻断(生成待办)
- 补偿月数(N/N+1/2N/自定义)+计算基数(近12月/合同/自定义)
- 解聘并入花名册操作栏(类型选择跳转向导)
- 合同续签开始日期自动推导(原合同结束日+1天)
- 年龄合规筛查(童工阻断/未成年工/退休警告)
- 编辑入职日期后状态联动(待入职↔在职)
- 转正移植到花名册操作栏+薪资回写
- 男职工无法选择三期

P2体验优化(9项):
- "劳动合同"调整为"用工关系"
- 费用结算新增剩余年假折算(300%日工资)
- 身份证号全域改为"证件号码"(前后端18个文件)
- 手机号查重
- 开具证明+合同续签移植到花名册操作栏
- 批量转正+批量开具证明
- 去掉用工办理模块

P3规划(2项):
- 组织架构+审批流(Department/Position/ApprovalFlow/ApprovalInstance)
- 客服工作台(Ticket/ChatSession+SUPPORT角色)

新增模型: Department/Position/ApprovalFlow/ApprovalInstance/Ticket/TicketMessage/ChatSession/ChatMessage
新增字段: Employee.departmentId/supervisorId
新增角色: SUPPORT

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-15 12:37:27 +08:00
parent 8cfdd566af
commit e1b5ae9aab
46 changed files with 3455 additions and 206 deletions
+101
View File
@@ -0,0 +1,101 @@
/**
* 部门管理路由
* 提供部门的增删改查(树形结构)
*/
import { Router } from 'express'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import prisma from '../lib/prisma'
import { z } from 'zod'
const router = Router()
const createDeptSchema = z.object({
name: z.string().min(1, '部门名称必填'),
parentId: z.string().nullable().optional(),
sortOrder: z.number().int().default(0),
description: z.string().max(200).optional(),
})
const updateDeptSchema = createDeptSchema.partial()
/** 获取部门树 */
router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
try {
const departments = await prisma.department.findMany({
where: { orgId: req.user!.orgId! },
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'asc' }],
include: { _count: { select: { employees: true, positions: true } } },
})
res.json({ success: true, data: departments })
} catch (err) {
next(err)
}
})
/** 创建部门 */
router.post('/', authMiddleware, async (req: AuthRequest, res, next) => {
try {
const data = createDeptSchema.parse(req.body)
let level = 0
if (data.parentId) {
const parent = await prisma.department.findFirst({ where: { id: data.parentId, orgId: req.user!.orgId! } })
if (!parent) throw { code: 'NOT_FOUND', message: '父部门不存在' }
level = parent.level + 1
}
const dept = await prisma.department.create({
data: {
...data,
level,
orgId: req.user!.orgId!,
createdBy: req.user!.id,
},
})
res.json({ success: true, data: dept })
} catch (err) {
next(err)
}
})
/** 更新部门 */
router.put('/:id', authMiddleware, async (req: AuthRequest, res, next) => {
try {
const { id } = req.params
const data = updateDeptSchema.parse(req.body)
// 防止循环引用
if (data.parentId === id) throw { code: 'VALIDATION_ERROR', message: '不能将自身设为父部门' }
let level: number | undefined
if (data.parentId) {
const parent = await prisma.department.findFirst({ where: { id: data.parentId, orgId: req.user!.orgId! } })
if (!parent) throw { code: 'NOT_FOUND', message: '父部门不存在' }
level = parent.level + 1
} else if (data.parentId === null) {
level = 0
}
const dept = await prisma.department.update({
where: { id },
data: { ...data, level },
})
res.json({ success: true, data: dept })
} catch (err) {
next(err)
}
})
/** 删除部门 */
router.delete('/:id', authMiddleware, async (req: AuthRequest, res, next) => {
try {
const { id } = req.params
// 检查是否有子部门
const children = await prisma.department.findFirst({ where: { parentId: id, orgId: req.user!.orgId! } })
if (children) throw { code: 'VALIDATION_ERROR', message: '请先删除子部门' }
// 检查是否有关联员工
const employees = await prisma.employee.findFirst({ where: { departmentId: id, orgId: req.user!.orgId! } })
if (employees) throw { code: 'VALIDATION_ERROR', message: '该部门下仍有员工,无法删除' }
await prisma.department.delete({ where: { id } })
res.json({ success: true })
} catch (err) {
next(err)
}
})
export default router