const express = require('express'); const router = express.Router(); const { pool } = require('../config/db'); const crypto = require('crypto'); // 安全解析 JSON 或返回原值 const safeParseJSON = (data) => { if (typeof data === 'string') { try { return JSON.parse(data); } catch (e) { return null; } } return data; }; router.put('/jobs/:id', async (req, res) => { const { id } = req.params; const updates = req.body; try { // 构建动态更新语句 const updateFields = []; const values = []; for (const [key, value] of Object.entries(updates)) { updateFields.push(`${key} = ?`); values.push(value); } if (updateFields.length === 0) { return res.status(400).json({ error: 'No fields to update' }); } values.push(id); const query = ` UPDATE jobs SET ${updateFields.join(', ')}, updated_at = NOW() WHERE id = ? `; const [result] = await pool.query(query, values); if (result.affectedRows === 0) { return res.status(404).json({ error: 'Job not found' }); } // 返回更新后的职位信息 const [updatedJob] = await pool.query('SELECT * FROM jobs WHERE id = ?', [id]); res.json(updatedJob[0]); } catch (error) { console.error('Update job error:', error); res.status(500).json({ error: 'Database error' }); } }); // 获取所有职位 router.get('/', async (req, res) => { try { const [jobs] = await pool.query(` SELECT id, title, department, location, description, status, pending_resumes AS pendingResumes, passed_resumes AS passedResumes, ai_criteria AS aiCriteria, created_at AS createdAt, updated_at AS updatedAt FROM jobs WHERE status != 'deleted' ORDER BY created_at DESC `); // 安全解析 aiCriteria const processedJobs = jobs.map(job => ({ ...job, aiCriteria: safeParseJSON(job.aiCriteria) })); res.json(processedJobs); } catch (error) { console.error('Get jobs error:', error); res.status(500).json({ error: 'Server error' }); } }); // 更新职位信息 router.put('/:id', async (req, res) => { const { id } = req.params; const updates = req.body; // 验证必要字段 if (!updates || Object.keys(updates).length === 0) { return res.status(400).json({ error: '请求体不能为空', example: { title: "新职位标题", description: "职位描述" } }); } try { // 只允许更新特定字段 const allowedFields = ['title', 'description', 'department', 'location', 'status']; const updateFields = []; const values = []; for (const [key, value] of Object.entries(updates)) { if (allowedFields.includes(key)) { updateFields.push(`${key} = ?`); values.push(value); } } if (updateFields.length === 0) { return res.status(400).json({ error: '没有有效的字段可更新', allowedFields }); } values.push(id); const query = ` UPDATE jobs SET ${updateFields.join(', ')}, updated_at = NOW() WHERE id = ? `; const [result] = await pool.query(query, values); if (result.affectedRows === 0) { return res.status(404).json({ error: '职位不存在或未更改', jobId: id }); } // 返回更新后的数据 const [updatedJob] = await pool.query( 'SELECT * FROM jobs WHERE id = ?', [id] ); res.json({ success: true, data: updatedJob[0] }); } catch (error) { console.error('数据库更新错误:', error); res.status(500).json({ error: '更新职位信息失败', details: process.env.NODE_ENV === 'development' ? error.message : null }); } }); // 创建新职位(修正版) router.post('/', async (req, res) => { const connection = await pool.getConnection(); try { await connection.beginTransaction(); const { title, department, location, description = '', // 默认空字符串 status = 'draft', pendingResumes = 0, // 添加默认值 passedResumes = 0, // 添加默认值 aiCriteria = {} // 默认空对象 } = req.body; // 直接插入,使用数据库自增ID const [result] = await connection.query( `INSERT INTO jobs ( title, department, location, description, status, pending_resumes, passed_resumes, ai_criteria, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ title, department, location, description, status, pendingResumes, passedResumes, JSON.stringify(aiCriteria), new Date(), new Date() ] ); // 获取新创建的记录 const [newJob] = await connection.query( 'SELECT * FROM jobs WHERE id = ?', [result.insertId] ); await connection.commit(); res.status(201).json({ ...newJob[0], id: newJob[0].id, // 保持原始数值ID pendingResumes: newJob[0].pending_resumes, passedResumes: newJob[0].passed_resumes, aiCriteria: safeParseJSON(newJob[0].ai_criteria), createdAt: newJob[0].created_at, updatedAt: newJob[0].updated_at }); } catch (error) { await connection.rollback(); console.error('Create job error:', error); res.status(500).json({ error: 'Failed to create job', details: process.env.NODE_ENV === 'development' ? { message: error.message, stack: error.stack } : undefined }); } finally { connection.release(); } }); router.delete('/:id', async (req, res) => { const { id } = req.params; try { // 使用软删除,将状态标记为deleted const [result] = await pool.query( `UPDATE jobs SET status = 'deleted', updated_at = NOW() WHERE id = ? AND status != 'deleted'`, [id] ); if (result.affectedRows === 0) { return res.status(404).json({ success: false, error: '职位不存在或已被删除' }); } // 成功删除返回204 No Content res.status(204).end(); } catch (error) { console.error('删除职位失败:', error); res.status(500).json({ success: false, error: '删除职位失败', details: process.env.NODE_ENV === 'development' ? error.message : undefined }); } }); // 更新职位 router.put('/:id/status', async (req, res) => { const { id } = req.params; const { status } = req.body; // 验证状态值 if (!['active', 'paused'].includes(status)) { return res.status(400).json({ success: false, error: '无效的状态值', allowed: ['active', 'paused'] }); } try { const [result] = await pool.query( `UPDATE jobs SET status = ?, updated_at = NOW() WHERE id = ?`, [status, id] ); if (result.affectedRows === 0) { return res.status(404).json({ success: false, error: '职位不存在' }); } res.json({ success: true, message: '状态更新成功' }); } catch (error) { console.error('状态更新失败:', error); res.status(500).json({ success: false, error: '服务器错误' }); } }); // 触发职位筛选 router.post('/:id/screen', async (req, res) => { try { const { id } = req.params; // 获取职位信息 const [jobs] = await pool.query( 'SELECT * FROM jobs WHERE id = ?', [id] ); if (jobs.length === 0) { return res.status(404).json({ error: 'Job not found' }); } const job = jobs[0]; // 使用安全解析方法 const aiCriteria = safeParseJSON(job.ai_criteria); if (!aiCriteria) { return res.status(400).json({ error: 'Invalid AI criteria format' }); } // 这里应该调用AI筛选逻辑 // 模拟筛选过程 await new Promise(resolve => setTimeout(resolve, 2000)); // 更新筛选结果 const [candidates] = await pool.query( 'SELECT id FROM candidates WHERE job_id = ?', [id] ); const passedCount = Math.floor(candidates.length * 0.6); await pool.query( `UPDATE jobs SET pending_resumes = ?, passed_resumes = ?, updated_at = ? WHERE id = ?`, [candidates.length, passedCount, new Date(), id] ); // 更新候选人状态 await pool.query( `UPDATE candidates SET status = CASE WHEN RAND() < 0.6 THEN 'passed' ELSE 'rejected' END WHERE job_id = ? AND status = 'pending'`, [id] ); const [updatedJob] = await pool.query( 'SELECT * FROM jobs WHERE id = ?', [id] ); res.json({ ...updatedJob[0], pendingResumes: updatedJob[0].pending_resumes, passedResumes: updatedJob[0].passed_resumes, aiCriteria: safeParseJSON(updatedJob[0].ai_criteria), createdAt: updatedJob[0].created_at, updatedAt: updatedJob[0].updated_at }); } catch (error) { console.error('Screen job error:', error); res.status(500).json({ error: 'Server error' }); } }); module.exports = router;