添加岗位详情接口

This commit is contained in:
zk
2026-03-20 19:24:10 +08:00
parent 7e5171d637
commit e2fb308655
5 changed files with 234 additions and 16 deletions
@@ -2,15 +2,13 @@ package org.jiayunet.controller;
import lombok.AllArgsConstructor;
import org.jiayunet.pojo.PageResult;
import org.jiayunet.pojo.dto.job.JobDetailDto;
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;
import org.springframework.web.bind.annotation.*;
/**
* 岗位接口
@@ -33,4 +31,14 @@ public class JobController {
Long userId = UserSecurityTool.getUserId();
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 org.jiayunet.mapper.*;
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.JobMatchScoreDto;
import org.jiayunet.pojo.param.job.JobQueryParam;
@@ -19,9 +20,9 @@ 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>
* <p>主要功能:岗位列表查询、岗位详情查询、匹配度计算</p>
* <p>依赖服务JobMatchService(匹配度计算)</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
*/
@@ -48,11 +49,21 @@ public class JobService {
private IndustryMapper industryMapper;
@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) {
// 1. 扩展筛选条件的子级
@@ -150,4 +161,104 @@ public class JobService {
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));
}
/**
* 查询岗位详情
* <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;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import java.time.Instant;
import java.util.List;
/**
* 公司表(bg_company
@@ -13,7 +16,7 @@ import java.time.Instant;
* @author zk
*/
@Data
@TableName(value = "bg_company")
@TableName(value = "bg_company", autoResultMap = true)
public class Company {
@TableId(type = IdType.ASSIGN_ID)
@@ -37,8 +40,9 @@ public class Company {
/** 行业ID */
private Long industryId;
/** 公司标签JSON数组) */
private String tags;
/** 公司标签 */
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> tags;
/** 公司简要 */
private String summary;
@@ -64,8 +68,9 @@ public class Company {
/** 最新估值 */
private String latestValuation;
/** 新闻动态JSON数组) */
private String news;
/** 新闻动态 */
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> news;
/** 状态 0=待完善 1=已完善 2=禁用 3=补充中 4=补充失败 */
private Integer status;
@@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
/**
@@ -62,7 +63,9 @@ public class CompanyCleanTransactionService {
// tagsJSON数组
JsonNode tagsNode = root.path("tags");
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
@@ -116,7 +119,9 @@ public class CompanyCleanTransactionService {
// newsJSON数组
JsonNode newsNode = root.path("news");
if (newsNode.isArray() && !newsNode.isEmpty()) {
company.setNews(newsNode.toString());
List<String> news = new ArrayList<>();
newsNode.forEach(node -> news.add(node.asText()));
company.setNews(news);
}
// 更新状态和时间