This commit is contained in:
张成
2025-12-30 15:46:18 +08:00
parent d14f89e008
commit 65833dd32d
29 changed files with 2416 additions and 1048 deletions

View File

@@ -0,0 +1,126 @@
/**
* 薪资解析工具
* 统一处理职位薪资和期望薪资的解析逻辑
*/
class SalaryParser {
/**
* 解析薪资范围字符串
* @param {string} salaryDesc - 薪资描述 (如 "15-20K", "8000-12000元")
* @returns {{ min: number, max: number }} 薪资范围(单位:元)
*/
static parse(salaryDesc) {
if (!salaryDesc || typeof salaryDesc !== 'string') {
return { min: 0, max: 0 };
}
// 尝试各种格式
return this.parseK(salaryDesc)
|| this.parseYuan(salaryDesc)
|| this.parseMixed(salaryDesc)
|| { min: 0, max: 0 };
}
/**
* 解析 K 格式薪资 (如 "15-20K", "8-12k")
*/
static parseK(desc) {
const kMatch = desc.match(/(\d+)[-~](\d+)[kK千]/);
if (kMatch) {
return {
min: parseInt(kMatch[1]) * 1000,
max: parseInt(kMatch[2]) * 1000
};
}
return null;
}
/**
* 解析元格式薪资 (如 "8000-12000元", "15000-20000")
*/
static parseYuan(desc) {
const yuanMatch = desc.match(/(\d+)[-~](\d+)元?/);
if (yuanMatch) {
return {
min: parseInt(yuanMatch[1]),
max: parseInt(yuanMatch[2])
};
}
return null;
}
/**
* 解析混合格式 (如 "8k-12000元")
*/
static parseMixed(desc) {
const mixedMatch = desc.match(/(\d+)[kK千][-~](\d+)元?/);
if (mixedMatch) {
return {
min: parseInt(mixedMatch[1]) * 1000,
max: parseInt(mixedMatch[2])
};
}
return null;
}
/**
* 检查职位薪资是否在期望范围内
* @param {object} jobSalary - 职位薪资 { min, max }
* @param {number} minExpected - 期望最低薪资
* @param {number} maxExpected - 期望最高薪资
*/
static isWithinRange(jobSalary, minExpected, maxExpected) {
if (!jobSalary || jobSalary.min === 0) {
return true; // 无法判断时默认通过
}
// 职位最高薪资 >= 期望最低薪资
if (minExpected > 0 && jobSalary.max < minExpected) {
return false;
}
// 职位最低薪资 <= 期望最高薪资
if (maxExpected > 0 && jobSalary.min > maxExpected) {
return false;
}
return true;
}
/**
* 计算薪资匹配度(用于职位评分)
* @param {object} jobSalary - 职位薪资
* @param {object} expectedSalary - 期望薪资
* @returns {number} 匹配度 0-1
*/
static calculateMatch(jobSalary, expectedSalary) {
if (!jobSalary || !expectedSalary || jobSalary.min === 0 || expectedSalary.min === 0) {
return 0.5; // 无法判断时返回中性值
}
const jobAvg = (jobSalary.min + jobSalary.max) / 2;
const expectedAvg = (expectedSalary.min + expectedSalary.max) / 2;
const diff = Math.abs(jobAvg - expectedAvg);
const range = (jobSalary.max - jobSalary.min + expectedSalary.max - expectedSalary.min) / 2;
// 差距越小,匹配度越高
return Math.max(0, 1 - diff / (range || 1));
}
/**
* 格式化薪资显示
* @param {object} salary - 薪资对象 { min, max }
* @returns {string} 格式化字符串
*/
static format(salary) {
if (!salary || salary.min === 0) {
return '面议';
}
const minK = (salary.min / 1000).toFixed(0);
const maxK = (salary.max / 1000).toFixed(0);
return `${minK}-${maxK}K`;
}
}
module.exports = SalaryParser;