123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- 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.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.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.put('/:id', async (req, res) => {
- try {
- const { id } = req.params;
- const updates = req.body;
-
- const updateFields = [];
- const queryParams = [];
-
- // 动态构建更新语句
- for (const [key, value] of Object.entries(updates)) {
- if (key === 'aiCriteria') {
- updateFields.push('ai_criteria = ?');
- queryParams.push(JSON.stringify(value));
- } else {
- updateFields.push(`${key} = ?`);
- queryParams.push(value);
- }
- }
-
- if (updateFields.length === 0) {
- return res.status(400).json({ error: 'No fields to update' });
- }
-
- queryParams.push(id);
-
- await pool.query(
- `UPDATE jobs
- SET ${updateFields.join(', ')}, updated_at = ?
- WHERE id = ?`,
- [...queryParams, new Date()]
- );
-
- const [updatedJob] = await pool.query(
- 'SELECT * FROM jobs WHERE id = ?',
- [id]
- );
-
- if (updatedJob.length === 0) {
- return res.status(404).json({ error: 'Job not found' });
- }
-
- 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('Update job error:', error);
- res.status(500).json({ error: 'Server error' });
- }
- });
- // 删除职位
- router.delete('/:id', async (req, res) => {
- try {
- const { id } = req.params;
-
- // 软删除
- await pool.query(
- `UPDATE jobs SET status = 'deleted', updated_at = ?
- WHERE id = ?`,
- [new Date(), id]
- );
-
- res.status(204).end();
- } catch (error) {
- console.error('Delete job error:', error);
- res.status(500).json({ error: 'Server 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;
|