修改竞争力分析
This commit is contained in:
@@ -14,9 +14,12 @@ import com.ruoyi.cms.mapper.AppUserMapper;
|
||||
import com.ruoyi.cms.mapper.JobApplyMapper;
|
||||
import com.ruoyi.cms.mapper.JobMapper;
|
||||
import com.ruoyi.cms.service.IBussinessDictTypeService;
|
||||
import com.ruoyi.cms.util.RoleUtils;
|
||||
import com.ruoyi.cms.util.StringUtil;
|
||||
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -186,22 +189,6 @@ public class JobCollectionServiceImpl extends ServiceImpl<JobCollectionMapper,Jo
|
||||
throw new RuntimeException("岗位不存在");
|
||||
}
|
||||
|
||||
// 获取所有申请该岗位的用户
|
||||
List<AppUser> appUsers = appUserMapper.selectByJobId(jobId);
|
||||
Long applyCount = (long) appUsers.size();
|
||||
|
||||
if (appUsers.isEmpty()) {
|
||||
// 没有人申请,返回默认值或0
|
||||
CompetitivenessResponse emptyResponse = new CompetitivenessResponse();
|
||||
emptyResponse.setTotalApplicants(0);
|
||||
emptyResponse.setMatchScore(0);
|
||||
emptyResponse.setRank(0);
|
||||
emptyResponse.setPercentile(0);
|
||||
RadarChart radarChart = new RadarChart();
|
||||
emptyResponse.setRadarChart(radarChart);
|
||||
return emptyResponse;
|
||||
}
|
||||
|
||||
// ================== 数据字典映射(用于排序比较)==================
|
||||
// 假设你有字典工具类,如 DictUtils.getSort("education", value) 返回排序值
|
||||
// 这里我们用 Map 模拟字典排序(越小要求越低)
|
||||
@@ -212,81 +199,53 @@ public class JobCollectionServiceImpl extends ServiceImpl<JobCollectionMapper,Jo
|
||||
// 构建学历排序映射:dictLabel -> dictSort(越小要求越低)
|
||||
Map<String, Integer> educationRank = educationDict.stream()
|
||||
.collect(Collectors.toMap(
|
||||
BussinessDictData::getDictLabel,
|
||||
BussinessDictData::getDictValue,
|
||||
data -> Math.toIntExact(data.getDictSort()),
|
||||
(a, b) -> a // 若有重复 key,保留第一个
|
||||
));
|
||||
|
||||
Map<String, Integer> experienceRank = experienceDict.stream()
|
||||
.collect(Collectors.toMap(
|
||||
BussinessDictData::getDictLabel,
|
||||
BussinessDictData::getDictValue,
|
||||
data -> Math.toIntExact(data.getDictSort()),
|
||||
(a, b) -> a
|
||||
));
|
||||
|
||||
// ================== 提取岗位要求 ==================
|
||||
Integer jobEducationRank = educationRank.getOrDefault(job.getEducation(), 999);
|
||||
Integer jobExperienceRank = experienceRank.getOrDefault(job.getExperience(), 999);
|
||||
Integer jobMinSalary = job.getMinSalary() != null ? job.getMinSalary().intValue() : 0;
|
||||
String jobLocation = job.getJobLocation();
|
||||
// 获取所有申请该岗位的用户
|
||||
List<AppUser> appUsers = appUserMapper.selectByJobId(jobId);
|
||||
appUsers = appUsers == null ? new ArrayList<>() : appUsers;
|
||||
Long applyCount = (long) appUsers.size();
|
||||
|
||||
//获取求职者状态
|
||||
Long loginUserId = RoleUtils.getAppUserId();
|
||||
boolean isLogin = loginUserId != null;
|
||||
AppUser currentAppUser = new AppUser();
|
||||
if (isLogin) {
|
||||
AppUser tempUser = appUserMapper.selectById(loginUserId);
|
||||
if (tempUser != null && !StringUtil.IS_COMPANY_USER.equals(tempUser.getIsCompanyUser())) {
|
||||
currentAppUser = tempUser;
|
||||
}
|
||||
}
|
||||
|
||||
if (appUsers.isEmpty()) {
|
||||
// 没有人申请,返回默认值或0
|
||||
CompetitivenessResponse emptyResponse = new CompetitivenessResponse();
|
||||
emptyResponse.setTotalApplicants(0);
|
||||
//计算匹配度
|
||||
int realMatchScore = isLogin && !StringUtil.IS_COMPANY_USER.equals(currentAppUser.getIsCompanyUser())? calculateUserMatchScore(currentAppUser, job, educationRank, experienceRank): 0;
|
||||
emptyResponse.setMatchScore(realMatchScore);
|
||||
emptyResponse.setRank(0);
|
||||
emptyResponse.setPercentile(0);
|
||||
emptyResponse.setRadarChart(buildUserRadarChart(currentAppUser,job,educationRank,experienceRank));
|
||||
return emptyResponse;
|
||||
}
|
||||
|
||||
// ================== 聚合用户数据用于分析 ==================
|
||||
List<UserCompetitiveness> userScores = new ArrayList<>();
|
||||
|
||||
for (AppUser user : appUsers) {
|
||||
int matchScore = 0;
|
||||
int totalFactors = 6; // 年龄、经验、学历、技能(暂用期望岗位匹配)、薪资、地点
|
||||
|
||||
// 1. 学历匹配(越接近越好)
|
||||
Integer userEduRank = educationRank.getOrDefault(user.getEducation(), 999);
|
||||
if (userEduRank <= jobEducationRank) {
|
||||
matchScore += 1; // 达标
|
||||
} else if (userEduRank == jobEducationRank + 1) {
|
||||
matchScore += 0.5; // 略高也算匹配
|
||||
}
|
||||
|
||||
// 2. 经验匹配
|
||||
Integer userExpRank = experienceRank.getOrDefault(user.getExperience(), 999);
|
||||
if (userExpRank >= jobExperienceRank) {
|
||||
matchScore += 1; // 满足经验要求
|
||||
}
|
||||
|
||||
// 3. 薪资匹配(用户期望 <= 岗位最大薪资,且不低于最小太多)
|
||||
Integer userMinSalary = parseSalary(user.getSalaryMin());
|
||||
if (userMinSalary != null && userMinSalary <= job.getMaxSalary()) {
|
||||
if (userMinSalary >= job.getMinSalary()) {
|
||||
matchScore += 1;
|
||||
} else if (userMinSalary >= job.getMinSalary() * 0.8) {
|
||||
matchScore += 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 地点匹配
|
||||
if (user.getArea() != null) {
|
||||
if(user.getArea().contains(jobLocation) || jobLocation.contains(user.getArea())){
|
||||
matchScore += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 年龄估算(从生日计算)
|
||||
int userAge = getUserAge(DateUtils.stringToDateWithYmd(user.getBirthDate(),DateUtils.YYYY_MM_DD));
|
||||
// 假设最佳年龄区间为 22-35,越接近越匹配
|
||||
if (userAge >= 22 && userAge <= 35) {
|
||||
matchScore += 1;
|
||||
} else if (userAge >= 18 && userAge <= 45) {
|
||||
matchScore += 0.5;
|
||||
}
|
||||
|
||||
// 6. 技能/岗位匹配(简化:期望岗位是否包含该岗位关键词)
|
||||
boolean jobMatch = user.getJobTitle() != null &&
|
||||
user.getJobTitle().stream().anyMatch(title -> title.contains(job.getJobTitle()) || job.getJobTitle().contains(title));
|
||||
if (jobMatch) {
|
||||
matchScore += 1;
|
||||
}
|
||||
|
||||
// 匹配度:0-6 → 映射为 0-100 分
|
||||
int finalScore = (int) Math.round((matchScore / (double) totalFactors) * 100);
|
||||
userScores.add(new UserCompetitiveness(user, finalScore));
|
||||
int matchScore = calculateUserMatchScore(user, job, educationRank, experienceRank);
|
||||
userScores.add(new UserCompetitiveness(user, matchScore));
|
||||
}
|
||||
|
||||
// 按匹配度降序
|
||||
@@ -314,19 +273,128 @@ public class JobCollectionServiceImpl extends ServiceImpl<JobCollectionMapper,Jo
|
||||
response.setTotalApplicants(Math.toIntExact(applyCount));
|
||||
|
||||
// Top 用户的匹配分
|
||||
int topMatchScore = userScores.get(0).getScore();
|
||||
/*int topMatchScore = userScores.get(0).getScore();
|
||||
response.setMatchScore(topMatchScore);
|
||||
|
||||
// 排名(Top1)
|
||||
response.setRank(1);
|
||||
|
||||
// 百分位:top用户超过多少人?(100%)
|
||||
response.setPercentile(100);
|
||||
|
||||
// 排名(Top1)
|
||||
response.setRank(1);*/
|
||||
if (isLogin && !StringUtil.IS_COMPANY_USER.equals(currentAppUser.getIsCompanyUser())) {
|
||||
int userMatchScore = calculateUserMatchScore(currentAppUser, job, educationRank, experienceRank);
|
||||
response.setMatchScore(userMatchScore);
|
||||
int userRank = 1;
|
||||
for (UserCompetitiveness uc : userScores) {
|
||||
if (uc.getScore() > userMatchScore) {
|
||||
userRank++;
|
||||
}
|
||||
}
|
||||
response.setRank(userRank);
|
||||
int percentile = (int) Math.round(((applyCount - userRank) * 1.0 / applyCount) * 100);
|
||||
response.setPercentile(percentile);
|
||||
} else {
|
||||
response.setMatchScore(0);
|
||||
response.setRank(0);
|
||||
response.setPercentile(0);
|
||||
}
|
||||
response.setRadarChart(radarChart);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建雷达图
|
||||
* @param user
|
||||
* @param job
|
||||
* @param educationRank
|
||||
* @param experienceRank
|
||||
* @return
|
||||
*/
|
||||
private RadarChart buildUserRadarChart(AppUser user, Job job, Map<String, Integer> educationRank, Map<String, Integer> experienceRank) {
|
||||
RadarChart radarChart = new RadarChart();
|
||||
int ageScore = getAgeScore(DateUtils.stringToDateWithYmd(user.getBirthDate(),DateUtils.YYYY_MM_DD));
|
||||
int expScore = getExperienceScore(user.getExperience(), job.getExperience(), experienceRank);
|
||||
int eduScore = getEducationScore(user.getEducation(), job.getEducation(), educationRank);
|
||||
int skillScore = getSkillScore(user, job);
|
||||
int salaryScore = getSalaryScore(user, job);
|
||||
int locScore = getLocationScore(user, job);
|
||||
// 赋值雷达图
|
||||
radarChart.setAge(ageScore);
|
||||
radarChart.setExperience(expScore);
|
||||
radarChart.setEducation(eduScore);
|
||||
radarChart.setSkill(skillScore);
|
||||
radarChart.setSalary(salaryScore);
|
||||
radarChart.setLocation(locScore);
|
||||
return radarChart;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建匹配度
|
||||
* @param user
|
||||
* @param job
|
||||
* @param educationRank
|
||||
* @param experienceRank
|
||||
* @return
|
||||
*/
|
||||
private int calculateUserMatchScore(AppUser user, Job job, Map<String, Integer> educationRank, Map<String, Integer> experienceRank) {
|
||||
int matchScore = 0;
|
||||
int totalFactors = 6;
|
||||
Integer userEduRank = educationRank.getOrDefault(user.getEducation(), 999);
|
||||
Integer jobEduRank = educationRank.getOrDefault(job.getEducation(), 999);
|
||||
//判断学历
|
||||
if (userEduRank <= jobEduRank) {
|
||||
matchScore += 1;
|
||||
} else if (userEduRank == jobEduRank + 1) {
|
||||
matchScore += 0.5;
|
||||
}
|
||||
//判断工作经验
|
||||
Integer userExpRank = experienceRank.getOrDefault(user.getExperience(), 999);
|
||||
Integer jobExpRank = experienceRank.getOrDefault(job.getExperience(), 999);
|
||||
if (userExpRank >= jobExpRank) {
|
||||
matchScore += 1;
|
||||
}
|
||||
//判断薪资
|
||||
Integer userMinSalary = parseSalary(user.getSalaryMin());
|
||||
if (userMinSalary != null && userMinSalary <= job.getMaxSalary()) {
|
||||
if (userMinSalary >= job.getMinSalary()) {
|
||||
matchScore += 1;
|
||||
} else if (userMinSalary >= job.getMinSalary() * 0.8) {
|
||||
matchScore += 0.5;
|
||||
}
|
||||
}
|
||||
//判断区域
|
||||
if (user.getArea() != null && job.getJobLocationAreaCode() != null) {
|
||||
//if(user.getArea().contains(job.getJobLocation()) || job.getJobLocation().contains(user.getArea())){
|
||||
String userArea = user.getArea().trim();
|
||||
String jobArea = job.getJobLocationAreaCode().trim();
|
||||
if(userArea.equals(jobArea)){
|
||||
matchScore += 1;
|
||||
}
|
||||
}
|
||||
//判断年龄
|
||||
if (!StringUtil.IS_COMPANY_USER.equals(user.getIsCompanyUser())) {
|
||||
int userAge = 0;
|
||||
String birthDate = user.getBirthDate();
|
||||
if (StringUtils.isNotBlank(birthDate)) {
|
||||
userAge = getUserAge(DateUtils.stringToDateWithYmd(birthDate, DateUtils.YYYY_MM_DD));
|
||||
}else if (StringUtils.isNotBlank(user.getIdCard())) {
|
||||
birthDate = StringUtil.getBirthDateFromIdCard(user.getIdCard());
|
||||
userAge = getUserAge(DateUtils.stringToDateWithYmd(birthDate, DateUtils.YYYY_MM_DD));
|
||||
}
|
||||
if (userAge >= 22 && userAge <= 35) {
|
||||
matchScore += 1;
|
||||
} else if (userAge >= 18 && userAge <= 45) {
|
||||
matchScore += 0.5;
|
||||
}
|
||||
}
|
||||
//判断岗位
|
||||
boolean jobMatch = user.getJobTitle() != null &&
|
||||
user.getJobTitle().stream().anyMatch(title -> title.contains(job.getJobTitle()) || job.getJobTitle().contains(title));
|
||||
if (jobMatch) {
|
||||
matchScore += 1;
|
||||
}
|
||||
return (int) Math.round((matchScore / (double) totalFactors) * 100);
|
||||
}
|
||||
|
||||
private int getUserAge(Date birthDate) {
|
||||
if (birthDate == null) return 0;
|
||||
try {
|
||||
|
||||
@@ -74,4 +74,26 @@ public class RoleUtils {
|
||||
}
|
||||
return appUser.getIsCompanyUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取userId
|
||||
* @return
|
||||
*/
|
||||
public static Long getAppUserId(){
|
||||
try {
|
||||
Long userId=null;
|
||||
LoginSiteUser loginSiteUser = SiteSecurityUtils.getLoginUser();
|
||||
AppUser appUser = loginSiteUser.getUser();
|
||||
if (appUser != null) {
|
||||
userId=appUser.getUserId();
|
||||
}else{
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
SysUser sysUser = loginUser.getUser();
|
||||
userId=sysUser.getAppUserId();
|
||||
}
|
||||
return userId;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user