This commit is contained in:
张成
2026-04-08 15:00:49 +08:00
parent 21fe005c19
commit bfd39eddcf
2 changed files with 106 additions and 163 deletions

View File

@@ -1,3 +1,4 @@
const { Op } = require('sequelize');
const aiService = require('../../../services/ai_service');
const { jobFilterService } = require('../services');
const locationService = require('../../../services/locationService');
@@ -5,6 +6,7 @@ const logs = require('../../logProxy');
const db = require('../../dbProxy');
const { v4: uuidv4 } = require('uuid');
/**
* 工作管理模块
* 负责简历获取、分析、存储和匹配度计算
@@ -14,13 +16,19 @@ class JobManager {
}
/**
* 设备 get_job_list 返回的 response.dataXHR 监听结果数组,每项 data 为接口 JSONzpData.jobList
* 解析设备 get_job_list / search_job_list 等返回的 response.data。
* 实际形态为「多页 XHR 监听结果」数组,与客户端一致,例如:
* `[{ url, method, status, data: { code: 0, message, zpData: { hasMore, jobList: [...] } } }, ...]`
* pageCount=3 时通常 3 条(每页一次 list.json本方法将所有页的 jobList 合并为一维数组。
*/
_jobListFromRecommendMonitorData(responseData) {
if (!Array.isArray(responseData)) return [];
const jobs = [];
for (const item of responseData) {
const list = item?.data?.zpData?.jobList;
const inner = item?.data;
if (!inner || typeof inner !== 'object') continue;
if (inner.code !== undefined && inner.code !== 0) continue;
const list = inner.zpData?.jobList;
if (Array.isArray(list)) jobs.push(...list);
}
return jobs;
@@ -164,8 +172,8 @@ class JobManager {
* @returns {Promise<object>} 搜索结果
*/
async search_jobs_with_params(sn_code, mqttClient, params = {}) {
const {
keyword = '前端',
const {
keyword = '前端',
platform = 'boss',
city = '',
cityName = '',
@@ -217,11 +225,9 @@ class JobManager {
console.log(`[工作管理] 成功获取岗位数据,共 ${jobs.length} 个岗位`);
// 保存职位到数据库
try {
await this.saveJobsToDatabase(sn_code, platform, keyword, jobs);
} catch (error) {
console.error(`[工作管理] 保存职位到数据库失败:`, error);
}
await this.saveJobsToDatabase(sn_code, platform, keyword, jobs);
return {
jobs: jobs,
@@ -239,7 +245,7 @@ class JobManager {
* @returns {Promise<object>} 执行结果
*/
async search_and_deliver(sn_code, mqttClient, params = {}) {
const {
const {
keyword,
searchParams = {},
pageCount = 3,
@@ -352,8 +358,8 @@ class JobManager {
let securityId = jobData.securityId || '';
try {
if (jobData.originalData) {
const originalData = typeof jobData.originalData === 'string'
? JSON.parse(jobData.originalData)
const originalData = typeof jobData.originalData === 'string'
? JSON.parse(jobData.originalData)
: jobData.originalData;
securityId = originalData.securityId || securityId;
}
@@ -419,82 +425,27 @@ class JobManager {
}
/**
* 获取岗位列表(支持多条件搜索
* @param {string} sn_code - 设备SN
* @param {object} mqttClient - MQTT客户端
* @param {object} params - 参数
* 获取岗位列表(与客户端/Electron 约定一致
* @param {string} sn_code - 设备 SN
* @param {object} mqttClient - MQTT 客户端
* @param {object} params - { platform?, pageCount?, tabLabel?, keyword? }keyword 仅服务端入库用,下发设备只有 pageCount + tabLabel
* @returns {Promise<object>} 岗位列表
*/
async get_job_list(sn_code, mqttClient, params = {}) {
const {
platform = 'boss',
pageCount = 3,
city = '',
cityName = '',
salary = '',
experience = '',
education = '',
industry = '',
companySize = '',
financingStage = '',
page = 1,
pageSize = 20,
tabLabel
} = params;
// 入库用:可与前端一致带 keyword设备端只收 pageCount + tabLabel与 Electron get_job_list 一致)
const { platform = 'boss', pageCount = 3, tabLabel } = params;
const keyword = String(params.keyword || '').trim() || String(tabLabel || '').trim();
const multiKeys = ['city', 'cityName', 'salary', 'experience', 'education', 'industry', 'companySize', 'financingStage'];
const hasMultiParams = multiKeys.some((k) => params[k]) || params.page !== undefined || params.pageSize !== undefined;
const devicePayload = () => ({
const data = {
pageCount,
...(String(tabLabel || '').trim() ? { tabLabel: String(tabLabel).trim() } : {})
});
};
if (hasMultiParams) {
// 使用多条件搜索逻辑
console.log(`[工作管理] 开始多条件搜索设备 ${sn_code} 的职位,关键词: ${keyword}, 城市: ${cityName || city}`);
// 通过MQTT指令获取岗位列表保持action不变前端已使用
const response = await mqttClient.publishAndWait(sn_code, {
platform,
action: "get_job_list", // 保持与原有get_job_list相同的action前端已使用
data: devicePayload()
});
if (!response || response.code !== 200) {
console.error(`[工作管理] 多条件搜索职位失败:`, response);
throw new Error('多条件搜索职位失败');
}
const jobs = this._jobListFromRecommendMonitorData(response.data);
console.log(`[工作管理] 成功获取岗位数据,共 ${jobs.length} 个岗位`);
// 保存职位到数据库
try {
await this.saveJobsToDatabase(sn_code, platform, keyword, jobs);
} catch (error) {
console.error(`[工作管理] 保存职位到数据库失败:`, error);
}
return {
jobs: jobs,
keyword: keyword,
platform: platform,
count: jobs.length
};
}
// 简单搜索逻辑(保持原有逻辑)
console.log(`[工作管理] 开始获取设备 ${sn_code} 的岗位列表,关键词: ${keyword}`);
console.log(`[工作管理] get_job_list ${sn_code} → 设备`, data, `入库 keyword=${keyword}`);
const response = await mqttClient.publishAndWait(sn_code, {
platform,
action: "get_job_list",
data: devicePayload()
action: 'get_job_list',
data
});
if (!response || response.code !== 200) {
@@ -505,22 +456,16 @@ class JobManager {
const jobs = this._jobListFromRecommendMonitorData(response.data);
console.log(`[工作管理] 成功获取岗位数据,共 ${jobs.length} 个岗位`);
// 保存职位到数据库
try {
await this.saveJobsToDatabase(sn_code, platform, keyword, jobs);
} catch (error) {
console.error(`[工作管理] 保存职位到数据库失败:`, error);
// 不影响主流程,继续返回数据
}
const result = {
jobs: jobs,
keyword: keyword,
platform: platform,
await this.saveJobsToDatabase(sn_code, platform, keyword, jobs);
return {
jobs,
keyword,
platform,
count: jobs.length
};
return result;
}
/**
@@ -536,89 +481,93 @@ class JobManager {
console.log(`[工作管理] 开始保存 ${jobs.length} 个职位到数据库`);
for (const job of jobs) {
try {
// 构建职位信息对象
const jobInfo = {
sn_code,
platform,
keyword,
// Boss直聘字段映射
encryptBossId: job.encryptBossId || '',
jobId: job.encryptJobId || '',
jobTitle: job.jobName || '',
companyId: job.encryptBrandId || '',
companyName: job.brandName || '',
companySize: job.brandScaleName || '',
companyIndustry: job.brandIndustry || '',
salary: job.salaryDesc || '',
// 构建职位信息对象
const jobInfo = {
sn_code,
platform,
keyword,
// 岗位要求(从 jobLabels 和 skills 提取)
jobRequirements: JSON.stringify({
experience: job.jobExperience || '',
education: job.jobDegree || '',
labels: job.jobLabels || [],
skills: job.skills || []
}),
// 工作地点
location: [job.cityName, job.areaDistrict, job.businessDistrict]
.filter(Boolean).join(' '),
// Boss直聘字段映射
encryptBossId: job.encryptBossId || '',
jobId: job.encryptJobId || '',
jobTitle: job.jobName || '',
companyId: job.encryptBrandId || '',
companyName: job.brandName || '',
companySize: job.brandScaleName || '',
companyIndustry: job.brandIndustry || '',
salary: job.salaryDesc || '',
// 岗位要求(从 jobLabels 和 skills 提取)
jobRequirements: JSON.stringify({
experience: job.jobExperience || '',
education: job.jobDegree || '',
labels: job.jobLabels || [],
skills: job.skills || []
}),
// 原始数据
originalData: JSON.stringify(job),
// 工作地点
location: [job.cityName, job.areaDistrict, job.businessDistrict]
.filter(Boolean).join(' '),
// 默认状态
applyStatus: 'pending',
chatStatus: 'none'
};
experience: job.jobExperience || '',
education: job.jobDegree || '',
// 调用位置服务解析 location + companyName 获取坐标
if (jobInfo.location && jobInfo.companyName) {
const addressToParse = `${jobInfo.location.replaceAll(' ', '')}${jobInfo.companyName}`;
// 原始数据
originalData: JSON.stringify(job),
// 默认状态
applyStatus: 'pending',
chatStatus: 'none'
};
// 调用位置服务解析 location + companyName 获取坐标
if (jobInfo.location && jobInfo.companyName) {
const addressToParse = `${jobInfo.location.replaceAll(' ', '')}${jobInfo.companyName}`;
// 等待 1秒
// await new Promise(resolve => setTimeout(resolve, 1000));
// 等待 1秒
// await new Promise(resolve => setTimeout(resolve, 1000));
// const location = await locationService.getLocationByAddress(addressToParse).catch(error => {
// console.error(`[工作管理] 获取位置失败:`, error);
// });
// const location = await locationService.getLocationByAddress(addressToParse).catch(error => {
// console.error(`[工作管理] 获取位置失败:`, error);
// });
// if (location) {
// jobInfo.latitude = String(location.lat);
// jobInfo.longitude = String(location.lng);
// }
}
// if (location) {
// jobInfo.latitude = String(location.lat);
// jobInfo.longitude = String(location.lng);
// }
// 创建新职位 重复投递时间 从 pla_account 中获取pla_account 列为 platform_type不是 platform
const pla_account = db.getModel('pla_account');
const account = await pla_account.findOne({
where: {
sn_code: sn_code,
platform_type: platform
}
});
// 检查是否已存在(根据 jobId 和 sn_code
const existingJob = await job_postings.findOne({
where: {
jobId: jobInfo.jobId,
sn_code: sn_code
let repeatDeliverDays = 30;
if (account) {
let dc = account.deliver_config?.repeat_deliver_days||30;
repeatDeliverDays = Number(dc);
}
const existingJob = await job_postings.findOne({
where: {
jobId: jobInfo.jobId,
sn_code: sn_code,
last_modify_time: {
[Op.gte]: new Date(Date.now() - repeatDeliverDays * 24 * 60 * 60 * 1000)
}
});
if (existingJob) {
// 更新现有职位
await job_postings.update(jobInfo, {
where: {
jobId: jobInfo.jobId,
sn_code: sn_code
}
});
console.log(`[工作管理] 职位已更新 - ${jobInfo.jobTitle} @ ${jobInfo.companyName}`);
} else {
// 创建新职位
await job_postings.create(jobInfo);
console.log(`[工作管理] 职位已创建 - ${jobInfo.jobTitle} @ ${jobInfo.companyName}`);
}
} catch (error) {
console.error(`[工作管理] 保存职位失败:`, error, job);
// 继续处理下一个职位
});
if (existingJob) {
await job_postings.update(jobInfo, { where: { id: existingJob.id } });
} else {
await job_postings.create(jobInfo);
console.log(`[工作管理] 职位已创建 - ${jobInfo.jobTitle} @ ${jobInfo.companyName}`);
}
}

View File

@@ -21,12 +21,6 @@ module.exports = (db) => {
allowNull: true,
defaultValue: ''
},
platform_type: {
comment: '平台',
type: Sequelize.STRING(50),
allowNull: false,
defaultValue: ''
},
login_name: {
comment: '登录名',
type: Sequelize.STRING(50),