This commit is contained in:
张成
2025-12-25 22:31:55 +08:00
parent 7ee92b8905
commit 55ef28440a
2 changed files with 137 additions and 138 deletions

View File

@@ -316,41 +316,118 @@ class ResumeManager {
const resume_info = db.getModel('resume_info'); 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 = `请分析以下简历,提供专业的评估: const prompt = `请分析以下简历,提供专业的评估:
姓名:${resumeInfo.fullName} 【基本信息】
工作年限${resumeInfo.workYears} 姓名${resumeInfo.fullName || ''}
当前职位${resumeInfo.currentPosition} 性别${resumeInfo.gender || ''}
期望职位${resumeInfo.expectedPosition} 年龄${resumeInfo.age || ''}
期望薪资${resumeInfo.expectedSalary} 所在地${resumeInfo.location || ''}
学历${resumeInfo.education} 工作年限${resumeInfo.workYears || ''}
技能:${resumeInfo.skills}
个人优势: 【教育背景】
${resumeInfo.skillDescription} 学历:${resumeInfo.education || ''}
专业:${resumeInfo.major || ''}
毕业院校:${resumeInfo.school || ''}
毕业年份:${resumeInfo.graduationYear || ''}
请从以下几个方面进行分析: 【当前状态】
1. 核心技能标签提取5-10个关键技能 当前职位:${resumeInfo.currentPosition || ''}
2. 优势分析100字以内 当前公司:${resumeInfo.currentCompany || ''}
3. 劣势分析100字以内 当前薪资:${resumeInfo.currentSalary || ''}
4. 职业建议150字以内
5. 竞争力评分0-100分`; 【求职期望】
期望职位:${resumeInfo.expectedPosition || ''}
期望薪资:${resumeInfo.expectedSalary || ''}
期望地点:${resumeInfo.expectedLocation || ''}
期望行业:${resumeInfo.expectedIndustry || ''}
【技能标签】
${skillsArray.length > 0 ? skillsArray.join('、') : '无'}
【个人优势描述】
${resumeInfo.skillDescription || ''}
【工作经历】
${workExpText || '无'}
【项目经历】
${projectExpText || '无'}
请从以下几个方面进行专业分析,并返回 JSON 格式结果:
1. skillTags: 核心技能标签数组提取5-10个最关键的技术技能Vue、React、Node.js等
2. strengths: 优势分析100字以内基于工作经历、项目经历、技能水平等综合评估
3. weaknesses: 劣势分析100字以内指出需要改进的地方或不足
4. careerSuggestion: 职业建议150字以内基于期望职位和当前能力给出职业发展建议
5. competitiveness: 竞争力评分0-100的整数综合考虑工作年限、技能深度、项目经验、学历等因素
要求:
- 竞争力评分要客观公正,基于实际能力评估
- 优势分析要突出核心亮点和技术能力
- 劣势分析要指出真实存在的问题
- 职业建议要具有针对性和可操作性`;
try {
// 调用AI服务进行分析 // 调用AI服务进行分析
const aiAnalysis = await aiService.analyzeResume(prompt); const aiAnalysis = await aiService.analyzeResume(prompt);
// 解析AI返回的结果 // 解析AI返回的结果如果AI没返回有效数据直接抛出错误
const analysis = this.parse_ai_analysis(aiAnalysis, resumeInfo); const analysis = this.parse_ai_analysis(aiAnalysis, resumeInfo);
if (!analysis) {
throw new Error('AI分析结果为空无法处理');
}
// 确保所有字段都有值 // 确保所有字段都有值
const updateData = { const updateData = {
aiSkillTags: JSON.stringify(analysis.skillTags || []), aiSkillTags: JSON.stringify(analysis.skillTags || []),
aiStrengths: analysis.strengths || '', aiStrengths: analysis.strengths || '',
aiWeaknesses: analysis.weaknesses || '', aiWeaknesses: analysis.weaknesses || '',
aiCareerSuggestion: analysis.careerSuggestion || '', aiCareerSuggestion: analysis.careerSuggestion || '',
aiCompetitiveness: parseInt(analysis.competitiveness || 70, 10) aiCompetitiveness: parseInt(analysis.competitiveness || 0, 10)
}; };
// 确保竞争力评分在 0-100 范围内 // 确保竞争力评分在 0-100 范围内
@@ -363,105 +440,44 @@ ${resumeInfo.skillDescription}
console.log(`[简历管理] AI分析完成 - 竞争力评分: ${updateData.aiCompetitiveness}, 技能标签: ${updateData.aiSkillTags}`); console.log(`[简历管理] AI分析完成 - 竞争力评分: ${updateData.aiCompetitiveness}, 技能标签: ${updateData.aiSkillTags}`);
return analysis; return analysis;
} catch (error) {
console.error(`[简历管理] AI分析失败:`, error, {
resumeId: resumeId,
fullName: resumeInfo.fullName
});
// 如果AI分析失败使用基于规则的默认分析并保存到数据库
const defaultAnalysis = this.get_default_analysis(resumeInfo);
// 保存默认分析结果到数据库
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分析结果
* @param {object} aiResponse - AI响应对象 * @param {object} aiResponse - AI响应对象
* @param {object} resumeInfo - 简历信息 * @param {object} resumeInfo - 简历信息
* @returns {object} 解析后的分析结果 * @returns {object|null} 解析后的分析结果如果AI没返回有效数据则返回null
*/ */
parse_ai_analysis(aiResponse, resumeInfo) { parse_ai_analysis(aiResponse, resumeInfo) {
try {
// aiService.analyzeResume 返回格式: { analysis: {...} } 或 { analysis: { content: "...", parseError: true } } // aiService.analyzeResume 返回格式: { analysis: {...} } 或 { analysis: { content: "...", parseError: true } }
const analysis = aiResponse.analysis; const analysis = aiResponse?.analysis;
// 如果解析失败analysis 会有 parseError 标记 // 如果解析失败analysis 会有 parseError 标记,直接返回 null
if (analysis && analysis.parseError) { if (!analysis || analysis.parseError) {
console.warn(`[简历管理] AI分析结果解析失败,使用默认分析`); console.warn(`[简历管理] AI分析结果解析失败或为空`);
return this.get_default_analysis(resumeInfo); return null;
} }
// 如果解析成功analysis 直接是解析后的对象 // 如果解析成功analysis 直接是解析后的对象
if (analysis && typeof analysis === 'object' && !analysis.parseError) { if (typeof analysis === 'object' && !analysis.parseError) {
// 检查是否有必要的字段
if (analysis.competitiveness === undefined && analysis.竞争力评分 === undefined) {
console.warn(`[简历管理] AI分析结果缺少竞争力评分字段`);
return null;
}
return { return {
skillTags: analysis.skillTags || analysis.技能标签 || [], skillTags: analysis.skillTags || analysis.技能标签 || [],
strengths: analysis.strengths || analysis.优势 || analysis.优势分析 || '', strengths: analysis.strengths || analysis.优势 || analysis.优势分析 || '',
weaknesses: analysis.weaknesses || analysis.劣势 || analysis.劣势分析 || '', weaknesses: analysis.weaknesses || analysis.劣势 || analysis.劣势分析 || '',
careerSuggestion: analysis.careerSuggestion || analysis.职业建议 || '', careerSuggestion: analysis.careerSuggestion || analysis.职业建议 || '',
competitiveness: parseInt(analysis.competitiveness || analysis.竞争力评分 || 70, 10) competitiveness: parseInt(analysis.competitiveness || analysis.竞争力评分 || 0, 10)
}; };
} }
// 如果 analysis 是字符串,尝试解析 // 如果格式不对,返回 null
const content = analysis?.content || analysis || ''; console.warn(`[简历管理] AI分析结果格式不正确`);
if (typeof content === 'string' && content.includes('{') && content.includes('}')) { return null;
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);
}
} }
/** /**

View File

@@ -156,23 +156,6 @@ class DeviceWorkStatusNotifier {
await mqttClient.publish(topic, message); 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) { } catch (error) {
// 通知失败不影响任务执行,只记录日志 // 通知失败不影响任务执行,只记录日志
console.warn(`[设备工作状态] 推送失败:`, error.message); console.warn(`[设备工作状态] 推送失败:`, error.message);