123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- 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;
|