diff --git a/api/middleware/job/resumeManager.js b/api/middleware/job/resumeManager.js index 98c8c41..3b6e446 100644 --- a/api/middleware/job/resumeManager.js +++ b/api/middleware/job/resumeManager.js @@ -316,152 +316,168 @@ class ResumeManager { const resume_info = db.getModel('resume_info'); + // 解析 JSON 字段 + let skillsArray = []; + let workExpArray = []; + let projectExpArray = []; + + try { + if (resumeInfo.skills) { + skillsArray = typeof resumeInfo.skills === 'string' ? JSON.parse(resumeInfo.skills) : resumeInfo.skills; + } + } catch (e) { + console.warn(`[简历管理] 解析技能字段失败:`, e); + } + + try { + if (resumeInfo.workExperience) { + workExpArray = typeof resumeInfo.workExperience === 'string' ? JSON.parse(resumeInfo.workExperience) : resumeInfo.workExperience; + } + } catch (e) { + console.warn(`[简历管理] 解析工作经历字段失败:`, e); + } + + try { + if (resumeInfo.projectExperience) { + projectExpArray = typeof resumeInfo.projectExperience === 'string' ? JSON.parse(resumeInfo.projectExperience) : resumeInfo.projectExperience; + } + } catch (e) { + console.warn(`[简历管理] 解析项目经历字段失败:`, e); + } + + // 构建工作经历文本 + const workExpText = workExpArray.map((work, index) => { + return `${index + 1}. ${work.company || ''} - ${work.position || ''} (${work.startDate || ''} ~ ${work.endDate || '至今'}) + 工作内容:${work.content || ''} + 行业:${work.industry || ''}`; + }).join('\n\n'); + + // 构建项目经历文本 + const projectExpText = projectExpArray.map((project, index) => { + return `${index + 1}. ${project.name || ''} - ${project.role || ''} (${project.startDate || ''} ~ ${project.endDate || '至今'}) + 项目描述:${project.description || ''} + 项目成果:${project.performance || ''}`; + }).join('\n\n'); + // 构建分析提示词 const prompt = `请分析以下简历,提供专业的评估: -姓名:${resumeInfo.fullName} -工作年限:${resumeInfo.workYears} -当前职位:${resumeInfo.currentPosition} -期望职位:${resumeInfo.expectedPosition} -期望薪资:${resumeInfo.expectedSalary} -学历:${resumeInfo.education} -技能:${resumeInfo.skills} +【基本信息】 +姓名:${resumeInfo.fullName || ''} +性别:${resumeInfo.gender || ''} +年龄:${resumeInfo.age || ''} +所在地:${resumeInfo.location || ''} +工作年限:${resumeInfo.workYears || ''} -个人优势: -${resumeInfo.skillDescription} +【教育背景】 +学历:${resumeInfo.education || ''} +专业:${resumeInfo.major || ''} +毕业院校:${resumeInfo.school || ''} +毕业年份:${resumeInfo.graduationYear || ''} -请从以下几个方面进行分析: -1. 核心技能标签(提取5-10个关键技能) -2. 优势分析(100字以内) -3. 劣势分析(100字以内) -4. 职业建议(150字以内) -5. 竞争力评分(0-100分)`; +【当前状态】 +当前职位:${resumeInfo.currentPosition || ''} +当前公司:${resumeInfo.currentCompany || ''} +当前薪资:${resumeInfo.currentSalary || ''} - try { - // 调用AI服务进行分析 - const aiAnalysis = await aiService.analyzeResume(prompt); +【求职期望】 +期望职位:${resumeInfo.expectedPosition || ''} +期望薪资:${resumeInfo.expectedSalary || ''} +期望地点:${resumeInfo.expectedLocation || ''} +期望行业:${resumeInfo.expectedIndustry || ''} - // 解析AI返回的结果 - const analysis = this.parse_ai_analysis(aiAnalysis, resumeInfo); +【技能标签】 +${skillsArray.length > 0 ? skillsArray.join('、') : '无'} - // 确保所有字段都有值 - const updateData = { - aiSkillTags: JSON.stringify(analysis.skillTags || []), - aiStrengths: analysis.strengths || '', - aiWeaknesses: analysis.weaknesses || '', - aiCareerSuggestion: analysis.careerSuggestion || '', - aiCompetitiveness: parseInt(analysis.competitiveness || 70, 10) - }; +【个人优势描述】 +${resumeInfo.skillDescription || ''} - // 确保竞争力评分在 0-100 范围内 - if (updateData.aiCompetitiveness < 0) updateData.aiCompetitiveness = 0; - if (updateData.aiCompetitiveness > 100) updateData.aiCompetitiveness = 100; +【工作经历】 +${workExpText || '无'} - // 更新简历的AI分析字段 - await resume_info.update(updateData, { where: { resumeId: resumeId } }); +【项目经历】 +${projectExpText || '无'} - console.log(`[简历管理] AI分析完成 - 竞争力评分: ${updateData.aiCompetitiveness}, 技能标签: ${updateData.aiSkillTags}`); +请从以下几个方面进行专业分析,并返回 JSON 格式结果: +1. skillTags: 核心技能标签数组(提取5-10个最关键的技术技能,如:Vue、React、Node.js等) +2. strengths: 优势分析(100字以内,基于工作经历、项目经历、技能水平等综合评估) +3. weaknesses: 劣势分析(100字以内,指出需要改进的地方或不足) +4. careerSuggestion: 职业建议(150字以内,基于期望职位和当前能力给出职业发展建议) +5. competitiveness: 竞争力评分(0-100的整数,综合考虑工作年限、技能深度、项目经验、学历等因素) - return analysis; - } catch (error) { - console.error(`[简历管理] AI分析失败:`, error, { - resumeId: resumeId, - fullName: resumeInfo.fullName - }); +要求: +- 竞争力评分要客观公正,基于实际能力评估 +- 优势分析要突出核心亮点和技术能力 +- 劣势分析要指出真实存在的问题 +- 职业建议要具有针对性和可操作性`; - // 如果AI分析失败,使用基于规则的默认分析,并保存到数据库 - const defaultAnalysis = this.get_default_analysis(resumeInfo); + // 调用AI服务进行分析 + const aiAnalysis = await aiService.analyzeResume(prompt); - // 保存默认分析结果到数据库 - const updateData = { - aiSkillTags: JSON.stringify(defaultAnalysis.skillTags || []), - aiStrengths: defaultAnalysis.strengths || '', - aiWeaknesses: defaultAnalysis.weaknesses || '', - aiCareerSuggestion: defaultAnalysis.careerSuggestion || '', - aiCompetitiveness: parseInt(defaultAnalysis.competitiveness || 70, 10) - }; - - // 确保竞争力评分在 0-100 范围内 - if (updateData.aiCompetitiveness < 0) updateData.aiCompetitiveness = 0; - if (updateData.aiCompetitiveness > 100) updateData.aiCompetitiveness = 100; - - await resume_info.update(updateData, { where: { resumeId: resumeId } }); - - console.log(`[简历管理] 使用默认分析结果 - 竞争力评分: ${updateData.aiCompetitiveness}`); - - return defaultAnalysis; + // 解析AI返回的结果,如果AI没返回有效数据,直接抛出错误 + const analysis = this.parse_ai_analysis(aiAnalysis, resumeInfo); + + if (!analysis) { + throw new Error('AI分析结果为空,无法处理'); } + + // 确保所有字段都有值 + const updateData = { + aiSkillTags: JSON.stringify(analysis.skillTags || []), + aiStrengths: analysis.strengths || '', + aiWeaknesses: analysis.weaknesses || '', + aiCareerSuggestion: analysis.careerSuggestion || '', + aiCompetitiveness: parseInt(analysis.competitiveness || 0, 10) + }; + + // 确保竞争力评分在 0-100 范围内 + if (updateData.aiCompetitiveness < 0) updateData.aiCompetitiveness = 0; + if (updateData.aiCompetitiveness > 100) updateData.aiCompetitiveness = 100; + + // 更新简历的AI分析字段 + await resume_info.update(updateData, { where: { resumeId: resumeId } }); + + console.log(`[简历管理] AI分析完成 - 竞争力评分: ${updateData.aiCompetitiveness}, 技能标签: ${updateData.aiSkillTags}`); + + return analysis; } /** * 解析AI分析结果 * @param {object} aiResponse - AI响应对象 * @param {object} resumeInfo - 简历信息 - * @returns {object} 解析后的分析结果 + * @returns {object|null} 解析后的分析结果,如果AI没返回有效数据则返回null */ parse_ai_analysis(aiResponse, resumeInfo) { - try { - // aiService.analyzeResume 返回格式: { analysis: {...} } 或 { analysis: { content: "...", parseError: true } } - const analysis = aiResponse.analysis; + // aiService.analyzeResume 返回格式: { analysis: {...} } 或 { analysis: { content: "...", parseError: true } } + const analysis = aiResponse?.analysis; - // 如果解析失败,analysis 会有 parseError 标记 - if (analysis && analysis.parseError) { - console.warn(`[简历管理] AI分析结果解析失败,使用默认分析`); - return this.get_default_analysis(resumeInfo); - } - - // 如果解析成功,analysis 直接是解析后的对象 - if (analysis && typeof analysis === 'object' && !analysis.parseError) { - return { - skillTags: analysis.skillTags || analysis.技能标签 || [], - strengths: analysis.strengths || analysis.优势 || analysis.优势分析 || '', - weaknesses: analysis.weaknesses || analysis.劣势 || analysis.劣势分析 || '', - careerSuggestion: analysis.careerSuggestion || analysis.职业建议 || '', - competitiveness: parseInt(analysis.competitiveness || analysis.竞争力评分 || 70, 10) - }; - } - - // 如果 analysis 是字符串,尝试解析 - const content = analysis?.content || analysis || ''; - if (typeof content === 'string' && content.includes('{') && content.includes('}')) { - const jsonMatch = content.match(/\{[\s\S]*\}/); - if (jsonMatch) { - const parsed = JSON.parse(jsonMatch[0]); - return { - skillTags: parsed.skillTags || parsed.技能标签 || [], - strengths: parsed.strengths || parsed.优势 || parsed.优势分析 || '', - weaknesses: parsed.weaknesses || parsed.劣势 || parsed.劣势分析 || '', - careerSuggestion: parsed.careerSuggestion || parsed.职业建议 || '', - competitiveness: parseInt(parsed.competitiveness || parsed.竞争力评分 || 70, 10) - }; - } - } - - // 如果无法解析JSON,尝试从文本中提取信息 - if (typeof content === 'string') { - const skillTagsMatch = content.match(/技能标签[::](.*?)(?:\n|$)/); - const strengthsMatch = content.match(/优势[分析]*[::](.*?)(?:\n|劣势)/s); - const weaknessesMatch = content.match(/劣势[分析]*[::](.*?)(?:\n|职业)/s); - const suggestionMatch = content.match(/职业建议[::](.*?)(?:\n|竞争力)/s); - const scoreMatch = content.match(/竞争力评分[::](\d+)/); - - return { - skillTags: skillTagsMatch ? skillTagsMatch[1].split(/[,,、]/).map(s => s.trim()) : [], - strengths: strengthsMatch ? strengthsMatch[1].trim() : '', - weaknesses: weaknessesMatch ? weaknessesMatch[1].trim() : '', - careerSuggestion: suggestionMatch ? suggestionMatch[1].trim() : '', - competitiveness: scoreMatch ? parseInt(scoreMatch[1], 10) : 70 - }; - } - - // 如果所有解析都失败,使用默认分析 - console.warn(`[简历管理] 无法解析AI分析结果,使用默认分析`); - return this.get_default_analysis(resumeInfo); - } catch (error) { - console.error(`[简历管理] 解析AI分析结果失败:`, error); - // 解析失败时使用默认分析 - return this.get_default_analysis(resumeInfo); + // 如果解析失败,analysis 会有 parseError 标记,直接返回 null + if (!analysis || analysis.parseError) { + console.warn(`[简历管理] AI分析结果解析失败或为空`); + return null; } + + // 如果解析成功,analysis 直接是解析后的对象 + if (typeof analysis === 'object' && !analysis.parseError) { + // 检查是否有必要的字段 + if (analysis.competitiveness === undefined && analysis.竞争力评分 === undefined) { + console.warn(`[简历管理] AI分析结果缺少竞争力评分字段`); + return null; + } + + return { + skillTags: analysis.skillTags || analysis.技能标签 || [], + strengths: analysis.strengths || analysis.优势 || analysis.优势分析 || '', + weaknesses: analysis.weaknesses || analysis.劣势 || analysis.劣势分析 || '', + careerSuggestion: analysis.careerSuggestion || analysis.职业建议 || '', + competitiveness: parseInt(analysis.competitiveness || analysis.竞争力评分 || 0, 10) + }; + } + + // 如果格式不对,返回 null + console.warn(`[简历管理] AI分析结果格式不正确`); + return null; } /** diff --git a/api/middleware/schedule/deviceWorkStatusNotifier.js b/api/middleware/schedule/deviceWorkStatusNotifier.js index dcffe24..ffbbdf6 100644 --- a/api/middleware/schedule/deviceWorkStatusNotifier.js +++ b/api/middleware/schedule/deviceWorkStatusNotifier.js @@ -156,23 +156,6 @@ class DeviceWorkStatusNotifier { await mqttClient.publish(topic, message); - if (workStatus.currentActivity) { - const activity = workStatus.currentActivity; - const activityInfo = activity.type === 'command' - ? `指令[${activity.name}]` - : `任务[${activity.name}]`; - console.log(` - 当前活动: ${activityInfo}`); - } else { - console.log(` - 当前活动: 无`); - } - - if (workStatus.waitingMessage) { - console.log(` - 等待消息: ${workStatus.waitingMessage.message}`); - } - - console.log(` - 待执行: ${workStatus.pendingQueue.totalCount}个`); - console.log(` - 下次执行: ${workStatus.pendingQueue.nextExecuteTimeText}`); - console.log(` - 消息大小: ${message.length} 字节`); } catch (error) { // 通知失败不影响任务执行,只记录日志 console.warn(`[设备工作状态] 推送失败:`, error.message);