123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- const db = require('../database/db');
- const { successResponse, errorResponse } = require('../utils/apiResponse');
- class CandidateController {
- static async getAllCandidates(req, res) {
- try {
- const [rows] = await db.query(`
- SELECT
- c.id,
- c.name,
- c.job_id as jobId,
- c.job_title as jobTitle,
- c.match_score as matchScore,
- c.status,
- c.highlights,
- c.concerns,
- c.summary,
- c.resume_text as resumeText,
- c.education,
- c.experience,
- c.skills,
- c.submitted_at as submittedAt,
- c.reviewed_at as reviewedAt,
- j.title as jobTitle
- FROM candidates c
- LEFT JOIN jobs j ON c.job_id = j.id
- `);
-
- const candidates = rows.map(row => ({
- ...row,
- highlights: row.highlights ? JSON.parse(row.highlights) : [],
- concerns: row.concerns ? JSON.parse(row.concerns) : [],
- skills: row.skills ? JSON.parse(row.skills) : [],
- submittedAt: row.submittedAt,
- reviewedAt: row.reviewedAt || null
- }));
-
- successResponse(res, candidates);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async getCandidatesByJob(req, res) {
- try {
- const [rows] = await db.query(`
- SELECT
- c.id,
- c.name,
- c.job_id as jobId,
- c.job_title as jobTitle,
- c.match_score as matchScore,
- c.status,
- c.highlights,
- c.concerns,
- c.summary,
- c.resume_text as resumeText,
- c.education,
- c.experience,
- c.skills,
- c.submitted_at as submittedAt,
- c.reviewed_at as reviewedAt
- FROM candidates c
- WHERE c.job_id = ?
- `, [req.params.jobId]);
-
- const candidates = rows.map(row => ({
- ...row,
- highlights: row.highlights ? JSON.parse(row.highlights) : [],
- concerns: row.concerns ? JSON.parse(row.concerns) : [],
- skills: row.skills ? JSON.parse(row.skills) : [],
- submittedAt: row.submittedAt,
- reviewedAt: row.reviewedAt || null
- }));
-
- successResponse(res, candidates);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async getCandidate(req, res) {
- try {
- const [rows] = await db.query(`
- SELECT
- c.id,
- c.name,
- c.job_id as jobId,
- c.job_title as jobTitle,
- c.match_score as matchScore,
- c.status,
- c.highlights,
- c.concerns,
- c.summary,
- c.resume_text as resumeText,
- c.education,
- c.experience,
- c.skills,
- c.submitted_at as submittedAt,
- c.reviewed_at as reviewedAt,
- j.title as jobTitle
- FROM candidates c
- LEFT JOIN jobs j ON c.job_id = j.id
- WHERE c.id = ?
- `, [req.params.id]);
-
- if (rows.length === 0) {
- return errorResponse(res, 'Candidate not found', 404);
- }
-
- const candidate = {
- ...rows[0],
- highlights: rows[0].highlights ? JSON.parse(rows[0].highlights) : [],
- concerns: rows[0].concerns ? JSON.parse(rows[0].concerns) : [],
- skills: rows[0].skills ? JSON.parse(rows[0].skills) : [],
- submittedAt: rows[0].submittedAt,
- reviewedAt: rows[0].reviewedAt || null
- };
-
- successResponse(res, candidate);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async createCandidate(req, res) {
- try {
- const {
- name,
- jobId,
- jobTitle,
- matchScore,
- status = 'pending',
- highlights = [],
- concerns = [],
- summary = '',
- resumeText,
- education,
- experience,
- skills = []
- } = req.body;
-
- const [result] = await db.query(`
- INSERT INTO candidates (
- id,
- name,
- job_id,
- job_title,
- match_score,
- status,
- highlights,
- concerns,
- summary,
- resume_text,
- education,
- experience,
- skills,
- submitted_at
- ) VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
- `, [
- name,
- jobId,
- jobTitle,
- matchScore,
- status,
- JSON.stringify(highlights),
- JSON.stringify(concerns),
- summary,
- resumeText,
- education,
- experience,
- JSON.stringify(skills)
- ]);
-
- const [newCandidate] = await db.query(`
- SELECT * FROM candidates WHERE id = ?
- `, [result.insertId]);
-
- successResponse(res, {
- ...newCandidate[0],
- highlights: newCandidate[0].highlights ? JSON.parse(newCandidate[0].highlights) : [],
- concerns: newCandidate[0].concerns ? JSON.parse(newCandidate[0].concerns) : [],
- skills: newCandidate[0].skills ? JSON.parse(newCandidate[0].skills) : [],
- submittedAt: newCandidate[0].submitted_at,
- reviewedAt: newCandidate[0].reviewed_at || null
- }, 201);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async updateCandidate(req, res) {
- try {
- const {
- name,
- jobId,
- jobTitle,
- matchScore,
- status,
- highlights,
- concerns,
- summary,
- resumeText,
- education,
- experience,
- skills
- } = req.body;
-
- const updates = [];
- const params = [];
-
- if (name) { updates.push('name = ?'); params.push(name); }
- if (jobId) { updates.push('job_id = ?'); params.push(jobId); }
- if (jobTitle) { updates.push('job_title = ?'); params.push(jobTitle); }
- if (matchScore) { updates.push('match_score = ?'); params.push(matchScore); }
- if (status) { updates.push('status = ?'); params.push(status); }
- if (highlights) { updates.push('highlights = ?'); params.push(JSON.stringify(highlights)); }
- if (concerns) { updates.push('concerns = ?'); params.push(JSON.stringify(concerns)); }
- if (summary) { updates.push('summary = ?'); params.push(summary); }
- if (resumeText) { updates.push('resume_text = ?'); params.push(resumeText); }
- if (education) { updates.push('education = ?'); params.push(education); }
- if (experience) { updates.push('experience = ?'); params.push(experience); }
- if (skills) { updates.push('skills = ?'); params.push(JSON.stringify(skills)); }
-
- if (updates.length === 0) {
- return errorResponse(res, 'No valid fields to update', 400);
- }
-
- params.push(req.params.id);
-
- await db.query(`
- UPDATE candidates
- SET ${updates.join(', ')}
- WHERE id = ?
- `, params);
-
- const [updatedCandidate] = await db.query(`
- SELECT * FROM candidates WHERE id = ?
- `, [req.params.id]);
-
- if (updatedCandidate.length === 0) {
- return errorResponse(res, 'Candidate not found', 404);
- }
-
- successResponse(res, {
- ...updatedCandidate[0],
- highlights: updatedCandidate[0].highlights ? JSON.parse(updatedCandidate[0].highlights) : [],
- concerns: updatedCandidate[0].concerns ? JSON.parse(updatedCandidate[0].concerns) : [],
- skills: updatedCandidate[0].skills ? JSON.parse(updatedCandidate[0].skills) : [],
- submittedAt: updatedCandidate[0].submitted_at,
- reviewedAt: updatedCandidate[0].reviewed_at || null
- });
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async updateCandidateStatus(req, res) {
- try {
- const { status } = req.body;
- console.log(`准备更新候选人状态: ID=${req.params.id}, 新状态=${status}`); // 调试
-
- if (!['pending', 'passed', 'rejected', 'interviewed'].includes(status)) {
- return errorResponse(res, 'Invalid status value', 400);
- }
- // 获取当前状态用于记录历史
- const [currentCandidate] = await db.query(`
- SELECT status FROM candidates WHERE id = ?
- `, [req.params.id]);
-
- if (currentCandidate.length === 0) {
- console.log('未找到候选人:', req.params.id); // 调试
- return errorResponse(res, 'Candidate not found', 404);
- }
- const oldStatus = currentCandidate[0].status;
- console.log(`当前状态: ${oldStatus}, 将更新为: ${status}`); // 调试
- // 执行更新
- const [result] = await db.query(`
- UPDATE candidates
- SET status = ?, reviewed_at = NOW()
- WHERE id = ?
- `, [status, req.params.id]);
-
- console.log('更新结果:', result); // 调试
- console.log('影响行数:', result.affectedRows); // 调试
- if (result.affectedRows === 0) {
- console.log('更新未影响任何行'); // 调试
- return errorResponse(res, 'No rows updated', 404);
- }
- // 记录状态变更历史
- await db.query(`
- INSERT INTO candidate_status_history (
- candidate_id, old_status, new_status, changed_by, change_reason
- ) VALUES (?, ?, ?, ?, ?)
- `, [
- req.params.id,
- oldStatus,
- status,
- req.user?.username || 'system',
- req.body.reason || 'Status updated'
- ]);
- // 获取更新后的数据
- const [updatedCandidate] = await db.query(`
- SELECT * FROM candidates WHERE id = ?
- `, [req.params.id]);
-
- console.log('更新后的候选人:', updatedCandidate[0]); // 调试
-
- successResponse(res, {
- ...updatedCandidate[0],
- highlights: JSON.parse(updatedCandidate[0].highlights || '[]'),
- concerns: JSON.parse(updatedCandidate[0].concerns || '[]'),
- skills: JSON.parse(updatedCandidate[0].skills || '[]')
- });
-
- } catch (err) {
- console.error('更新状态出错:', err); // 调试
- errorResponse(res, err);
- }
- }
- static async batchScreenCandidates(req, res) {
- try {
- const { candidateIds } = req.body;
-
- if (!Array.isArray(candidateIds) || candidateIds.length === 0) {
- return errorResponse(res, 'Invalid candidate IDs', 400);
- }
-
- // 这里可以添加实际的批量筛选逻辑
- // 例如调用AI服务进行批量评分等
-
- // 模拟批量更新状态
- await db.query(`
- UPDATE candidates
- SET status = 'passed', reviewed_at = NOW()
- WHERE id IN (?)
- `, [candidateIds]);
-
- // 获取更新后的候选人列表
- const [updatedCandidates] = await db.query(`
- SELECT * FROM candidates WHERE id IN (?)
- `, [candidateIds]);
-
- const formattedCandidates = updatedCandidates.map(candidate => ({
- ...candidate,
- highlights: candidate.highlights ? JSON.parse(candidate.highlights) : [],
- concerns: candidate.concerns ? JSON.parse(candidate.concerns) : [],
- skills: candidate.skills ? JSON.parse(candidate.skills) : [],
- submittedAt: candidate.submitted_at,
- reviewedAt: candidate.reviewed_at || null
- }));
-
- successResponse(res, formattedCandidates);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async deleteCandidate(req, res) {
- try {
- const [result] = await db.query(`
- DELETE FROM candidates WHERE id = ?
- `, [req.params.id]);
-
- if (result.affectedRows === 0) {
- return errorResponse(res, 'Candidate not found', 404);
- }
-
- successResponse(res, { id: req.params.id, deleted: true });
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async getCandidateSkills(req, res) {
- try {
- const [rows] = await db.query(`
- SELECT
- s.id,
- s.name,
- s.category,
- cs.proficiency
- FROM candidate_skills cs
- JOIN skills s ON cs.skill_id = s.id
- WHERE cs.candidate_id = ?
- `, [req.params.id]);
-
- successResponse(res, rows);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- static async getCandidateStatusHistory(req, res) {
- try {
- const [rows] = await db.query(`
- SELECT
- id,
- old_status as oldStatus,
- new_status as newStatus,
- changed_by as changedBy,
- change_reason as changeReason,
- change_time as changeTime
- FROM candidate_status_history
- WHERE candidate_id = ?
- ORDER BY change_time DESC
- `, [req.params.id]);
-
- successResponse(res, rows);
- } catch (err) {
- errorResponse(res, err);
- }
- }
- }
- module.exports = CandidateController;
|