feat: 完成多多畅职就业服务平台核心功能开发
主要更新: - ✅ 完成主题配色从暗色到亮蓝白配色的全面转换 - ✅ 实现高薪岗位页面及后端API集成 - ✅ 完成登录注册页面及认证系统 - ✅ 实现预招录确认功能 - ✅ 添加数据库管理和维护工具脚本 - ✅ 优化错误处理和用户体验 核心功能: 1. 首页 (index.html) - 3D地球、专业分类、过渡岗位 2. 高薪岗位页面 (high.html) - 岗位详情、预招录确认、成功案例 3. 登录注册 (auth.html) - 用户认证、专业分类选择 4. 后端API - RESTful接口,JWT认证,MySQL数据库 技术栈: - 前端:Three.js, GSAP, 原生JavaScript - 后端:Node.js, Express, MySQL - 认证:JWT, bcrypt - 样式:自定义CSS,响应式设计 数据库工具: - kill-by-ids.js - 批量终止MySQL进程 - unlock-all-tables.js - 解锁数据库表 - init-db.js - 初始化数据库 - 其他管理脚本 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
272
api/controllers/applicationController.js
Normal file
272
api/controllers/applicationController.js
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* 投递记录控制器
|
||||
*/
|
||||
|
||||
const db = require('../../config/database');
|
||||
|
||||
/**
|
||||
* 创建投递记录
|
||||
*/
|
||||
exports.createApplication = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const {
|
||||
job_type, job_name, company_name, company_short_name,
|
||||
city, province, segment_name, application_data
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!job_type || !job_name || !company_name) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '岗位类型、岗位名称和企业名称不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证job_type
|
||||
if (!['transition', 'referral'].includes(job_type)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的岗位类型'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否已投递过相同岗位
|
||||
const [existing] = await db.query(`
|
||||
SELECT id FROM job_applications
|
||||
WHERE user_id = ? AND job_name = ? AND company_name = ? AND status != 'withdrawn'
|
||||
`, [userId, job_name, company_name]);
|
||||
|
||||
if (existing.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '您已经投递过该岗位'
|
||||
});
|
||||
}
|
||||
|
||||
// 插入投递记录
|
||||
const [result] = await db.query(`
|
||||
INSERT INTO job_applications (
|
||||
user_id, job_type, job_name, company_name, company_short_name,
|
||||
city, province, segment_name, status, application_data
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?)
|
||||
`, [
|
||||
userId, job_type, job_name, company_name, company_short_name || null,
|
||||
city || null, province || null, segment_name || null,
|
||||
application_data ? JSON.stringify(application_data) : null
|
||||
]);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '投递成功',
|
||||
data: {
|
||||
id: result.insertId
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建投递记录错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取投递记录列表
|
||||
*/
|
||||
exports.getApplications = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const { job_type, status, page = 1, limit = 20 } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
let whereClause = 'user_id = ?';
|
||||
const params = [userId];
|
||||
|
||||
if (job_type) {
|
||||
whereClause += ' AND job_type = ?';
|
||||
params.push(job_type);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
whereClause += ' AND status = ?';
|
||||
params.push(status);
|
||||
}
|
||||
|
||||
// 计算分页
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 查询总数
|
||||
const [countResult] = await db.query(
|
||||
`SELECT COUNT(*) as total FROM job_applications WHERE ${whereClause}`,
|
||||
params
|
||||
);
|
||||
const total = countResult[0].total;
|
||||
|
||||
// 查询列表
|
||||
const [applications] = await db.query(`
|
||||
SELECT
|
||||
id, job_type, job_name, company_name, company_short_name,
|
||||
city, province, segment_name, status, applied_at, updated_at, notes
|
||||
FROM job_applications
|
||||
WHERE ${whereClause}
|
||||
ORDER BY applied_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`, [...params, parseInt(limit), parseInt(offset)]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
list: applications,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取投递记录错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取单个投递记录详情
|
||||
*/
|
||||
exports.getApplicationDetail = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const { id } = req.params;
|
||||
|
||||
const [applications] = await db.query(`
|
||||
SELECT * FROM job_applications
|
||||
WHERE id = ? AND user_id = ?
|
||||
`, [id, userId]);
|
||||
|
||||
if (applications.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '投递记录不存在'
|
||||
});
|
||||
}
|
||||
|
||||
const application = applications[0];
|
||||
|
||||
// 解析JSON字段
|
||||
if (application.application_data) {
|
||||
try {
|
||||
application.application_data = JSON.parse(application.application_data);
|
||||
} catch (e) {
|
||||
application.application_data = null;
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: application
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取投递详情错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回投递
|
||||
*/
|
||||
exports.withdrawApplication = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const { id } = req.params;
|
||||
|
||||
// 检查投递记录是否存在且属于当前用户
|
||||
const [applications] = await db.query(`
|
||||
SELECT id, status FROM job_applications
|
||||
WHERE id = ? AND user_id = ?
|
||||
`, [id, userId]);
|
||||
|
||||
if (applications.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '投递记录不存在'
|
||||
});
|
||||
}
|
||||
|
||||
const application = applications[0];
|
||||
|
||||
// 检查状态是否允许撤回
|
||||
if (['offered', 'rejected', 'withdrawn'].includes(application.status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '该投递记录无法撤回'
|
||||
});
|
||||
}
|
||||
|
||||
// 更新状态为撤回
|
||||
await db.query(`
|
||||
UPDATE job_applications
|
||||
SET status = 'withdrawn', updated_at = NOW()
|
||||
WHERE id = ?
|
||||
`, [id]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '撤回成功'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('撤回投递错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取投递统计
|
||||
*/
|
||||
exports.getStatistics = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
|
||||
// 统计各状态的投递数量
|
||||
const [stats] = await db.query(`
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN job_type = 'transition' THEN 1 ELSE 0 END) as transition_count,
|
||||
SUM(CASE WHEN job_type = 'referral' THEN 1 ELSE 0 END) as referral_count,
|
||||
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
|
||||
SUM(CASE WHEN status = 'reviewing' THEN 1 ELSE 0 END) as reviewing_count,
|
||||
SUM(CASE WHEN status = 'interviewed' THEN 1 ELSE 0 END) as interviewed_count,
|
||||
SUM(CASE WHEN status = 'offered' THEN 1 ELSE 0 END) as offered_count,
|
||||
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count
|
||||
FROM job_applications
|
||||
WHERE user_id = ?
|
||||
`, [userId]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: stats[0]
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取统计错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user