添加岗位详情接口
This commit is contained in:
@@ -2,15 +2,13 @@ package org.jiayunet.controller;
|
|||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.jiayunet.pojo.PageResult;
|
import org.jiayunet.pojo.PageResult;
|
||||||
|
import org.jiayunet.pojo.dto.job.JobDetailDto;
|
||||||
import org.jiayunet.pojo.dto.job.JobDto;
|
import org.jiayunet.pojo.dto.job.JobDto;
|
||||||
import org.jiayunet.pojo.param.job.JobQueryParam;
|
import org.jiayunet.pojo.param.job.JobQueryParam;
|
||||||
import org.jiayunet.service.JobService;
|
import org.jiayunet.service.JobService;
|
||||||
import org.jiayunet.tool.UserSecurityTool;
|
import org.jiayunet.tool.UserSecurityTool;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 岗位接口
|
* 岗位接口
|
||||||
@@ -33,4 +31,14 @@ public class JobController {
|
|||||||
Long userId = UserSecurityTool.getUserId();
|
Long userId = UserSecurityTool.getUserId();
|
||||||
return jobService.listJobs(param, userId);
|
return jobService.listJobs(param, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位详情
|
||||||
|
* <p>返回岗位完整信息、公司信息、匹配度、收藏状态</p>
|
||||||
|
*/
|
||||||
|
@GetMapping("/{jobId}")
|
||||||
|
public JobDetailDto getJobDetail(@PathVariable Long jobId) {
|
||||||
|
Long userId = UserSecurityTool.getUserId();
|
||||||
|
return jobService.getJobDetail(jobId, userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package org.jiayunet.pojo.dto.job;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位详情出参
|
||||||
|
*
|
||||||
|
* @author zk
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class JobDetailDto {
|
||||||
|
// ========== 岗位信息 ==========
|
||||||
|
/** 岗位ID */
|
||||||
|
private Long jobId;
|
||||||
|
/** 岗位标题 */
|
||||||
|
private String jobTitle;
|
||||||
|
/** 薪资描述 */
|
||||||
|
private String salary;
|
||||||
|
/** 工作类型 0=全职 1=兼职 */
|
||||||
|
private Integer employmentType;
|
||||||
|
/** 学历要求 0=不限 1=大专 2=本科 3=硕士 4=博士 */
|
||||||
|
private Integer education;
|
||||||
|
/** 最低工作年限 */
|
||||||
|
private Integer minExperience;
|
||||||
|
/** 岗位职责 */
|
||||||
|
private String description;
|
||||||
|
/** 任职要求 */
|
||||||
|
private String requirement;
|
||||||
|
/** 加分项 */
|
||||||
|
private String bonus;
|
||||||
|
/** 岗位标签 */
|
||||||
|
private List<String> tags;
|
||||||
|
/** 技能标签 */
|
||||||
|
private List<String> skillTags;
|
||||||
|
/** 来源链接 */
|
||||||
|
private String sourceUrl;
|
||||||
|
/** 岗位类型名称 */
|
||||||
|
private String categoryName;
|
||||||
|
/** 要求的行业经验名称 */
|
||||||
|
private String requiredIndustryName;
|
||||||
|
|
||||||
|
// ========== 公司信息 ==========
|
||||||
|
/** 公司ID */
|
||||||
|
private Long companyId;
|
||||||
|
/** 公司名称 */
|
||||||
|
private String companyName;
|
||||||
|
/** 公司简称 */
|
||||||
|
private String companyShortName;
|
||||||
|
/** 公司Logo */
|
||||||
|
private String companyLogoUrl;
|
||||||
|
/** 公司类型 */
|
||||||
|
private String companyType;
|
||||||
|
/** 公司所属行业名称 */
|
||||||
|
private String companyIndustryName;
|
||||||
|
/** 公司标签 */
|
||||||
|
private List<String> companyTags;
|
||||||
|
/** 公司简介 */
|
||||||
|
private String companySummary;
|
||||||
|
/** 公司描述 */
|
||||||
|
private String companyDescription;
|
||||||
|
/** 成立时间 */
|
||||||
|
private String companyFoundedYear;
|
||||||
|
/** 公司地址 */
|
||||||
|
private String companyAddress;
|
||||||
|
/** 公司规模 */
|
||||||
|
private String companyScale;
|
||||||
|
/** 公司官网 */
|
||||||
|
private String companyWebsite;
|
||||||
|
/** 融资状态 */
|
||||||
|
private String companyFinancingStage;
|
||||||
|
/** 最新估值 */
|
||||||
|
private String companyLatestValuation;
|
||||||
|
/** 公司新闻 */
|
||||||
|
private List<String> companyNews;
|
||||||
|
/** 地区名称 */
|
||||||
|
private String regionName;
|
||||||
|
|
||||||
|
// ========== 匹配度信息 ==========
|
||||||
|
/** 匹配总分 */
|
||||||
|
private Integer matchScore;
|
||||||
|
/** 匹配度详情 */
|
||||||
|
private JobMatchScoreDto matchDetail;
|
||||||
|
|
||||||
|
// ========== 用户操作状态 ==========
|
||||||
|
/** 是否已收藏 */
|
||||||
|
private Boolean isFavorite;
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jiayunet.mapper.*;
|
import org.jiayunet.mapper.*;
|
||||||
import org.jiayunet.pojo.PageResult;
|
import org.jiayunet.pojo.PageResult;
|
||||||
|
import org.jiayunet.pojo.dto.job.JobDetailDto;
|
||||||
import org.jiayunet.pojo.dto.job.JobDto;
|
import org.jiayunet.pojo.dto.job.JobDto;
|
||||||
import org.jiayunet.pojo.dto.job.JobMatchScoreDto;
|
import org.jiayunet.pojo.dto.job.JobMatchScoreDto;
|
||||||
import org.jiayunet.pojo.param.job.JobQueryParam;
|
import org.jiayunet.pojo.param.job.JobQueryParam;
|
||||||
@@ -19,9 +20,9 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 岗位服务
|
* 岗位服务
|
||||||
* <p>主要功能:岗位列表查询、匹配度计算</p>
|
* <p>主要功能:岗位列表查询、岗位详情查询、匹配度计算</p>
|
||||||
* <p>依赖:JobMatchService(匹配度计算)</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>
|
* <p>使用的表:bg_job、bg_company、bg_user_job_dislike、bg_user_job_favorite、bg_china_regions_code、bg_job_category、bg_industry</p>
|
||||||
*
|
*
|
||||||
* @author zk
|
* @author zk
|
||||||
*/
|
*/
|
||||||
@@ -48,11 +49,21 @@ public class JobService {
|
|||||||
private IndustryMapper industryMapper;
|
private IndustryMapper industryMapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private org.jiayunet.service.JobMatchService jobMatchService;
|
private CompanyMapper companyMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JobMatchService jobMatchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 岗位列表查询
|
* 岗位列表查询
|
||||||
* <p>1. 扩展筛选条件子级 2. 查询不感兴趣记录 3. 扩展不感兴趣的地区和行业子级 4. 执行分页查询 5. 查询收藏状态 6. 批量计算匹配度 7. 组装返回数据</p>
|
* <p>方法逻辑流程:</p>
|
||||||
|
* <p>1. 扩展筛选条件的子级(地区/岗位类型/行业)</p>
|
||||||
|
* <p>2. 查询用户不感兴趣记录</p>
|
||||||
|
* <p>3. 提取排除列表(岗位/公司/地区/行业)</p>
|
||||||
|
* <p>4. 执行分页查询</p>
|
||||||
|
* <p>5. 查询收藏状态</p>
|
||||||
|
* <p>6. 批量计算匹配度</p>
|
||||||
|
* <p>7. 组装返回数据</p>
|
||||||
*/
|
*/
|
||||||
public PageResult<JobDto> listJobs(JobQueryParam param, Long userId) {
|
public PageResult<JobDto> listJobs(JobQueryParam param, Long userId) {
|
||||||
// 1. 扩展筛选条件的子级
|
// 1. 扩展筛选条件的子级
|
||||||
@@ -150,4 +161,104 @@ public class JobService {
|
|||||||
List<UserJobFavorite> favorites = userJobFavoriteMapper.selectList(new LambdaQueryWrapper<UserJobFavorite>().eq(UserJobFavorite::getUserId, userId).in(UserJobFavorite::getJobId, jobIds));
|
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));
|
return favorites.stream().collect(Collectors.toMap(UserJobFavorite::getJobId, f -> true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询岗位详情
|
||||||
|
* <p>方法逻辑流程:</p>
|
||||||
|
* <p>1. 查询岗位信息(校验状态)</p>
|
||||||
|
* <p>2. 查询公司信息</p>
|
||||||
|
* <p>3. 查询收藏状态</p>
|
||||||
|
* <p>4. 计算匹配度</p>
|
||||||
|
* <p>5. 查询关联名称(岗位类型/行业/地区)</p>
|
||||||
|
* <p>6. 组装返回数据</p>
|
||||||
|
*/
|
||||||
|
public JobDetailDto getJobDetail(Long jobId, Long userId) {
|
||||||
|
// 1. 查询岗位
|
||||||
|
Job job = jobMapper.selectOne(new LambdaQueryWrapper<Job>().eq(Job::getId, jobId).eq(Job::getStatus, 0));
|
||||||
|
if (job == null) throw new RuntimeException("岗位不存在或已下架");
|
||||||
|
|
||||||
|
// 2. 查询公司
|
||||||
|
Company company = companyMapper.selectById(job.getCompanyId());
|
||||||
|
if (company == null) throw new RuntimeException("公司信息不存在");
|
||||||
|
|
||||||
|
// 3. 查询收藏状态
|
||||||
|
Long count = userJobFavoriteMapper.selectCount(new LambdaQueryWrapper<UserJobFavorite>().eq(UserJobFavorite::getUserId, userId).eq(UserJobFavorite::getJobId, jobId));
|
||||||
|
|
||||||
|
// 4. 计算匹配度
|
||||||
|
List<JobListItemVo> jobList = new ArrayList<>();
|
||||||
|
JobListItemVo item = new JobListItemVo();
|
||||||
|
item.setId(job.getId());
|
||||||
|
item.setRequiredIndustryId(job.getRequiredIndustryId());
|
||||||
|
item.setMinExperience(job.getMinExperience());
|
||||||
|
jobList.add(item);
|
||||||
|
Map<Long, Map<String, Integer>> matchScoreMap = jobMatchService.batchCalculateMatchScore(jobList, userId);
|
||||||
|
Map<String, Integer> scoreMap = matchScoreMap.get(jobId);
|
||||||
|
|
||||||
|
// 5. 查询关联名称
|
||||||
|
String categoryName = null;
|
||||||
|
if (job.getCategoryId() != null) {
|
||||||
|
JobCategory category = categoryMapper.selectById(job.getCategoryId());
|
||||||
|
if (category != null) categoryName = category.getName();
|
||||||
|
}
|
||||||
|
String requiredIndustryName = null;
|
||||||
|
if (job.getRequiredIndustryId() != null) {
|
||||||
|
Industry industry = industryMapper.selectById(job.getRequiredIndustryId());
|
||||||
|
if (industry != null) requiredIndustryName = industry.getName();
|
||||||
|
}
|
||||||
|
String companyIndustryName = null;
|
||||||
|
if (company.getIndustryId() != null) {
|
||||||
|
Industry industry = industryMapper.selectById(company.getIndustryId());
|
||||||
|
if (industry != null) companyIndustryName = industry.getName();
|
||||||
|
}
|
||||||
|
String regionName = null;
|
||||||
|
if (company.getRegionCode() != null) {
|
||||||
|
ChinaRegionsCode region = regionMapper.selectOne(new LambdaQueryWrapper<ChinaRegionsCode>().eq(ChinaRegionsCode::getCode, company.getRegionCode()));
|
||||||
|
if (region != null) regionName = region.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 组装返回
|
||||||
|
JobDetailDto dto = new JobDetailDto();
|
||||||
|
dto.setJobId(job.getId());
|
||||||
|
dto.setJobTitle(job.getTitle());
|
||||||
|
dto.setSalary(job.getSalary());
|
||||||
|
dto.setEmploymentType(job.getEmploymentType());
|
||||||
|
dto.setEducation(job.getEducation());
|
||||||
|
dto.setMinExperience(job.getMinExperience());
|
||||||
|
dto.setDescription(job.getDescription());
|
||||||
|
dto.setRequirement(job.getRequirement());
|
||||||
|
dto.setBonus(job.getBonus());
|
||||||
|
dto.setTags(job.getTags());
|
||||||
|
dto.setSkillTags(job.getSkillTags());
|
||||||
|
dto.setSourceUrl(job.getSourceUrl());
|
||||||
|
dto.setCategoryName(categoryName);
|
||||||
|
dto.setRequiredIndustryName(requiredIndustryName);
|
||||||
|
dto.setCompanyId(company.getId());
|
||||||
|
dto.setCompanyName(company.getName());
|
||||||
|
dto.setCompanyShortName(company.getShortName());
|
||||||
|
dto.setCompanyLogoUrl(company.getLogoUrl());
|
||||||
|
dto.setCompanyType(company.getCompanyType());
|
||||||
|
dto.setCompanyIndustryName(companyIndustryName);
|
||||||
|
dto.setCompanyTags(company.getTags());
|
||||||
|
dto.setCompanySummary(company.getSummary());
|
||||||
|
dto.setCompanyDescription(company.getDescription());
|
||||||
|
dto.setCompanyFoundedYear(company.getFoundedYear());
|
||||||
|
dto.setCompanyAddress(company.getAddress());
|
||||||
|
dto.setCompanyScale(company.getScale());
|
||||||
|
dto.setCompanyWebsite(company.getWebsite());
|
||||||
|
dto.setCompanyFinancingStage(company.getFinancingStage());
|
||||||
|
dto.setCompanyLatestValuation(company.getLatestValuation());
|
||||||
|
dto.setCompanyNews(company.getNews());
|
||||||
|
dto.setRegionName(regionName);
|
||||||
|
dto.setIsFavorite(count > 0);
|
||||||
|
if (scoreMap != null) {
|
||||||
|
dto.setMatchScore(scoreMap.get("totalScore"));
|
||||||
|
dto.setMatchDetail(new JobMatchScoreDto(scoreMap.get("industryScore"), scoreMap.get("skillScore"), scoreMap.get("experienceScore")));
|
||||||
|
} else {
|
||||||
|
dto.setMatchScore(0);
|
||||||
|
dto.setMatchDetail(new JobMatchScoreDto(0, 0, 0));
|
||||||
|
}
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.jiayunet.pojo.po;
|
package org.jiayunet.pojo.po;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公司表(bg_company)
|
* 公司表(bg_company)
|
||||||
@@ -13,7 +16,7 @@ import java.time.Instant;
|
|||||||
* @author zk
|
* @author zk
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@TableName(value = "bg_company")
|
@TableName(value = "bg_company", autoResultMap = true)
|
||||||
public class Company {
|
public class Company {
|
||||||
|
|
||||||
@TableId(type = IdType.ASSIGN_ID)
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
@@ -37,8 +40,9 @@ public class Company {
|
|||||||
/** 行业ID */
|
/** 行业ID */
|
||||||
private Long industryId;
|
private Long industryId;
|
||||||
|
|
||||||
/** 公司标签(JSON数组) */
|
/** 公司标签 */
|
||||||
private String tags;
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
/** 公司简要 */
|
/** 公司简要 */
|
||||||
private String summary;
|
private String summary;
|
||||||
@@ -64,8 +68,9 @@ public class Company {
|
|||||||
/** 最新估值 */
|
/** 最新估值 */
|
||||||
private String latestValuation;
|
private String latestValuation;
|
||||||
|
|
||||||
/** 新闻动态(JSON数组) */
|
/** 新闻动态 */
|
||||||
private String news;
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> news;
|
||||||
|
|
||||||
/** 状态 0=待完善 1=已完善 2=禁用 3=补充中 4=补充失败 */
|
/** 状态 0=待完善 1=已完善 2=禁用 3=补充中 4=补充失败 */
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +63,9 @@ public class CompanyCleanTransactionService {
|
|||||||
// tags:JSON数组
|
// tags:JSON数组
|
||||||
JsonNode tagsNode = root.path("tags");
|
JsonNode tagsNode = root.path("tags");
|
||||||
if (tagsNode.isArray() && !tagsNode.isEmpty()) {
|
if (tagsNode.isArray() && !tagsNode.isEmpty()) {
|
||||||
company.setTags(tagsNode.toString());
|
List<String> tags = new ArrayList<>();
|
||||||
|
tagsNode.forEach(node -> tags.add(node.asText()));
|
||||||
|
company.setTags(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// summary
|
// summary
|
||||||
@@ -116,7 +119,9 @@ public class CompanyCleanTransactionService {
|
|||||||
// news:JSON数组
|
// news:JSON数组
|
||||||
JsonNode newsNode = root.path("news");
|
JsonNode newsNode = root.path("news");
|
||||||
if (newsNode.isArray() && !newsNode.isEmpty()) {
|
if (newsNode.isArray() && !newsNode.isEmpty()) {
|
||||||
company.setNews(newsNode.toString());
|
List<String> news = new ArrayList<>();
|
||||||
|
newsNode.forEach(node -> news.add(node.asText()));
|
||||||
|
company.setNews(news);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新状态和时间
|
// 更新状态和时间
|
||||||
|
|||||||
Reference in New Issue
Block a user