添加岗位模板下载,岗位模板上传功能
This commit is contained in:
@@ -5,7 +5,9 @@ import com.ruoyi.cms.domain.*;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
import com.ruoyi.cms.domain.vo.CandidateVO;
|
||||
import com.ruoyi.cms.domain.vo.CompanyVo;
|
||||
import com.ruoyi.cms.domain.vo.JobExcelVo;
|
||||
import com.ruoyi.cms.service.*;
|
||||
import com.ruoyi.cms.util.EasyExcelUtils;
|
||||
import com.ruoyi.cms.util.RoleUtils;
|
||||
import com.ruoyi.cms.util.StringUtil;
|
||||
import com.ruoyi.cms.util.sensitiveWord.SensitiveWordChecker;
|
||||
@@ -24,12 +26,15 @@ import com.ruoyi.common.utils.bean.BeanUtils;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -358,4 +363,82 @@ public class CmsJobController extends BaseController
|
||||
}
|
||||
return AjaxResult.success("此岗位已存在!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传请求(单个)
|
||||
*/
|
||||
@PostMapping("/uploadFile")
|
||||
@ApiOperation("岗位批量上传")
|
||||
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
if (file.isEmpty()) {
|
||||
return AjaxResult.error("上传文件不能为空");
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null || !fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
|
||||
return AjaxResult.error("请上传Excel格式的文件");
|
||||
}
|
||||
|
||||
try (InputStream inputStream = file.getInputStream()){
|
||||
List<JobExcelVo> allExcelVoList = new ArrayList<>();
|
||||
EasyExcelUtils.readExcelByBatch(inputStream, JobExcelVo.class, 100, list -> {
|
||||
allExcelVoList.addAll(list);
|
||||
});
|
||||
if (CollectionUtils.isEmpty(allExcelVoList)) {
|
||||
throw new Exception("Excel文件中无有效数据");
|
||||
}
|
||||
jobService.uploadFileJob(allExcelVoList);
|
||||
return AjaxResult.success("已上传!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板下载
|
||||
* @param request
|
||||
* @param response
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/downloadModel")
|
||||
public void downloadModel(HttpServletRequest request, HttpServletResponse response)throws Exception{
|
||||
String name = "模板.xlsx";
|
||||
String pathFile="/data/downloadmodel/"+name;
|
||||
File url = new File(pathFile);
|
||||
|
||||
String resMsg = "";
|
||||
try {
|
||||
request.setCharacterEncoding("utf-8");
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
try {
|
||||
name = new String(name.getBytes("gb2312"), "ISO8859-1");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
response.reset();
|
||||
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment; filename="+ name);
|
||||
response.setHeader("Pragma", "public");
|
||||
response.setHeader("Cache-Control", "max-age=0");
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(url);
|
||||
} catch (FileNotFoundException e1) {
|
||||
resMsg = "文件未找到";
|
||||
e1.printStackTrace();
|
||||
response.getWriter().write(resMsg + ":" + name);
|
||||
}
|
||||
OutputStream ou = response.getOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int i = -1;
|
||||
while ((i = in.read(buffer)) != -1) {
|
||||
ou.write(buffer, 0, i);
|
||||
}
|
||||
ou.flush();
|
||||
ou.close();
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.ruoyi.cms.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 岗位对象 job
|
||||
*/
|
||||
@Data
|
||||
public class JobExcelVo
|
||||
{
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 0)
|
||||
@Excel(name = "职位名称")
|
||||
@ApiModelProperty("职位名称")
|
||||
private String jobTitle;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 1)
|
||||
@Excel(name = "最小薪资", readConverterExp = "元=")
|
||||
@ApiModelProperty("最小薪资(元)")
|
||||
private Long minSalary;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 2)
|
||||
@Excel(name = "最大薪资", readConverterExp = "元=")
|
||||
@ApiModelProperty("最大薪资(元)")
|
||||
private Long maxSalary;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 3)
|
||||
@Excel(name = "学历要求 对应字典education")
|
||||
@ApiModelProperty("学历要求 对应字典education")
|
||||
private String education;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 4)
|
||||
@Excel(name = "工作经验要求 对应字典experience")
|
||||
@ApiModelProperty("工作经验要求 对应字典experience")
|
||||
private String experience;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 5)
|
||||
@Excel(name = "用人单位名称")
|
||||
@ApiModelProperty("用人单位名称")
|
||||
private String companyName;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 6)
|
||||
@Excel(name = "招聘人数")
|
||||
@ApiModelProperty("招聘人数")
|
||||
private Long vacancies;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 7)
|
||||
@ApiModelProperty("岗位描述")
|
||||
private String description;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 8)
|
||||
@ApiModelProperty("岗位分类")
|
||||
private String jobCategory;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 9)
|
||||
@ApiModelProperty("岗位类型 0疆内 1疆外")
|
||||
private String jobType;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 10)
|
||||
@ApiModelProperty("类型 0常规岗位 1就业见习岗位 2实习实训岗位 3社区实践岗位 4零工 对应字段字典position_type")
|
||||
private String type;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 11)
|
||||
@ApiModelProperty("工作地点")
|
||||
private String jobAddress;
|
||||
|
||||
@ExcelProperty(value = "职位名称", index = 12)
|
||||
@Excel(name = "岗位区划")
|
||||
@ApiModelProperty("岗位区划")
|
||||
private String jobLocation;
|
||||
|
||||
@ExcelProperty(value = "联系人", index = 13)
|
||||
@ApiModelProperty("联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@ExcelProperty(value = "联系人电话", index = 14)
|
||||
@ApiModelProperty("联系人电话")
|
||||
private String contactPersonPhone;
|
||||
|
||||
@ApiModelProperty("是否发布 0未发布 1发布")
|
||||
private Integer isPublish;
|
||||
|
||||
@ApiModelProperty("工作地点区县字典代码")
|
||||
private Integer jobLocationAreaCode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "发布时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty("发布时间")
|
||||
private String postingDate;
|
||||
|
||||
@ApiModelProperty("数据来源")
|
||||
private String dataSource;
|
||||
}
|
||||
@@ -29,4 +29,6 @@ public interface CompanyMapper extends BaseMapper<Company>
|
||||
List<Company> selectLikeCompanyList(Company company);
|
||||
|
||||
Company selectCompanyByJobId(Long jobId);
|
||||
|
||||
List<Company> selectByNames(List<String> list);
|
||||
}
|
||||
|
||||
@@ -60,4 +60,6 @@ public interface JobMapper extends BaseMapper<Job>
|
||||
Job getJobInfo(Long jobId);
|
||||
|
||||
Integer getTotals(Job job);
|
||||
|
||||
void updateFileBatchInsert(List<Job> list);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.ruoyi.cms.domain.ESJobDocument;
|
||||
import com.ruoyi.cms.domain.Job;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
import com.ruoyi.cms.domain.vo.CandidateVO;
|
||||
import com.ruoyi.cms.domain.vo.JobExcelVo;
|
||||
import com.ruoyi.common.core.domain.entity.AppUser;
|
||||
import org.dromara.easyes.core.biz.EsPageInfo;
|
||||
|
||||
@@ -110,4 +111,6 @@ public interface IJobService
|
||||
* @return
|
||||
*/
|
||||
public Integer getTotals(Job job);
|
||||
|
||||
void uploadFileJob(List<JobExcelVo> list);
|
||||
}
|
||||
|
||||
@@ -14,5 +14,7 @@ import java.util.List;
|
||||
public interface JobContactService{
|
||||
|
||||
List<JobContact> getSelectList(JobContact jobContact);
|
||||
|
||||
int batchInsert(List<JobContact> list);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,4 +18,9 @@ public class JobContactServiceImpl extends ServiceImpl<JobContactMapper, JobCont
|
||||
public List<JobContact> getSelectList(JobContact jobContact){
|
||||
return jobContactMapper.getSelectList(jobContact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int batchInsert(List<JobContact> list) {
|
||||
return jobContactMapper.batchInsert(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
@@ -10,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.cms.domain.*;
|
||||
import com.ruoyi.cms.domain.vo.JobExcelVo;
|
||||
import com.ruoyi.cms.util.notice.NoticeUtils;
|
||||
import com.ruoyi.common.core.domain.entity.File;
|
||||
import com.ruoyi.cms.domain.query.ESJobSearch;
|
||||
@@ -25,6 +24,7 @@ import com.ruoyi.common.core.domain.entity.Company;
|
||||
import com.ruoyi.common.core.domain.entity.JobTitle;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.SiteSecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
@@ -1137,4 +1137,166 @@ public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobSe
|
||||
public Integer getTotals(Job job) {
|
||||
return jobMapper.getTotals(job);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void uploadFileJob(List<JobExcelVo> list) {
|
||||
//查询所有企业
|
||||
Map<String, Long> companyNameToIdMap = companyNameToIdMap(list);
|
||||
//岗位字典转换
|
||||
List<Job> jobList = buildJobList(list, companyNameToIdMap);
|
||||
//岗位去重
|
||||
List<Job> dedupedJobList = dedupJobList(jobList);
|
||||
//批量保存岗
|
||||
jobMapper.updateFileBatchInsert(dedupedJobList);
|
||||
//构建联系人列表(关联去重后的岗位)
|
||||
List<JobContact> contactList = buildJobContactList(dedupedJobList, list, companyNameToIdMap);
|
||||
//联系人去重(按:企业ID+联系人手机号 去重;企业ID为0/null时,仅按手机号去重)
|
||||
List<JobContact> dedupedContactList = dedupContactList(contactList, dedupedJobList);
|
||||
//批量保存联系人
|
||||
if (CollectionUtils.isNotEmpty(dedupedContactList)) {
|
||||
jobContactMapper.batchInsert(dedupedContactList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业名称查询关联(核心业务逻辑)
|
||||
*/
|
||||
private Map<String, Long> companyNameToIdMap(List<JobExcelVo> allExcelVoList) {
|
||||
Set<String> companyNameSet = allExcelVoList.stream()
|
||||
.map(JobExcelVo::getCompanyName)
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (companyNameSet.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
List<Company> companies = companyMapper.selectByNames(new ArrayList<>(companyNameSet));
|
||||
if (CollectionUtils.isEmpty(companies)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return companies.stream()
|
||||
.collect(Collectors.toMap(
|
||||
Company::getName,
|
||||
Company::getCompanyId,
|
||||
(v1, v2) -> v1
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典转换 + Job对象构建(业务数据转换)
|
||||
*/
|
||||
private List<Job> buildJobList(List<JobExcelVo> allExcelVoList, Map<String, Long> companyNameToIdMap) {
|
||||
return allExcelVoList.stream().map(it -> {
|
||||
Job job = new Job();
|
||||
BeanUtils.copyProperties(it, job);
|
||||
//字典转换
|
||||
String education = DictUtils.getDictValue("education", it.getEducation());
|
||||
String experience = DictUtils.getDictValue("experience", it.getExperience());
|
||||
String jobType = DictUtils.getDictValue("job_type", it.getJobType());
|
||||
String type = DictUtils.getDictValue("position_type", it.getType());
|
||||
String areaCode = DictUtils.getDictValue("area", it.getJobLocation());
|
||||
// 字段赋值
|
||||
job.setEducation(education);
|
||||
job.setExperience(experience);
|
||||
job.setJobType(jobType);
|
||||
job.setType(type);
|
||||
job.setJobLocation(StringUtils.isBlank(it.getJobAddress()) ? it.getJobLocation() : it.getJobAddress());
|
||||
job.setJobLocationAreaCode(StringUtils.isBlank(areaCode) ? 0 : Integer.valueOf(areaCode));
|
||||
job.setIsPublish(1);
|
||||
job.setDataSource("1");
|
||||
job.setPostingDate(DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS));
|
||||
|
||||
String companyName = it.getCompanyName();
|
||||
job.setCompanyName(companyName);
|
||||
job.setCompanyId(companyNameToIdMap.getOrDefault(companyName, null));
|
||||
|
||||
return job;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 岗位去重(业务规则:企业ID+岗位名称+工作地点唯一)
|
||||
*/
|
||||
private List<Job> dedupJobList(List<Job> jobList) {
|
||||
Map<String, Job> dedupMap = new LinkedHashMap<>();
|
||||
for (Job job : jobList) {
|
||||
String dedupKey = String.format(
|
||||
"%s_%s_%s",
|
||||
Optional.ofNullable(job.getCompanyId()).orElse(null),
|
||||
job.getJobTitle().trim(),
|
||||
job.getJobLocation().trim()
|
||||
);
|
||||
dedupMap.putIfAbsent(dedupKey, job);
|
||||
}
|
||||
return new ArrayList<>(dedupMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建联系人列表(业务关联)
|
||||
*/
|
||||
private List<JobContact> buildJobContactList(List<Job> dedupedJobList, List<JobExcelVo> allExcelVoList, Map<String, Long> companyNameToIdMap) {
|
||||
List<JobContact> contactList = new ArrayList<>();
|
||||
|
||||
Map<String, Job> jobMatchMap = dedupedJobList.stream()
|
||||
.collect(Collectors.toMap(
|
||||
job -> String.format(
|
||||
"%s_%s",
|
||||
job.getJobTitle().trim(),
|
||||
job.getJobLocation().trim()
|
||||
),
|
||||
job -> job
|
||||
));
|
||||
|
||||
for (JobExcelVo vo : allExcelVoList) {
|
||||
String jobTitle = vo.getJobTitle().trim();
|
||||
String jobLocation = StringUtils.isBlank(vo.getJobAddress()) ? vo.getJobLocation().trim() : vo.getJobAddress().trim();
|
||||
String matchKey = String.format("%s_%s", jobTitle, jobLocation);
|
||||
|
||||
Job matchedJob = jobMatchMap.get(matchKey);
|
||||
if (matchedJob == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JobContact contact = new JobContact();
|
||||
contact.setJobId(matchedJob.getJobId());
|
||||
contact.setContactPerson(vo.getContactPerson());
|
||||
contact.setContactPersonPhone(vo.getContactPersonPhone());
|
||||
contactList.add(contact);
|
||||
}
|
||||
|
||||
return contactList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 联系人去重(兼容企业ID为0的情况)
|
||||
*/
|
||||
private List<JobContact> dedupContactList(List<JobContact> contactList, List<Job> dedupedJobList) {
|
||||
if (CollectionUtils.isEmpty(contactList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<Long> validJobIds = dedupedJobList.stream().map(Job::getJobId).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
|
||||
Map<String, JobContact> dedupMap = new LinkedHashMap<>();
|
||||
for (JobContact contact : contactList) {
|
||||
if (StringUtils.isBlank(contact.getContactPersonPhone())) {
|
||||
System.out.printf("跳过联系人:姓名=%s(手机号为空)%n", contact.getContactPerson());
|
||||
continue;
|
||||
}
|
||||
|
||||
Long jobId = contact.getJobId();
|
||||
if (jobId == null || !validJobIds.contains(jobId)) {
|
||||
System.out.printf("跳过联系人:姓名=%s(岗位ID无效或不存在)%n", contact.getContactPerson());
|
||||
continue;
|
||||
}
|
||||
|
||||
String dedupKey = String.format("%s_%s",jobId,contact.getContactPersonPhone().trim());
|
||||
|
||||
dedupMap.putIfAbsent(dedupKey, contact);
|
||||
}
|
||||
|
||||
return new ArrayList<>(dedupMap.values());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user