diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index cbee6d1..8e3f5da 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -170,6 +170,7 @@ model Organization { attendanceRecords AttendanceRecord[] trainingRecords TrainingRecord[] performanceRecords PerformanceRecord[] + performanceTemplates PerformanceTemplate[] retirementPolicies RetirementPolicy[] socialMonthlyProcesses SocialMonthlyProcess[] evidenceChains EvidenceChain[] @@ -644,11 +645,14 @@ model PerformanceRecord { employeeId String employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade) period String // 考核周期 YYYY-MM 或 YYYY-Q1 + periodType String @default("MONTHLY") // MONTHLY/QUARTERLY/YEARLY score Float @default(0) // 考核得分 grade String @default("B") // A/B/C/D result String @default("QUALIFIED") // EXCELLENT/QUALIFIED/NEED_IMPROVE/UNQUALIFIED summary String? // 考核评语 improvementPlan String? // 改进计划(不胜任时) + templateId String? // 关联绩效模板(选填) + dimensionScores Json? // 各维度得分明细 { dimensionName: score } employeeAck Boolean @default(false) ackDate DateTime? reviewer String? @@ -659,6 +663,22 @@ model PerformanceRecord { @@index([orgId, employeeId]) } +model PerformanceTemplate { + id String @id @default(cuid()) + orgId String + org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade) + name String // 模板名称 + description String? // 模板说明 + dimensions Json // 考核维度 [{ name, weight, maxScore, description }] + gradeRules Json? // 等级规则 [{ grade, result, minScore, maxScore }] + isDefault Boolean @default(false) + createdBy String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([orgId]) +} + // ========== 员工端表 ========== model Payslip { diff --git a/backend/src/routes/employee.routes.ts b/backend/src/routes/employee.routes.ts index 1d3614a..10ffcd4 100644 --- a/backend/src/routes/employee.routes.ts +++ b/backend/src/routes/employee.routes.ts @@ -3,6 +3,7 @@ import { authMiddleware, AuthRequest } from '../middleware/auth' import { auditLog } from '../middleware/auditLog' import { createEvidence } from '../services/evidence.service' import prisma from '../lib/prisma' +import { sha256 } from '../lib/crypto' import { createEmployeeSchema, updateEmployeeSchema, @@ -97,6 +98,24 @@ router.get('/:id', authMiddleware, async (req: AuthRequest, res, next) => { } }) +// 身份证查重 +router.get('/check-id-card', authMiddleware, async (req: AuthRequest, res, next) => { + try { + const idCard = req.query.idCard as string + if (!idCard || idCard.length < 18) { + return res.json({ success: true, data: { exists: false } }) + } + const hash = sha256(idCard) + const employee = await prisma.employee.findFirst({ + where: { orgId: req.user!.orgId, idCardHash: hash }, + select: { id: true, name: true, department: true, status: true }, + }) + res.json({ success: true, data: { exists: !!employee, employee } }) + } catch (err) { + next(err) + } +}) + router.post('/', authMiddleware, async (req: AuthRequest, res, next) => { try { const data = createEmployeeSchema.parse(req.body) diff --git a/backend/src/routes/enterprise-template.routes.ts b/backend/src/routes/enterprise-template.routes.ts index db33cc2..b0d683f 100644 --- a/backend/src/routes/enterprise-template.routes.ts +++ b/backend/src/routes/enterprise-template.routes.ts @@ -146,10 +146,17 @@ router.get('/:id/download', authMiddleware, async (req: AuthRequest, res: Respon if (!template) { return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } }) } + // 包装为 HTML 格式以确保 Word 正确打开 + const htmlContent = ` +
Word 文件转换失败,请下载查看
正在转换 Word 文档...
管理全员绩效考核记录
{t.description}