This commit is contained in:
张成
2026-04-16 14:01:52 +08:00
parent 7ef0c68ad1
commit df0aacc782
10 changed files with 531 additions and 22 deletions

View File

@@ -5,7 +5,7 @@ const db = require('../../dbProxy');
/**
* 职位过滤引擎schedule 自动投递用)
* 本文件集中:过滤(薪资/关键词/活跃度/去重)+ 评分(权重分+关键词奖励)+ 按分数阈值筛。
* 自动投递只需调 filterAndScoreJobsForDeliver 一个方法
* 自动投递调 filterAndScoreJobsForDeliver,返回 { scored, skipReasonByJobId }
*/
class JobFilterEngine {
getJobKey(job) {
@@ -89,24 +89,82 @@ class JobFilterEngine {
return filtered;
}
/**
* 单条职位:判断 filterJobs 中哪一步未通过(用于写入 job_postings.deliver_failed_reason
*/
async get_single_job_filter_fail_reason(job, config) {
const j = job;
const after_salary = this.filterBySalary([j], config);
if (after_salary.length === 0) {
return `薪资不在设定范围(${config.min_salary ?? 0}-${config.max_salary ?? 0}K)`;
}
const after_title = this.filterByTitleIncludeKeywords(after_salary, config);
if (after_title.length === 0) {
const kws = (config.title_include_keywords || []).join('、') || '无';
return `职位标题须包含以下关键词之一:${kws}`;
}
const after_kw = this.filterByKeywords(after_title, config);
if (after_kw.length === 0) {
const match_text = `${j.jobTitle || ''}`;
const match_result = KeywordMatcher.match(match_text, {
excludeKeywords: Array.isArray(config.exclude_keywords) ? config.exclude_keywords : [],
filterKeywords: Array.isArray(config.filter_keywords) ? config.filter_keywords : [],
bonusKeywords: []
});
if (match_result && match_result.details && match_result.details.exclude && match_result.details.exclude.matched) {
const hit_keywords = match_result.details.exclude.keywords || [];
if (hit_keywords.length) {
return `命中排除关键词: ${hit_keywords.join('、')}`;
}
}
if (match_result && match_result.details && match_result.details.filter && !match_result.details.filter.matched) {
const needed = Array.isArray(config.filter_keywords) ? config.filter_keywords : [];
if (needed.length) {
return `未命中包含关键词: ${needed.join('、')}`;
}
}
return '命中排除关键词或未满足包含词规则';
}
if (config.filter_inactive_companies) {
const after_act = await this.filterByCompanyActivity(after_kw, config.company_active_days || 7);
if (after_act.length === 0) {
return '公司活跃度不满足配置';
}
}
if (config.deduplicate) {
const after_dedup = this.deduplicateJobs(after_kw);
if (after_dedup.length === 0) {
return '与列表内职位重复(公司+岗位名)';
}
}
return '未通过职位过滤';
}
/**
* 自动投递用:过滤 + 评分 + 按 60 分阈值筛,一次调用完成(便于阅读与维护)
* @returns {{ scored: Array, skipReasonByJobId: Record<number|string, string> }}
*/
async filterAndScoreJobsForDeliver(jobs, filterConfig, resume, accountConfig, jobTypeConfig, recentCompanies) {
const scored = [];
const skipReasonByJobId = {};
const jobDesc = (j) => `${j.companyName || '?'} / ${j.jobTitle || '?'}`;
const { jobFilterService } = require('../../job/services');
console.log(`[jobFilterEngine] filterAndScoreJobsForDeliver 开始,待处理: ${jobs.length}`);
for (const job of jobs) {
const job_id = job.id;
if (job.companyName && recentCompanies.has(job.companyName)) {
const msg = '近期已在配置天数内投递过该公司';
if (job_id != null) skipReasonByJobId[job_id] = msg;
console.log(`[jobFilterEngine] 已投递公司 剔除: ${jobDesc(job)}`);
continue;
}
const filtered = await this.filterJobs([job], filterConfig, resume);
if (filtered.length === 0) {
const reason = await this.get_single_job_filter_fail_reason(job, filterConfig);
if (job_id != null) skipReasonByJobId[job_id] = reason;
console.log(`[jobFilterEngine] 过滤条件不通过 剔除: ${jobDesc(job)}`);
continue;
}
@@ -126,6 +184,8 @@ class JobFilterEngine {
const finalScore = scoreResult.totalScore + keywordBonus.score;
if (finalScore < 60) {
const msg = `评分不足(总分${finalScore.toFixed(1)},需>=60)`;
if (job_id != null) skipReasonByJobId[job_id] = msg;
console.log(`[jobFilterEngine] 评分不足(>=60) 剔除: ${jobDesc(job)} 总分=${finalScore.toFixed(1)}`);
continue;
}
@@ -139,7 +199,7 @@ class JobFilterEngine {
scored.sort((a, b) => b.matchScore - a.matchScore);
console.log(`[jobFilterEngine] filterAndScoreJobsForDeliver 结束: 原始${jobs.length} 通过${scored.length}`);
return scored;
return { scored, skipReasonByJobId };
}
/**