岗位列表接口
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package org.jiayunet.controller;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.jiayunet.pojo.PageResult;
|
||||
import org.jiayunet.pojo.dto.job.JobDto;
|
||||
import org.jiayunet.pojo.param.job.JobQueryParam;
|
||||
import org.jiayunet.service.JobService;
|
||||
import org.jiayunet.tool.UserSecurityTool;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 岗位接口
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/job")
|
||||
@AllArgsConstructor
|
||||
public class JobController {
|
||||
|
||||
private final JobService jobService;
|
||||
|
||||
/**
|
||||
* 岗位列表查询
|
||||
* <p>支持按地区/岗位类型/行业/工作类型筛选,自动排除不感兴趣的岗位/公司/地区/行业,返回匹配度和收藏状态</p>
|
||||
*/
|
||||
@PostMapping("/list")
|
||||
public PageResult<JobDto> listJobs(@Validated @RequestBody JobQueryParam param) {
|
||||
Long userId = UserSecurityTool.getUserId();
|
||||
return jobService.listJobs(param, userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.jiayunet.pojo.dto.job;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 岗位列表出参
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
public class JobDto {
|
||||
|
||||
/** 岗位ID */
|
||||
private Long id;
|
||||
|
||||
/** 岗位名称 */
|
||||
private String title;
|
||||
|
||||
/** 薪资描述 */
|
||||
private String salary;
|
||||
|
||||
/** 公司名称 */
|
||||
private String companyName;
|
||||
|
||||
/** 公司简称 */
|
||||
private String companyShortName;
|
||||
|
||||
/** 公司类型 */
|
||||
private String companyType;
|
||||
|
||||
/** 公司Logo */
|
||||
private String companyLogoUrl;
|
||||
|
||||
/** 地区名称 */
|
||||
private String regionName;
|
||||
|
||||
/** 岗位类型名称 */
|
||||
private String categoryName;
|
||||
|
||||
/** 岗位标签 */
|
||||
private List<String> tags;
|
||||
|
||||
/** 来源链接 */
|
||||
private String sourceUrl;
|
||||
|
||||
/** 是否收藏 */
|
||||
private Boolean isFavorite;
|
||||
|
||||
/** 匹配总分(0-90) */
|
||||
private Integer matchScore;
|
||||
|
||||
/** 匹配度详情 */
|
||||
private JobMatchScoreDto matchDetail;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.jiayunet.pojo.dto.job;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 岗位匹配度详情DTO
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class JobMatchScoreDto {
|
||||
|
||||
/** 行业得分(0-100,百分制) */
|
||||
private Integer industryScore;
|
||||
|
||||
/** 技能得分(0-100,百分制) */
|
||||
private Integer skillScore;
|
||||
|
||||
/** 经验得分(0-100,百分制) */
|
||||
private Integer experienceScore;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.jiayunet.pojo.param.job;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jiayunet.pojo.PageParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 岗位查询入参
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class JobQueryParam extends PageParam {
|
||||
|
||||
/** 地区编码列表 */
|
||||
private List<String> regionCodes;
|
||||
|
||||
/** 岗位类型ID列表 */
|
||||
private List<Long> categoryIds;
|
||||
|
||||
/** 行业ID列表 */
|
||||
private List<Long> industryIds;
|
||||
|
||||
/** 工作类型 0=全职 1=兼职 */
|
||||
private Integer employmentType;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package org.jiayunet.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jiayunet.mapper.*;
|
||||
import org.jiayunet.pojo.PageResult;
|
||||
import org.jiayunet.pojo.dto.job.JobDto;
|
||||
import org.jiayunet.pojo.dto.job.JobMatchScoreDto;
|
||||
import org.jiayunet.pojo.param.job.JobQueryParam;
|
||||
import org.jiayunet.pojo.po.*;
|
||||
import org.jiayunet.pojo.vo.JobListItemVo;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 岗位服务
|
||||
* <p>主要功能:岗位列表查询、匹配度计算</p>
|
||||
* <p>依赖:JobMatchService(匹配度计算)</p>
|
||||
* <p>使用表:bg_job(查询岗位)、bg_user_job_dislike(查询不感兴趣记录)、bg_user_job_favorite(查询收藏状态)、bg_china_regions_code(扩展地区子级)、bg_job_category(扩展岗位类型子级)、bg_industry(扩展行业子级)</p>
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class JobService {
|
||||
|
||||
@Autowired
|
||||
private JobMapper jobMapper;
|
||||
|
||||
@Autowired
|
||||
private UserJobDislikeMapper userJobDislikeMapper;
|
||||
|
||||
@Autowired
|
||||
private UserJobFavoriteMapper userJobFavoriteMapper;
|
||||
|
||||
@Autowired
|
||||
private ChinaRegionsCodeMapper regionMapper;
|
||||
|
||||
@Autowired
|
||||
private JobCategoryMapper categoryMapper;
|
||||
|
||||
@Autowired
|
||||
private IndustryMapper industryMapper;
|
||||
|
||||
@Autowired
|
||||
private org.jiayunet.service.JobMatchService jobMatchService;
|
||||
|
||||
/**
|
||||
* 岗位列表查询
|
||||
* <p>1. 扩展筛选条件子级 2. 查询不感兴趣记录 3. 扩展不感兴趣的地区和行业子级 4. 执行分页查询 5. 查询收藏状态 6. 批量计算匹配度 7. 组装返回数据</p>
|
||||
*/
|
||||
public PageResult<JobDto> listJobs(JobQueryParam param, Long userId) {
|
||||
// 1. 扩展筛选条件的子级
|
||||
List<String> expandedRegionCodes = expandRegionCodes(param.getRegionCodes());
|
||||
List<Long> expandedCategoryIds = expandCategoryIds(param.getCategoryIds());
|
||||
List<Long> expandedIndustryIds = expandIndustryIds(param.getIndustryIds());
|
||||
|
||||
// 2. 查询用户不感兴趣记录
|
||||
List<UserJobDislike> dislikes = userJobDislikeMapper.selectList(new LambdaQueryWrapper<UserJobDislike>().eq(UserJobDislike::getUserId, userId));
|
||||
|
||||
// 3. 提取排除列表
|
||||
List<Long> excludeJobIds = dislikes.stream().map(UserJobDislike::getJobId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
List<Long> excludeCompanyIds = dislikes.stream().map(UserJobDislike::getCompanyId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
|
||||
// 4. 提取不感兴趣的地区(直接使用,不扩展子级)
|
||||
List<String> excludeRegionCodes = dislikes.stream().map(UserJobDislike::getRegionCode).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
|
||||
// 5. 提取不感兴趣的行业(直接使用,不扩展子级)
|
||||
List<Long> excludeIndustryIds = dislikes.stream().map(UserJobDislike::getIndustryId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
|
||||
// 6. 执行分页查询
|
||||
Page<JobListItemVo> page = jobMapper.selectJobPage(param.toPage(), expandedRegionCodes, expandedCategoryIds, expandedIndustryIds, param.getEmploymentType(), excludeJobIds, excludeCompanyIds, excludeRegionCodes, excludeIndustryIds);
|
||||
|
||||
// 7. 查询收藏状态
|
||||
List<Long> jobIds = page.getRecords().stream().map(JobListItemVo::getId).collect(Collectors.toList());
|
||||
Map<Long, Boolean> favoriteMap = getFavoriteMap(userId, jobIds);
|
||||
|
||||
// 8. 批量计算匹配度
|
||||
Map<Long, Map<String, Integer>> matchScoreMap = jobMatchService.batchCalculateMatchScore(page.getRecords(), userId);
|
||||
|
||||
// 9. 组装返回数据
|
||||
List<JobDto> dtoList = page.getRecords().stream().map(vo -> {
|
||||
JobDto dto = new JobDto();
|
||||
BeanUtils.copyProperties(vo, dto);
|
||||
dto.setIsFavorite(favoriteMap.getOrDefault(vo.getId(), false));
|
||||
Map<String, Integer> scoreMap = matchScoreMap.get(vo.getId());
|
||||
if (scoreMap != null) {
|
||||
JobMatchScoreDto matchScore = new JobMatchScoreDto(scoreMap.get("industryScore"), scoreMap.get("skillScore"), scoreMap.get("experienceScore"));
|
||||
dto.setMatchScore(scoreMap.get("totalScore"));
|
||||
dto.setMatchDetail(matchScore);
|
||||
} else {
|
||||
dto.setMatchScore(0);
|
||||
dto.setMatchDetail(new JobMatchScoreDto(0, 0, 0));
|
||||
}
|
||||
return dto;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(page.getCurrent(), page.getSize(), page.getTotal(), dtoList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展地区编码(包含自身和所有子级)
|
||||
* <p>一次查询:code本身 + provinceCode匹配 + cityCode匹配</p>
|
||||
*/
|
||||
private List<String> expandRegionCodes(List<String> codes) {
|
||||
if (codes == null || codes.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ChinaRegionsCode> regions = regionMapper.selectList(new LambdaQueryWrapper<ChinaRegionsCode>().in(ChinaRegionsCode::getCode, codes).or().in(ChinaRegionsCode::getProvinceCode, codes).or().in(ChinaRegionsCode::getCityCode, codes));
|
||||
return regions.stream().map(ChinaRegionsCode::getCode).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展岗位类型ID(包含自身和所有子级)
|
||||
* <p>一次查询:id本身 + rootId匹配 + parentId匹配</p>
|
||||
*/
|
||||
private List<Long> expandCategoryIds(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<JobCategory> categories = categoryMapper.selectList(new LambdaQueryWrapper<JobCategory>().in(JobCategory::getId, ids).or().in(JobCategory::getRootId, ids).or().in(JobCategory::getParentId, ids));
|
||||
return categories.stream().map(JobCategory::getId).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展行业ID(包含自身和所有子级)
|
||||
* <p>一次查询:id本身 + rootId匹配 + parentId匹配</p>
|
||||
*/
|
||||
private List<Long> expandIndustryIds(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Industry> industries = industryMapper.selectList(new LambdaQueryWrapper<Industry>().in(Industry::getId, ids).or().in(Industry::getRootId, ids).or().in(Industry::getParentId, ids));
|
||||
return industries.stream().map(Industry::getId).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询收藏状态Map
|
||||
* <p>批量查询用户对岗位的收藏状态</p>
|
||||
*/
|
||||
private Map<Long, Boolean> getFavoriteMap(Long userId, List<Long> jobIds) {
|
||||
if (jobIds == null || jobIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<UserJobFavorite> favorites = userJobFavoriteMapper.selectList(new LambdaQueryWrapper<UserJobFavorite>().eq(UserJobFavorite::getUserId, userId).in(UserJobFavorite::getJobId, jobIds));
|
||||
return favorites.stream().collect(Collectors.toMap(UserJobFavorite::getJobId, f -> true));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user