refactor: WorkProcess 业务逻辑复用原有 service

- HIRE 调用 createEmployee(含社保/公积金/薪资/部门记录)
- RENEW/CUSTOM_CONTRACT 调用 addContract(含重复检查、试用期验证、风险检测)
- TERMINATE/RESCIND 调用 createTerminationDraft + executeTermination(含风险评级、合规清单、社保截止)
- CONFIRM 增加 probationEndDate 更新和 runRiskDetection
- FLEXIBLE 调用 createEmployee + addContract
- 前端 TERMINATE/RESCIND 表单增加 reason 和 compensation 字段
- 原有 Roster 入口保持不变,两条路径共用同一套 service
This commit is contained in:
freedakgmail
2026-07-30 10:27:47 +08:00
parent 42e0c650a4
commit cea7eb07c4
2 changed files with 93 additions and 93 deletions
+89 -93
View File
@@ -1,6 +1,9 @@
import prisma from '../lib/prisma' import prisma from '../lib/prisma'
import { encrypt } from '../lib/crypto' import { encrypt } from '../lib/crypto'
import crypto from 'crypto' import crypto from 'crypto'
import { createDraft as createTerminationDraft, executeTermination, createResignation } from './termination.service'
import { createEmployee, addContract, batchRenew } from './contract.service'
import { runRiskDetection } from './risk.service'
// 13类流程定义 // 13类流程定义
export const PROCESS_TYPES: Record<string, { label: string; description: string; icon: string }> = { export const PROCESS_TYPES: Record<string, { label: string; description: string; icon: string }> = {
@@ -33,38 +36,25 @@ export const PROCESS_STATUS: Record<string, { label: string; color: string }> =
export async function executeWorkProcess(processId: string, type: string, formData: any, orgId: string, userId: string) { export async function executeWorkProcess(processId: string, type: string, formData: any, orgId: string, userId: string) {
switch (type) { switch (type) {
case 'HIRE': { case 'HIRE': {
// 创建员工 + 合同
const { name, department, hireDate, monthlySalary, phone, idCardNumber, gender, contractStartDate, contractEndDate, contractType = 'FIXED' } = formData const { name, department, hireDate, monthlySalary, phone, idCardNumber, gender, contractStartDate, contractEndDate, contractType = 'FIXED' } = formData
const idCardHash = idCardNumber ? crypto.createHash('sha256').update(idCardNumber).digest('hex') : null const result = await createEmployee(orgId, userId, {
const employee = await prisma.employee.create({ name,
data: { department: department || '未分配',
orgId, hireDate,
name, monthlySalary: monthlySalary || 0,
department: department || '未分配', phone,
hireDate: new Date(hireDate), idCardNumber,
monthlySalary: encrypt(String(monthlySalary || 0)), gender,
phone: phone || null, contract: contractStartDate ? {
idCardNumber: idCardNumber ? encrypt(idCardNumber) : null, contractType,
idCardHash, startDate: contractStartDate,
gender: gender || null, endDate: contractEndDate || undefined,
createdBy: userId, contractYears: 3,
}, probationMonths: 0,
probationSalary: 0,
} : undefined,
}) })
if (contractStartDate) { return { employeeId: result.id }
await prisma.laborContract.create({
data: {
orgId,
employeeId: employee.id,
startDate: new Date(contractStartDate),
endDate: contractEndDate ? new Date(contractEndDate) : null,
contractType: contractType as any,
signMethod: 'PAPER',
contractYears: 3,
createdBy: userId,
},
})
}
return { employeeId: employee.id }
} }
case 'ONBOARD': { case 'ONBOARD': {
const { employeeId, hireDate } = formData const { employeeId, hireDate } = formData
@@ -79,17 +69,26 @@ export async function executeWorkProcess(processId: string, type: string, formDa
case 'CONFIRM': { case 'CONFIRM': {
const { employeeId, confirmDate, regularSalary } = formData const { employeeId, confirmDate, regularSalary } = formData
if (employeeId) { if (employeeId) {
const updateData: any = {}
if (regularSalary) { if (regularSalary) {
updateData.monthlySalary = encrypt(String(regularSalary))
}
if (confirmDate) {
updateData.probationEndDate = new Date(confirmDate)
}
if (Object.keys(updateData).length > 0) {
await prisma.employee.update({ await prisma.employee.update({
where: { id: employeeId }, where: { id: employeeId },
data: { monthlySalary: encrypt(String(regularSalary)) }, data: updateData,
}) })
await runRiskDetection(orgId)
} }
} }
return { employeeId } return { employeeId }
} }
case 'RENEW': { case 'RENEW': {
const { employeeId, oldContractId, newStartDate, newEndDate, newSalary, contractType = 'FIXED', contractYears = 3 } = formData const { employeeId, oldContractId, newStartDate, newEndDate, newSalary, contractType = 'FIXED', contractYears = 3 } = formData
// 截止旧合同
if (oldContractId) { if (oldContractId) {
const oldEndDate = new Date(newStartDate) const oldEndDate = new Date(newStartDate)
oldEndDate.setDate(oldEndDate.getDate() - 1) oldEndDate.setDate(oldEndDate.getDate() - 1)
@@ -98,21 +97,16 @@ export async function executeWorkProcess(processId: string, type: string, formDa
data: { endDate: oldEndDate }, data: { endDate: oldEndDate },
}) })
} }
// 创建新合同(复用 addContract 的验证逻辑)
if (employeeId) { if (employeeId) {
const oldContract = oldContractId ? await prisma.laborContract.findUnique({ where: { id: oldContractId } }) : null const result = await addContract(orgId, userId, {
const contract = await prisma.laborContract.create({ employeeId,
data: { startDate: newStartDate,
orgId, endDate: newEndDate || undefined,
employeeId, contractType,
signDate: new Date(), contractYears: Number(contractYears) || 3,
startDate: new Date(newStartDate), probationMonths: 0,
endDate: newEndDate ? new Date(newEndDate) : null, probationSalary: 0,
contractType: contractType as any,
signMethod: 'PAPER',
contractYears: Number(contractYears) || 3,
renewalCount: (oldContract?.renewalCount || 0) + 1,
createdBy: userId,
},
}) })
if (newSalary) { if (newSalary) {
await prisma.employee.update({ await prisma.employee.update({
@@ -120,39 +114,54 @@ export async function executeWorkProcess(processId: string, type: string, formDa
data: { monthlySalary: encrypt(String(newSalary)) }, data: { monthlySalary: encrypt(String(newSalary)) },
}) })
} }
return { employeeId, newContractId: contract.id } return { employeeId, newContractId: result.id }
} }
return { employeeId } return { employeeId }
} }
case 'TERMINATE': { case 'TERMINATE': {
const { employeeId, contractId, terminateDate } = formData const { employeeId, contractId, terminateDate, reason = 'EXPIRED', compensation = 0 } = formData
// 截止合同
if (contractId) { if (contractId) {
await prisma.laborContract.update({ await prisma.laborContract.update({
where: { id: contractId }, where: { id: contractId },
data: { endDate: new Date(terminateDate) }, data: { endDate: new Date(terminateDate) },
}) })
} }
// 通过 termination service 创建解聘记录(含风险评级、合规清单)
if (employeeId) { if (employeeId) {
await prisma.employee.update({ const draft = await createTerminationDraft(orgId, userId, {
where: { id: employeeId }, employeeId,
data: { status: 'RESIGNED' }, type: 'TERMINATION',
reason,
terminationDate: terminateDate,
compensation,
}) })
// 直接执行(WorkProcess 流程审批已通过)
await executeTermination(orgId, draft.id, userId)
return { employeeId, terminationRecordId: draft.id }
} }
return { employeeId } return { employeeId }
} }
case 'RESCIND': { case 'RESCIND': {
const { employeeId, contractId, rescindDate } = formData const { employeeId, contractId, rescindDate, reason = 'NEGOTIATED', compensation = 0 } = formData
// 截止合同
if (contractId) { if (contractId) {
await prisma.laborContract.update({ await prisma.laborContract.update({
where: { id: contractId }, where: { id: contractId },
data: { endDate: new Date(rescindDate) }, data: { endDate: new Date(rescindDate) },
}) })
} }
// 通过 termination service 创建解聘记录(含风险评级、合规清单)
if (employeeId) { if (employeeId) {
await prisma.employee.update({ const draft = await createTerminationDraft(orgId, userId, {
where: { id: employeeId }, employeeId,
data: { status: 'RESIGNED' }, type: 'TERMINATION',
reason,
terminationDate: rescindDate,
compensation,
}) })
await executeTermination(orgId, draft.id, userId)
return { employeeId, terminationRecordId: draft.id }
} }
return { employeeId } return { employeeId }
} }
@@ -179,53 +188,40 @@ export async function executeWorkProcess(processId: string, type: string, formDa
case 'CUSTOM_CONTRACT': { case 'CUSTOM_CONTRACT': {
const { employeeId, contractStartDate, contractEndDate, contractType = 'FIXED', signMethod = 'PAPER', contractYears = 3 } = formData const { employeeId, contractStartDate, contractEndDate, contractType = 'FIXED', signMethod = 'PAPER', contractYears = 3 } = formData
if (employeeId) { if (employeeId) {
const contract = await prisma.laborContract.create({ const result = await addContract(orgId, userId, {
data: { employeeId,
orgId, startDate: contractStartDate,
employeeId, endDate: contractEndDate || undefined,
signDate: new Date(), contractType,
startDate: new Date(contractStartDate), signMethod,
endDate: contractEndDate ? new Date(contractEndDate) : null, contractYears: Number(contractYears) || 3,
contractType: contractType as any, probationMonths: 0,
signMethod: signMethod as any, probationSalary: 0,
contractYears: Number(contractYears) || 3,
createdBy: userId,
},
}) })
return { employeeId, newContractId: contract.id } return { employeeId, newContractId: result.id }
} }
return {} return {}
} }
case 'FLEXIBLE': { case 'FLEXIBLE': {
const { name, phone, idCardNumber, department, agreementStartDate, agreementEndDate, payMethod } = formData const { name, phone, idCardNumber, department, agreementStartDate, agreementEndDate, payMethod } = formData
const idCardHash = idCardNumber ? crypto.createHash('sha256').update(idCardNumber).digest('hex') : null const empResult = await createEmployee(orgId, userId, {
const employee = await prisma.employee.create({ name,
data: { department: department || '灵活用工',
orgId, hireDate: agreementStartDate,
name, monthlySalary: 0,
department: department || '灵活用工', phone,
hireDate: new Date(agreementStartDate), idCardNumber,
monthlySalary: encrypt('0'),
phone: phone || null,
idCardNumber: idCardNumber ? encrypt(idCardNumber) : null,
idCardHash,
createdBy: userId,
},
}) })
const contract = await prisma.laborContract.create({ const contractResult = await addContract(orgId, userId, {
data: { employeeId: empResult.id,
orgId, startDate: agreementStartDate,
employeeId: employee.id, endDate: agreementEndDate || undefined,
signDate: new Date(), contractType: 'LABOR',
startDate: new Date(agreementStartDate), contractYears: 1,
endDate: agreementEndDate ? new Date(agreementEndDate) : null, probationMonths: 0,
contractType: 'LABOR', probationSalary: 0,
signMethod: 'PAPER',
contractYears: 1,
createdBy: userId,
},
}) })
return { employeeId: employee.id, newContractId: contract.id } return { employeeId: empResult.id, newContractId: contractResult.id }
} }
case 'INFO_SUBMIT': { case 'INFO_SUBMIT': {
const { employeeId, ...updateFields } = formData const { employeeId, ...updateFields } = formData
+4
View File
@@ -108,11 +108,15 @@ const FORM_FIELDS: Record<string, { key: string; label: string; type: 'text' | '
{ key: 'employeeId', label: '员工ID', type: 'text' }, { key: 'employeeId', label: '员工ID', type: 'text' },
{ key: 'contractId', label: '合同ID', type: 'text' }, { key: 'contractId', label: '合同ID', type: 'text' },
{ key: 'terminateDate', label: '终止日期', type: 'date' }, { key: 'terminateDate', label: '终止日期', type: 'date' },
{ key: 'reason', label: '终止原因', type: 'select', options: ['EXPIRED', 'NEGOTIATED', 'FAULT', 'NONFAULT', 'LAYOFF'] },
{ key: 'compensation', label: '经济补偿金', type: 'number' },
], ],
RESCIND: [ RESCIND: [
{ key: 'employeeId', label: '员工ID', type: 'text' }, { key: 'employeeId', label: '员工ID', type: 'text' },
{ key: 'contractId', label: '合同ID', type: 'text' }, { key: 'contractId', label: '合同ID', type: 'text' },
{ key: 'rescindDate', label: '解除日期', type: 'date' }, { key: 'rescindDate', label: '解除日期', type: 'date' },
{ key: 'reason', label: '解除原因', type: 'select', options: ['NEGOTIATED', 'FAULT', 'NONFAULT', 'LAYOFF'] },
{ key: 'compensation', label: '经济补偿金', type: 'number' },
], ],
LEAVING_CERT: [ LEAVING_CERT: [
{ key: 'employeeName', label: '员工姓名', type: 'text' }, { key: 'employeeName', label: '员工姓名', type: 'text' },