diff --git a/api/controller_front/apply.js b/api/controller_front/apply.js index 8764206..6c88be0 100644 --- a/api/controller_front/apply.js +++ b/api/controller_front/apply.js @@ -34,79 +34,74 @@ module.exports = { * description: 获取成功 */ 'POST /apply/list': async (ctx) => { - try { - const models = Framework.getModels(); - const { apply_records, op } = models; - const body = ctx.getBody(); - - // 从query或body中获取sn_code,优先从query获取 - const sn_code = ctx.query?.sn_code || body.sn_code; - - if (!sn_code) { - return ctx.fail('请提供设备SN码'); - } - - const seachOption = body.seachOption || {}; - const pageOption = body.pageOption || {}; - - // 获取分页参数 - const page = pageOption.page || 1; - const pageSize = pageOption.pageSize || 20; - const limit = pageSize; - const offset = (page - 1) * pageSize; - - const where = { sn_code }; - - // 平台筛选 - if (seachOption.platform) { - where.platform = seachOption.platform; - } - - // 投递状态筛选 - if (seachOption.applyStatus) { - where.applyStatus = seachOption.applyStatus; - } - - // 反馈状态筛选 - if (seachOption.feedbackStatus) { - where.feedbackStatus = seachOption.feedbackStatus; - } - - // 搜索:岗位名称、公司名称 - if (seachOption.key && seachOption.value) { - const key = seachOption.key; - const value = seachOption.value; - - if (key === 'jobTitle') { - where.jobTitle = { [op.like]: `%${value}%` }; - } else if (key === 'companyName') { - where.companyName = { [op.like]: `%${value}%` }; - } else { - // 默认搜索岗位名称或公司名称 - where[op.or] = [ - { jobTitle: { [op.like]: `%${value}%` } }, - { companyName: { [op.like]: `%${value}%` } } - ]; - } - } - - const result = await apply_records.findAndCountAll({ - where, - limit, - offset, - order: [['applyTime', 'DESC']] - }); - - return ctx.success({ - count: result.count, - total: result.count, - rows: result.rows, - list: result.rows - }); - } catch (error) { - console.error('获取投递记录列表失败:', error); - return ctx.fail('获取投递记录列表失败: ' + error.message); + const models = Framework.getModels(); + const { apply_records, op } = models; + const body = ctx.getBody(); + + // 从query或body中获取sn_code,优先从query获取 + const sn_code = ctx.query?.sn_code || body.sn_code; + + if (!sn_code) { + return ctx.fail('请提供设备SN码'); } + + const seachOption = body.seachOption || {}; + const pageOption = body.pageOption || {}; + + // 获取分页参数 + const page = pageOption.page || 1; + const pageSize = pageOption.pageSize || 20; + const limit = pageSize; + const offset = (page - 1) * pageSize; + + const where = { sn_code }; + + // 平台筛选 + if (seachOption.platform) { + where.platform = seachOption.platform; + } + + // 投递状态筛选 + if (seachOption.applyStatus) { + where.applyStatus = seachOption.applyStatus; + } + + // 反馈状态筛选 + if (seachOption.feedbackStatus) { + where.feedbackStatus = seachOption.feedbackStatus; + } + + // 搜索:岗位名称、公司名称 + if (seachOption.key && seachOption.value) { + const key = seachOption.key; + const value = seachOption.value; + + if (key === 'jobTitle') { + where.jobTitle = { [op.like]: `%${value}%` }; + } else if (key === 'companyName') { + where.companyName = { [op.like]: `%${value}%` }; + } else { + // 默认搜索岗位名称或公司名称 + where[op.or] = [ + { jobTitle: { [op.like]: `%${value}%` } }, + { companyName: { [op.like]: `%${value}%` } } + ]; + } + } + + const result = await apply_records.findAndCountAll({ + where, + limit, + offset, + order: [['applyTime', 'DESC']] + }); + + return ctx.success({ + count: result.count, + total: result.count, + rows: result.rows, + list: result.rows + }); }, /** @@ -128,43 +123,38 @@ module.exports = { * description: 获取成功 */ 'GET /apply/statistics': async (ctx) => { - try { - const models = Framework.getModels(); - const { apply_records } = models; - const { sn_code } = ctx.query; - const final_sn_code = sn_code; + const models = Framework.getModels(); + const { apply_records } = models; + const { sn_code } = ctx.query; + const final_sn_code = sn_code; - if (!final_sn_code) { - return ctx.fail('请提供设备SN码'); - } - - const [ - totalCount, - successCount, - failedCount, - pendingCount, - interviewCount - ] = await Promise.all([ - apply_records.count({ where: { sn_code: final_sn_code } }), - apply_records.count({ where: { sn_code: final_sn_code, applyStatus: 'success' } }), - apply_records.count({ where: { sn_code: final_sn_code, applyStatus: 'failed' } }), - apply_records.count({ where: { sn_code: final_sn_code, applyStatus: 'pending' } }), - apply_records.count({ where: { sn_code: final_sn_code, feedbackStatus: 'interview' } }) - ]); - - return ctx.success({ - totalCount, - successCount, - failedCount, - pendingCount, - interviewCount, - successRate: totalCount > 0 ? ((successCount / totalCount) * 100).toFixed(2) : 0, - interviewRate: totalCount > 0 ? ((interviewCount / totalCount) * 100).toFixed(2) : 0 - }); - } catch (error) { - console.error('获取投递统计失败:', error); - return ctx.fail('获取投递统计失败: ' + error.message); + if (!final_sn_code) { + return ctx.fail('请提供设备SN码'); } + + const [ + totalCount, + successCount, + failedCount, + pendingCount, + interviewCount + ] = await Promise.all([ + apply_records.count({ where: { sn_code: final_sn_code } }), + apply_records.count({ where: { sn_code: final_sn_code, applyStatus: 'success' } }), + apply_records.count({ where: { sn_code: final_sn_code, applyStatus: 'failed' } }), + apply_records.count({ where: { sn_code: final_sn_code, applyStatus: 'pending' } }), + apply_records.count({ where: { sn_code: final_sn_code, feedbackStatus: 'interview' } }) + ]); + + return ctx.success({ + totalCount, + successCount, + failedCount, + pendingCount, + interviewCount, + successRate: totalCount > 0 ? ((successCount / totalCount) * 100).toFixed(2) : 0, + interviewRate: totalCount > 0 ? ((interviewCount / totalCount) * 100).toFixed(2) : 0 + }); }, /** @@ -186,28 +176,23 @@ module.exports = { * description: 获取成功 */ 'GET /apply/detail': async (ctx) => { - try { - const models = Framework.getModels(); - const { apply_records } = models; - // 支持 applyId 和 id 两种参数名(向后兼容) - const recordId = ctx.query.applyId || ctx.query.id; + const models = Framework.getModels(); + const { apply_records } = models; + // 支持 applyId 和 id 两种参数名(向后兼容) + const recordId = ctx.query.applyId || ctx.query.id; - if (!recordId) { - return ctx.fail('投递记录ID不能为空'); - } - - // 使用 id 字段查询(数据库主键) - const record = await apply_records.findOne({ where: { id: recordId } }); - - if (!record) { - return ctx.fail('投递记录不存在'); - } - - return ctx.success(record); - } catch (error) { - console.error('获取投递记录详情失败:', error); - return ctx.fail('获取投递记录详情失败: ' + error.message); + if (!recordId) { + return ctx.fail('投递记录ID不能为空'); } + + // 使用 id 字段查询(数据库主键) + const record = await apply_records.findOne({ where: { id: recordId } }); + + if (!record) { + return ctx.fail('投递记录不存在'); + } + + return ctx.success(record); }, /** @@ -229,62 +214,57 @@ module.exports = { * description: 获取成功 */ 'GET /apply/trend': async (ctx) => { - try { - const models = Framework.getModels(); - const { apply_records, op } = models; - const { sn_code } = ctx.query; + const models = Framework.getModels(); + const { apply_records, op } = models; + const { sn_code } = ctx.query; - if (!sn_code) { - return ctx.fail('请提供设备SN码'); - } - - // 计算近7天的日期范围 - const today = new Date(); - today.setHours(23, 59, 59, 999); - const sevenDaysAgo = new Date(); - sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 6); - sevenDaysAgo.setHours(0, 0, 0, 0); - - // 查询近7天的投递记录 - const records = await apply_records.findAll({ - where: { - sn_code: sn_code, - applyTime: { - [op.gte]: sevenDaysAgo, - [op.lte]: today - } - }, - attributes: ['applyTime'], - raw: true - }); - - // 生成7天的日期数组 - const trendData = []; - for (let i = 6; i >= 0; i--) { - const date = new Date(); - date.setDate(date.getDate() - i); - date.setHours(0, 0, 0, 0); - - const dateStr = `${date.getMonth() + 1}/${date.getDate()}`; - - // 统计当天的投递数量 - const count = records.filter(record => { - const recordDate = new Date(record.applyTime); - recordDate.setHours(0, 0, 0, 0); - return recordDate.getTime() === date.getTime(); - }).length; - - trendData.push({ - date: dateStr, - value: count - }); - } - - return ctx.success(trendData); - } catch (error) { - console.error('获取投递趋势数据失败:', error); - return ctx.fail('获取投递趋势数据失败: ' + error.message); + if (!sn_code) { + return ctx.fail('请提供设备SN码'); } + + // 计算近7天的日期范围 + const today = new Date(); + today.setHours(23, 59, 59, 999); + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 6); + sevenDaysAgo.setHours(0, 0, 0, 0); + + // 查询近7天的投递记录 + const records = await apply_records.findAll({ + where: { + sn_code: sn_code, + applyTime: { + [op.gte]: sevenDaysAgo, + [op.lte]: today + } + }, + attributes: ['applyTime'], + raw: true + }); + + // 生成7天的日期数组 + const trendData = []; + for (let i = 6; i >= 0; i--) { + const date = new Date(); + date.setDate(date.getDate() - i); + date.setHours(0, 0, 0, 0); + + const dateStr = `${date.getMonth() + 1}/${date.getDate()}`; + + // 统计当天的投递数量 + const count = records.filter(record => { + const recordDate = new Date(record.applyTime); + recordDate.setHours(0, 0, 0, 0); + return recordDate.getTime() === date.getTime(); + }).length; + + trendData.push({ + date: dateStr, + value: count + }); + } + + return ctx.success(trendData); } }; diff --git a/api/middleware/schedule/taskQueue.js b/api/middleware/schedule/taskQueue.js index a9323dd..53c91f6 100644 --- a/api/middleware/schedule/taskQueue.js +++ b/api/middleware/schedule/taskQueue.js @@ -1069,10 +1069,46 @@ class TaskQueue { runningCount: 0 }; + // 辅助函数:从任务参数中提取职位和公司信息 + const extractJobInfo = (taskData) => { + let jobTitle = null; + let companyName = null; + + // 尝试从 taskParams 中提取 + if (taskData.taskParams) { + let params = null; + try { + params = typeof taskData.taskParams === 'string' + ? JSON.parse(taskData.taskParams) + : taskData.taskParams; + } catch (e) { + // 解析失败,忽略 + } + + if (params) { + jobTitle = params.jobTitle || params.job_title || null; + companyName = params.companyName || params.company_name || params.brandName || params.brand_name || null; + } + } + + // 如果 taskName 中包含职位信息(格式:投递简历 - 职位名 @ 公司名) + if (!jobTitle || !companyName) { + const taskName = taskData.taskName || taskData.task_name || ''; + const match = taskName.match(/投递简历[::]\s*(.+?)\s*@\s*(.+?)(?:\s*\(|$)/); + if (match) { + if (!jobTitle) jobTitle = match[1].trim(); + if (!companyName) companyName = match[2].trim(); + } + } + + return { jobTitle, companyName }; + }; + // 获取当前执行的任务(优先从内存状态获取,如果没有则从数据库查询) let currentTask = null; if (status.currentTask) { const taskData = status.currentTask; + const { jobTitle, companyName } = extractJobInfo(taskData); currentTask = { taskId: taskData.id, taskName: taskData.taskName || taskData.task_name || taskData.taskType || taskData.task_type || '未知任务', @@ -1080,7 +1116,9 @@ class TaskQueue { status: 'running', progress: taskData.progress || 0, currentStep: taskData.currentStep || taskData.current_step || '', - startTime: taskData.startTime || taskData.start_time || taskData.created_time + startTime: taskData.startTime || taskData.start_time || taskData.created_time, + jobTitle: jobTitle, + companyName: companyName }; } else { // 如果内存中没有,从数据库查询当前运行的任务 @@ -1096,6 +1134,11 @@ class TaskQueue { if (runningTask) { const taskData = runningTask.toJSON(); + // 确保 taskParams 字段名正确(可能是 taskParams 或 task_params) + if (!taskData.taskParams && taskData.task_params) { + taskData.taskParams = taskData.task_params; + } + const { jobTitle, companyName } = extractJobInfo(taskData); currentTask = { taskId: taskData.id, taskName: taskData.taskName || taskData.task_name || taskData.taskType || taskData.task_type || '未知任务', @@ -1103,7 +1146,9 @@ class TaskQueue { status: 'running', progress: taskData.progress || 0, currentStep: taskData.currentStep || taskData.current_step || '', - startTime: taskData.startTime || taskData.start_time || taskData.created_time + startTime: taskData.startTime || taskData.start_time || taskData.created_time, + jobTitle: jobTitle, + companyName: companyName }; } } catch (error) { @@ -1241,6 +1286,28 @@ class TaskQueue { // 改进日志输出,显示更详细的任务信息 if (summary.currentTask) { const task = summary.currentTask; + + // 构建任务标识信息 + let taskIdentifier = task.taskName || task.taskType || '未知任务'; + + // 如果是投递简历任务,显示具体的职位和公司信息 + if (task.jobTitle || task.companyName) { + const jobInfo = []; + if (task.jobTitle) { + jobInfo.push(task.jobTitle); + } + if (task.companyName) { + // 截断过长的公司名称 + const companyName = task.companyName.length > 20 + ? task.companyName.substring(0, 20) + '...' + : task.companyName; + jobInfo.push(companyName); + } + if (jobInfo.length > 0) { + taskIdentifier = `投递职位: ${jobInfo.join(' @ ')}`; + } + } + const progressInfo = task.progress !== null && task.progress !== undefined ? `进度: ${task.progress}%` : ''; @@ -1249,9 +1316,10 @@ class TaskQueue { : ''; const detailInfo = [progressInfo, stepInfo].filter(Boolean).join(', '); const detailStr = detailInfo ? ` (${detailInfo})` : ''; + // 使用总待执行数(包括当前任务的剩余步骤) const totalCount = summary.totalPendingCount !== undefined ? summary.totalPendingCount : summary.pendingCount; - console.log(`[任务队列] 已发送任务状态摘要到 ${sn_code}: 当前任务=${task.taskName || task.taskType || '未知任务'}${detailStr}, 待执行=${totalCount}个`); + console.log(`[任务队列] 已发送任务状态摘要到 ${sn_code}: 当前任务=${taskIdentifier}${detailStr}, 待执行=${totalCount}个`); } else { console.log(`[任务队列] 已发送任务状态摘要到 ${sn_code}: 当前任务=无, 待执行=${summary.pendingCount}个`); }