1.新增面试管理-导出功能

This commit is contained in:
lw 2024-06-21 14:15:10 +08:00
parent 360ee9a998
commit 218294e559
10 changed files with 165 additions and 26 deletions

View File

@ -141,7 +141,7 @@ public class HomePageController extends BaseController {
//11.合格简历百分比100 电话面试通过百分比电话通过人数/简历合格数 初试通过百分比 初试通过人数/初试到面人数 终试通过百分比终试通过人数/终试到面人数
List<ResumeStatVO.FunnelData> funnelDataList = new ArrayList<>();
//封装合格简历
funnelDataList.add(new ResumeStatVO.FunnelData().setName("合格简历:" + resumeFollowRecordList.size() + "").setValue(100d));
funnelDataList.add(new ResumeStatVO.FunnelData().setName("合格简历:" + 100 + "%").setValue(Long.valueOf(resumeFollowRecordList.size())));
//封装电话面试通过百分比
//通过电话面试的人数
//通过
@ -154,7 +154,7 @@ public class HomePageController extends BaseController {
phonePassPercentage = new BigDecimal(phonePassCount).divide(new BigDecimal(resumeFollowRecordList.size()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
}
funnelDataList.add(new ResumeStatVO.FunnelData().setName("电面通过:" + phonePassCount + "").setValue(phonePassPercentage.doubleValue()));
funnelDataList.add(new ResumeStatVO.FunnelData().setName("电面通过:" + phonePassPercentage.doubleValue() + "%").setValue(phonePassCount));
//封装初试通过百分比
long firstReachCount = resumeFollowRecordList.stream().filter(record -> Objects.nonNull(record.getFirstReach()) && "1".equals(record.getFirstReach())).count();
BigDecimal firstPassPercentage = new BigDecimal(0d);
@ -162,11 +162,11 @@ public class HomePageController extends BaseController {
firstPassPercentage = new BigDecimal(resumeStatVO.getFistCount()).divide(new BigDecimal(firstReachCount), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
}
funnelDataList.add(new ResumeStatVO.FunnelData().setName("初试通过:" + resumeStatVO.getFistCount() + "").setValue(firstPassPercentage.doubleValue()));
funnelDataList.add(new ResumeStatVO.FunnelData().setName("初试通过率:" + firstPassPercentage.doubleValue() + "%").setValue(resumeStatVO.getFistCount()));
//封装终试通过百分比
funnelDataList.add(new ResumeStatVO.FunnelData().setName("终试通过:" + resumeStatVO.getFinalPassCount() + "").setValue(resumeStatVO.getPassPercent()));
funnelDataList.add(new ResumeStatVO.FunnelData().setName("终试通过:" + resumeStatVO.getPassPercent() + "%").setValue(resumeStatVO.getFinalPassCount()));
//封装入职百分比
funnelDataList.add(new ResumeStatVO.FunnelData().setName("入职通过:" + resumeStatVO.getEntryCount() + "").setValue(resumeStatVO.getEntryPercent()));
funnelDataList.add(new ResumeStatVO.FunnelData().setName("入职通过:" + resumeStatVO.getEntryPercent() + "%").setValue(resumeStatVO.getEntryCount()));
resumeStatVO.setFunnelData(funnelDataList);
//查询待我审批的条数

View File

@ -214,15 +214,15 @@ public class RecruitFollowController extends BaseController {
//如果存在则插入数据
if(result.isPresent()){
FollowVO followVO = result.get();
//设置一面人数
//设置初试人数
followVO.setFirstInterviewCount(resumeFollowRecords.stream()
.filter( record -> record.getFirstReach() != null && "1".equals(record.getFirstReach()))
.count());
//设置二面人数
//设置复试人数
followVO.setSecondInterviewCount(resumeFollowRecords.stream()
.filter(record -> record.getSecondReach() != null && "1".equals(record.getSecondReach()))
.count());
//设置三面人数
//设置终试人数
followVO.setThirdInterviewCount(resumeFollowRecords.stream()
.filter(record -> record.getFinalReach() != null && "1".equals(record.getFinalReach()))
.count());

View File

@ -6,6 +6,7 @@ import cn.zeroerr.common.constant.Constants;
import cn.zeroerr.common.core.controller.BaseController;
import cn.zeroerr.common.core.domain.AjaxResult;
import cn.zeroerr.common.core.domain.entity.RecruitStructure;
import cn.zeroerr.common.core.domain.entity.SysDictData;
import cn.zeroerr.common.core.domain.entity.SysRole;
import cn.zeroerr.common.core.domain.entity.SysUser;
import cn.zeroerr.common.core.domain.model.LoginUser;
@ -15,6 +16,7 @@ import cn.zeroerr.common.utils.StringUtils;
import cn.zeroerr.common.utils.file.FileUploadUtils;
import cn.zeroerr.common.utils.file.FileUtils;
import cn.zeroerr.common.utils.file.MimeTypeUtils;
import cn.zeroerr.common.utils.poi.ExcelUtil;
import cn.zeroerr.domain.dto.ToppingDTO;
import cn.zeroerr.domain.entity.PostPlanFollow;
import cn.zeroerr.domain.entity.RecruitPost;
@ -23,6 +25,7 @@ import cn.zeroerr.domain.vo.UserVO;
import cn.zeroerr.service.RecruitPostService;
import cn.zeroerr.service.ResumeFollowRecordService;
import cn.zeroerr.system.mapper.RecruitStructureMapper;
import cn.zeroerr.system.service.ISysDictDataService;
import cn.zeroerr.system.service.ISysRoleService;
import cn.zeroerr.system.service.ISysUserService;
import cn.zeroerr.system.service.RecruitStructureService;
@ -64,6 +67,9 @@ public class RecruitInterviewController extends BaseController {
@Autowired
private RecruitStructureService recruitStructureService;
@Autowired
private ISysDictDataService iSysDictDataService;
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:list')")
@GetMapping("/interview/getAllUser")
@ApiOperation(value = "查询所有的用户(领导+hr")
@ -86,7 +92,7 @@ public class RecruitInterviewController extends BaseController {
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:list')")
@GetMapping("/interview/list")
@ApiOperation(value = "获取《面试管理》简历跟进记录列表")
public TableDataInfo getResumeRecordList(ResumeFollowRecord req){
public TableDataInfo getResumeRecordList(ResumeFollowRecord req) {
//获取操作人的角色,如果是hr或者hr其他角色则只显示自己的信息
//根据用户id获取所有的该审批人的角色ids
boolean isHr = false;
@ -94,24 +100,24 @@ public class RecruitInterviewController extends BaseController {
if (!CollectionUtils.isEmpty(sysRoles)) {
for (SysRole sysRole : sysRoles) {
//如果角色是hr
if (sysRole.getRoleKey().equals("hr")||sysRole.getRoleKey().equals("hrleader")) {
if (sysRole.getRoleKey().equals("hr") || sysRole.getRoleKey().equals("hrleader")) {
isHr = true;
}
}
}
if(isHr){
if (isHr) {
req.setHrId(getUserId());
}
startPage();
//找出所有置顶后的排序数据
List<ResumeFollowRecord> resumeFollowRecordListTopping= resumeFollowRecordService.listByQuery(req);
List<ResumeFollowRecord> resumeFollowRecordListTopping = resumeFollowRecordService.listByQuery(req);
return getDataTable(resumeFollowRecordListTopping);
}
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:add')")
@PostMapping("/interview/add")
@ApiOperation(value = "增加《面试管理》简历跟进记录")
public AjaxResult addInterviewRecord(@Validated @RequestBody ResumeFollowRecord req ){
public AjaxResult addInterviewRecord(@Validated @RequestBody ResumeFollowRecord req) {
req.setHrId(getUserId());
SysUser sysUser = iSysUserService.selectUserById(getUserId());
req.setHrName(sysUser.getNickName());
@ -126,21 +132,21 @@ public class RecruitInterviewController extends BaseController {
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:query')")
@GetMapping("/interview/{id}")
@ApiOperation(value = "获取《面试管理》简历跟进记录列表")
public AjaxResult getResumeRecordList(@PathVariable ("id")Long id){
public AjaxResult getResumeRecordList(@PathVariable("id") Long id) {
return success(resumeFollowRecordService.getById(id));
}
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:delete')")
@DeleteMapping("/interview/{id}")
@ApiOperation(value = "获取《面试管理》简历跟进记录列表")
public AjaxResult deleteResumeRecordList(@PathVariable ("id")Long id){
public AjaxResult deleteResumeRecordList(@PathVariable("id") Long id) {
return toAjax(resumeFollowRecordService.removeById(id));
}
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:edit')")
@PutMapping("/interview/edit")
@ApiOperation(value = "修改《面试管理》简历跟进记录")
public AjaxResult edit(@Validated @RequestBody ResumeFollowRecord req){
public AjaxResult edit(@Validated @RequestBody ResumeFollowRecord req) {
return toAjax(resumeFollowRecordService.updateById(req));
}
@ -161,14 +167,97 @@ public class RecruitInterviewController extends BaseController {
}
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:edit')")
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:topping')")
@PutMapping("/interview/editTopping")
@ApiOperation(value = "修改《面试管理》简历跟进记录是否置顶")
public AjaxResult editTopping(@RequestBody ToppingDTO req){
LocalDateTime localDateTime=LocalDateTime.now();
if(req.getTopping()){
return AjaxResult.success(resumeFollowRecordService.update(new LambdaUpdateWrapper<ResumeFollowRecord>().set(ResumeFollowRecord::getTopping,req.getTopping()).set(ResumeFollowRecord::getToppingTime,localDateTime).eq(ResumeFollowRecord::getId,req.getId())));
public AjaxResult editTopping(@RequestBody ToppingDTO req) {
LocalDateTime localDateTime = LocalDateTime.now();
if (req.getTopping()) {
return AjaxResult.success(resumeFollowRecordService.update(new LambdaUpdateWrapper<ResumeFollowRecord>()
.set(ResumeFollowRecord::getTopping, req.getTopping())
.set(ResumeFollowRecord::getToppingTime, localDateTime)
.eq(ResumeFollowRecord::getId, req.getId())));
}
return AjaxResult.success(resumeFollowRecordService.update(new LambdaUpdateWrapper<ResumeFollowRecord>().set(ResumeFollowRecord::getTopping,req.getTopping()).set(ResumeFollowRecord::getToppingTime,null).set(ResumeFollowRecord::getToppingTime,localDateTime).eq(ResumeFollowRecord::getId,req.getId())));
return AjaxResult.success(resumeFollowRecordService.update(new LambdaUpdateWrapper<ResumeFollowRecord>()
.set(ResumeFollowRecord::getTopping, req.getTopping())
.set(ResumeFollowRecord::getToppingTime, null)
.eq(ResumeFollowRecord::getId, req.getId())));
}
@PostMapping("/interview/export")
@PreAuthorize("@ss.hasAnyPermi('recruit:interview:export')")
@ApiOperation(value = "下载《面试管理》简历跟进记录")
@Log(title = "下载《面试管理》简历跟进记录", businessType = BusinessType.EXPORT)
public void export(HttpServletResponse response,ResumeFollowRecord req) {
boolean isHr = false;
List<SysRole> sysRoles = iSysRoleService.rolesByUserId(getUserId());
if (!CollectionUtils.isEmpty(sysRoles)) {
for (SysRole sysRole : sysRoles) {
//如果角色是hr
if (sysRole.getRoleKey().equals("hr") || sysRole.getRoleKey().equals("hrleader")) {
isHr = true;
}
}
}
if (isHr) {
req.setHrId(getUserId());
}
List<ResumeFollowRecord> resumeFollowRecordList = resumeFollowRecordService.listByQuery(req);
if (!CollectionUtils.isEmpty(resumeFollowRecordList)) {
resumeFollowRecordList.forEach(
record -> {
// 处理招聘渠道
String recruitmentChannel = record.getRecruitmentChannel();
if (StringUtils.hasText(recruitmentChannel)) {
String[] channelArray = recruitmentChannel.split(",");
StringBuilder newChannel = new StringBuilder();
for (String channel : channelArray) {
// 去字典里找 recruit_process_channel, 如果前台编辑了记得修改
SysDictData channelDict = iSysDictDataService.selectByChannel(channel, "recruit_process_channel");
if (channelDict != null && StringUtils.hasText(channelDict.getDictLabel())) {
if (newChannel.length() > 0) {
newChannel.append(",");
}
newChannel.append(channelDict.getDictLabel());
}
}
record.setRecruitmentChannel(newChannel.toString());
}
//处理初试人
record.setFirstInterviewerIds(handleInterviewee(record.getFirstInterviewerIds()));
//处理复试人
record.setSecondInterviewerIds(handleInterviewee(record.getSecondInterviewerIds()));
//处理终试人
record.setFinalInterviewerIds(handleInterviewee(record.getFinalInterviewerIds()));
//处理简历
record.setFile("https://oa.hr.zeroerr.cn/prod-api"+record.getFile());
}
);
}
ExcelUtil<ResumeFollowRecord> util = new ExcelUtil<ResumeFollowRecord>(ResumeFollowRecord.class);
util.exportExcel(response,resumeFollowRecordList, "面试管理数据");
}
private String handleInterviewee(String interviewerIds) {
if (!StringUtils.hasText(interviewerIds)) {
return "";
}
StringBuilder nameList = new StringBuilder();
String[] intervieweeArray = interviewerIds.split(",");
for (String interviewee : intervieweeArray) {
if (StringUtils.hasText(interviewee)) {
SysUser user = iSysUserService.selectUserById(Long.valueOf(interviewee));
if (user != null && StringUtils.hasText(user.getNickName())) {
if (nameList.length() > 0) {
nameList.append(",");
}
nameList.append(user.getNickName());
}
}
}
return nameList.toString();
}
}

View File

@ -1,5 +1,6 @@
package cn.zeroerr.domain.entity;
import cn.zeroerr.common.annotation.Excel;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
@ -22,6 +23,7 @@ public class ResumeFollowRecord implements Serializable {
* 主键id
*/
@TableId(value = "id", type = IdType.AUTO)
@Excel(name = "用户序号", prompt = "用户编号")
private Long id;
/**
@ -30,30 +32,35 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "receive_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "收到简历日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate receiveDate;
/**
* 招聘渠道
*/
@TableField(value = "recruitment_channel")
@Excel(name = "用户序号", width = 30)
private String recruitmentChannel;
/**
* 简历来源
*/
@TableField(value = "resume_source")
@Excel(name = "简历来源", dictType = "recruit_interview_source")
private String resumeSource;
/**
* 面试人姓名
*/
@TableField(value = "name")
@Excel(name = "面试人姓名")
private String name;
/**
* 电话
*/
@TableField(value = "phone")
@Excel(name = "电话")
private String phone;
/**
@ -66,6 +73,7 @@ public class ResumeFollowRecord implements Serializable {
* 岗位名称
*/
@TableField(value = "post_name")
@Excel(name = "岗位名称")
private String postName;
/**
@ -84,6 +92,7 @@ public class ResumeFollowRecord implements Serializable {
* 负责招聘的hr名字
*/
@TableField(value = "hr_name")
@Excel(name = "负责招聘的hr名字")
private String hrName;
/**
@ -92,12 +101,14 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "interview_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "面试日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate interviewDate;
/**
* 是否通过面试
* 是否通过面试 recruit_interview_phoneresult
*/
@TableField(value = "is_pass")
@Excel(name = "是否通过面试", dictType = "recruit_interview_phoneresult")
private String isPass;
/**
@ -106,12 +117,14 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "invitation_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "邀约日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate invitationDate;
/**
* 候选人拒绝的原因
*/
@TableField(value = "rejected_reason")
@Excel(name = "候选人拒绝的原因")
private String rejectedReason;
/**
@ -120,30 +133,34 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "first_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "初试日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate firstDate;
/**
* 初试是否到面
*/
@TableField(value = "first_reach")
@Excel(name = "初试是否到面", dictType = "recruit_interview_pass")
private String firstReach;
/**
* 初试是否通过笔试
*/
@TableField(value = "first_test")
@Excel(name = "初试是否通过笔试", dictType = "recruit_interview_pass")
private String firstTest;
/**
* 初试面试人id
*/
@TableField(value = "first_interviewer_ids")
@Excel(name = "初试面试人")
private String firstInterviewerIds;
/**
* 初试是否通过
*/
@TableField(value = "first_pass")
@Excel(name = "初试是否通过", dictType = "recruit_interview_pass")
private String firstPass;
/**
@ -152,24 +169,28 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "second_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "复试日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate secondDate;
/**
* 复试是否到面
*/
@TableField(value = "second_reach")
@Excel(name = "复试是否到面", dictType = "recruit_interview_pass")
private String secondReach;
/**
* 复试面试人id
*/
@TableField(value = "second_interviewer_ids")
@Excel(name = "复试面试人")
private String secondInterviewerIds;
/**
* 复试是否通过
*/
@TableField(value = "second_pass")
@Excel(name = "复试是否通过", dictType = "recruit_interview_pass")
private String secondPass;
/**
@ -178,30 +199,35 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "final_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "终试日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate finalDate;
/**
* 终试是否到面
*/
@TableField(value = "final_reach")
@Excel(name = "终试是否到面", dictType = "recruit_interview_pass")
private String finalReach;
/**
* 终试面试人id
*/
@TableField(value = "final_interviewer_ids")
@Excel(name = "终试面试人")
private String finalInterviewerIds;
/**
* 终面是否通过
*/
@TableField(value = "final_pass")
@Excel(name = "终面是否通过", dictType = "recruit_interview_pass")
private String finalPass;
/**
* 是否接受offer
*/
@TableField(value = "accept_offer")
@Excel(name = "是否接受offer", dictType = "recruit_interview_pass")
private String acceptOffer;
/**
@ -210,18 +236,21 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "join_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "预计入职日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate joinDate;
/**
* n的原因
*/
@TableField(value = "fail_reason")
@Excel(name = "n的原因")
private String failReason;
/**
* 特殊情况说明
*/
@TableField(value = "special_reason")
@Excel(name = "特殊情况说明")
private String specialReason;
/**
@ -246,12 +275,14 @@ public class ResumeFollowRecord implements Serializable {
@TableField(value = "actual_join_date",updateStrategy = FieldStrategy.IGNORED)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "实际入职日期", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDate actualJoinDate;
/**
* 上传的简历
* 上传的简历 cellType = ColumnType.IMAGE
*/
@TableField(value = "file")
@Excel(name = "简历", cellType = Excel.ColumnType.TEXT)
private String file;
/**

View File

@ -80,7 +80,7 @@ public class ResumeStatVO {
@ApiModel
public static class FunnelData {
@ApiModelProperty(value = "")
private Double value;
private Long value;
@ApiModelProperty(value = "命名")
private String name;

View File

@ -73,6 +73,7 @@
<if test="req.secondPass!=null and req.secondPass!=''">and second_pass = #{req.secondPass}</if>
<if test="req.finalPass!=null and req.finalPass!=''">and final_pass = #{req.finalPass}</if>
<if test="req.isPass!=null and req.isPass!=''">and is_pass = #{req.isPass}</if>
<if test="req.id!=null">and id = #{req.id}</if>
order by
case when topping = b'1' then 0 else 1 end,
topping_time desc,

View File

@ -92,4 +92,8 @@ public interface SysDictDataMapper
* @return 结果
*/
public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType);
SysDictData selectByChannel(@Param("channel") String channel, @Param("recruitProcessChannel") String recruitProcessChannel);
}

View File

@ -57,4 +57,6 @@ public interface ISysDictDataService
* @return 结果
*/
public int updateDictData(SysDictData dictData);
SysDictData selectByChannel(String channel, String recruit_process_channel);
}

View File

@ -108,4 +108,9 @@ public class SysDictDataServiceImpl implements ISysDictDataService
}
return row;
}
@Override
public SysDictData selectByChannel(String channel, String recruit_process_channel) {
return dictDataMapper.selectByChannel(channel,recruit_process_channel);
}
}

View File

@ -121,4 +121,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
)
</insert>
<select id="selectByChannel" resultMap="SysDictDataResult">
SELECT *
FROM sys_dict_data
WHERE dict_type = #{recruitProcessChannel}
AND dict_value =#{channel}
</select>
</mapper>