1
This commit is contained in:
@@ -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.data:XHR 监听结果数组,每项 data 为接口 JSON(zpData.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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user