This commit is contained in:
张成
2026-04-08 17:27:40 +08:00
parent f2a8e61016
commit 51bbdacdda
5 changed files with 201 additions and 80 deletions

View File

@@ -30,7 +30,7 @@ class DeliverHandler extends BaseHandler {
*/
async doDeliver(task) {
const { sn_code, taskParams } = task;
const { keyword, platform = 'boss', pageCount = 3, maxCount = 10, filterRules = {} } = taskParams;
const { keyword, platform = 'boss', pageCount = 3, maxCount = 10 } = taskParams;
console.log(`[自动投递] 开始 - 设备: ${sn_code}, 关键词: ${keyword}`);
@@ -103,8 +103,8 @@ class DeliverHandler extends BaseHandler {
};
}
// 8. 合并过滤配置
const filterConfig = this.mergeFilterConfig(deliverConfig, filterRules, jobTypeConfig);
// 8. 过滤配置仅来自职位类型 job_types排除词 / 标题须含词等),不与账号投递配置、任务参数混用
const filterConfig = this.mergeFilterConfig(jobTypeConfig);
// 9. 过滤已投递的公司repeat_deliver_days 由投递配置给出,缺省 30上限 365
const repeatDeliverDays = Math.min(365, Math.max(1, Number(deliverConfig.repeat_deliver_days) || 30));
@@ -120,6 +120,9 @@ class DeliverHandler extends BaseHandler {
recentCompanies
);
// 本轮未进入「可投递」列表的待投递记录,标记为已过滤,避免长期停留在 pending
await this.markFilteredJobsNotPassed(pendingJobs, filteredJobs, sn_code, platform);
const jobsToDeliver = filteredJobs.slice(0, actualMaxCount);
console.log(`[自动投递] 职位筛选完成 - 原始: ${pendingJobs.length}, 符合条件: ${filteredJobs.length}, 将投递: ${jobsToDeliver.length}`);
@@ -294,6 +297,44 @@ class DeliverHandler extends BaseHandler {
await command.executeCommands(taskId, [getJobListCommand], this.mqttClient);
}
/**
* 将本批中未通过过滤/评分的职位从 pending 更新为 filtered仍 pending 的仅为通过筛选且等待下轮投递的)
* @param {Array} pendingJobs - 本批拉取的待投递
* @param {Array} filteredJobs - filterAndScoreJobsForDeliver 通过的结果(含 matchScore
*/
async markFilteredJobsNotPassed(pendingJobs, filteredJobs, sn_code, platform) {
if (!pendingJobs || pendingJobs.length === 0) {
return;
}
const passedIds = new Set(
(filteredJobs || []).map((j) => j.id).filter((id) => id != null)
);
const notPassedIds = pendingJobs
.map((j) => (j && j.id != null ? j.id : null))
.filter((id) => id != null && !passedIds.has(id));
if (notPassedIds.length === 0) {
return;
}
const job_postings = db.getModel('job_postings');
const { op } = db.models;
try {
const [n] = await job_postings.update(
{ applyStatus: 'filtered' },
{
where: {
id: { [op.in]: notPassedIds },
sn_code,
platform,
applyStatus: 'pending'
}
}
);
console.log(`[自动投递] 不符合条件已标记 filtered: ${notPassedIds.length} 条(更新行数 ${n}`);
} catch (e) {
console.warn('[自动投递] 标记 filtered 失败:', e.message);
}
}
/**
* 获取待投递职位
*/
@@ -314,42 +355,42 @@ class DeliverHandler extends BaseHandler {
}
/**
* 合并过滤配置
* 自动投递过滤配置:仅使用 job_typesexcludeKeywords、titleIncludeKeywords
* 薪资筛选不在此合并min/max 为 0 表示不做薪资过滤);评分权重仍走 accountConfig.is_salary_priority
*/
mergeFilterConfig(deliverConfig, filterRules, jobTypeConfig) {
// 排除关键词
const rawJobTypeExclude = jobTypeConfig?.excludeKeywords
? ConfigManager.parseConfig(jobTypeConfig.excludeKeywords, [])
: [];
mergeFilterConfig(jobTypeConfig) {
const base = {
exclude_keywords: [],
filter_keywords: [],
title_include_keywords: [],
min_salary: 0,
max_salary: 0,
priority_weights: []
};
const jobTypeExclude = Array.isArray(rawJobTypeExclude) ? rawJobTypeExclude : [];
if (!jobTypeConfig) {
return base;
}
const deliverExcludeRaw = ConfigManager.getExcludeKeywords(deliverConfig);
const deliverExclude = Array.isArray(deliverExcludeRaw) ? deliverExcludeRaw : [];
const filterExcludeRaw = filterRules.excludeKeywords || [];
const filterExclude = Array.isArray(filterExcludeRaw) ? filterExcludeRaw : [];
if (jobTypeConfig.excludeKeywords) {
try {
const raw = jobTypeConfig.excludeKeywords;
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
base.exclude_keywords = Array.isArray(parsed) ? parsed.map((k) => String(k || '').trim()).filter(Boolean) : [];
} catch (e) {
base.exclude_keywords = [];
}
}
// 过滤关键词
const deliverFilterRaw = ConfigManager.getFilterKeywords(deliverConfig);
const deliverFilter = Array.isArray(deliverFilterRaw) ? deliverFilterRaw : [];
const filterKeywordsRaw = filterRules.keywords || [];
const filterKeywords = Array.isArray(filterKeywordsRaw) ? filterKeywordsRaw : [];
// 薪资范围
const salaryRange = filterRules.minSalary || filterRules.maxSalary
? { min: filterRules.minSalary || 0, max: filterRules.maxSalary || 0 }
: ConfigManager.getSalaryRange(deliverConfig);
let title_include_keywords = [];
if (jobTypeConfig && jobTypeConfig.titleIncludeKeywords != null) {
if (jobTypeConfig.titleIncludeKeywords != null) {
const v = jobTypeConfig.titleIncludeKeywords;
if (Array.isArray(v)) {
title_include_keywords = v.map((k) => String(k || '').trim()).filter(Boolean);
base.title_include_keywords = v.map((k) => String(k || '').trim()).filter(Boolean);
} else if (typeof v === 'string' && v.trim()) {
try {
const p = JSON.parse(v);
if (Array.isArray(p)) {
title_include_keywords = p.map((k) => String(k || '').trim()).filter(Boolean);
base.title_include_keywords = p.map((k) => String(k || '').trim()).filter(Boolean);
}
} catch (e) {
/* ignore */
@@ -357,14 +398,7 @@ class DeliverHandler extends BaseHandler {
}
}
return {
exclude_keywords: [...jobTypeExclude, ...deliverExclude, ...filterExclude],
filter_keywords: filterKeywords.length > 0 ? filterKeywords : deliverFilter,
title_include_keywords,
min_salary: salaryRange.min,
max_salary: salaryRange.max,
priority_weights: ConfigManager.getPriorityWeights(deliverConfig)
};
return base;
}
/**