Files
TurboHR/backend/scripts/migrate-records.ts
T
selfrelease 0df8aa77d9 feat: AIHR 智能人力资源管理系统初始提交
- 员工花名册管理(加密存储、导入导出)
- 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条)
- 社保公积金(多城市配置、版本管理、基数调整)
- 解聘管理(6步流程、证据链、工作交接)
- AI 助手(合同审查、风险预测、RAG 知识库)
- Dashboard 仪表盘
- 设置与通知
2026-07-24 13:53:11 +08:00

151 lines
4.5 KiB
TypeScript

/**
* 一次性迁移脚本:为现有员工创建初始版本记录
* 运行方式:npx tsx scripts/migrate-records.ts
*/
import prisma from '../src/lib/prisma.js'
import { decrypt } from '../src/lib/crypto.js'
function dateToMonth(date: Date): string {
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
return `${y}-${m}`
}
function prevMonth(month: string): string {
const [y, m] = month.split('-').map(Number)
const d = new Date(y, m - 2, 1)
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
}
async function main() {
const employees = await prisma.employee.findMany({
include: {
terminations: { orderBy: { terminationDate: 'desc' }, take: 1 },
salaryChanges: { orderBy: { createdAt: 'desc' }, take: 1 },
socialInsRecords: { take: 1 },
housingFundRecords: { take: 1 },
departmentRecords: { take: 1 },
},
})
console.log(`Found ${employees.length} employees to migrate`)
for (const emp of employees) {
const hireMonth = dateToMonth(emp.hireDate)
const termination = emp.terminations[0]
const endMonth = termination ? dateToMonth(termination.terminationDate) : null
// 解密月薪获取数值
let salaryNum = 0
try {
salaryNum = parseFloat(decrypt(emp.monthlySalary)) || 0
} catch {
salaryNum = parseFloat(emp.monthlySalary) || 0
}
const socialInsBase = emp.socialInsBase ?? salaryNum
const housingFundBase = emp.housingFundBase ?? salaryNum
// 1. 社保缴费记录(仅当尚无记录时创建)
if (emp.socialInsRecords.length === 0) {
await prisma.employeeSocialInsRecord.create({
data: {
orgId: emp.orgId,
employeeId: emp.id,
startMonth: emp.socialInsStartMonth || hireMonth,
endMonth: endMonth || emp.socialInsEndMonth || null,
base: socialInsBase,
changeType: 'ONBOARDING',
createdBy: emp.createdBy,
},
})
}
// 2. 公积金缴费记录
if (emp.housingFundRecords.length === 0) {
await prisma.employeeHousingFundRecord.create({
data: {
orgId: emp.orgId,
employeeId: emp.id,
startMonth: emp.housingFundStartMonth || hireMonth,
endMonth: endMonth || emp.housingFundEndMonth || null,
base: housingFundBase,
changeType: 'ONBOARDING',
createdBy: emp.createdBy,
},
})
}
// 3. 薪资变更记录(仅当尚无记录时创建)
if (emp.salaryChanges.length === 0) {
await prisma.salaryChangeRecord.create({
data: {
orgId: emp.orgId,
employeeId: emp.id,
oldSalary: 0,
newSalary: salaryNum,
effectiveDate: emp.hireDate,
effectiveMonth: hireMonth,
endMonth: null,
changeType: 'ONBOARDING',
createdBy: emp.createdBy,
},
})
} else {
// 已有记录但缺少 effectiveMonth/endMonth/changeType,补充
const latest = emp.salaryChanges[0]
if (!latest.effectiveMonth || !latest.changeType) {
await prisma.salaryChangeRecord.update({
where: { id: latest.id },
data: {
effectiveMonth: dateToMonth(latest.effectiveDate),
changeType: latest.changeType || 'SALARY_CHANGE',
},
})
}
}
// 4. 部门变更记录
if (emp.departmentRecords.length === 0) {
await prisma.employeeDepartmentRecord.create({
data: {
orgId: emp.orgId,
employeeId: emp.id,
oldDepartment: '',
newDepartment: emp.department,
effectiveMonth: hireMonth,
endMonth: null,
changeType: 'ONBOARDING',
createdBy: emp.createdBy,
},
})
}
// 5. 同步 Employee 便捷字段
await prisma.employee.update({
where: { id: emp.id },
data: {
socialInsStartMonth: emp.socialInsStartMonth || hireMonth,
socialInsEndMonth: endMonth || emp.socialInsEndMonth || null,
socialInsBase,
housingFundStartMonth: emp.housingFundStartMonth || hireMonth,
housingFundEndMonth: endMonth || emp.housingFundEndMonth || null,
housingFundBase,
},
})
console.log(`${emp.name} (${emp.department}) — records created/synced`)
}
console.log('\nMigration complete!')
}
main()
.catch((e) => {
console.error('Migration failed:', e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})