package cn.fw.morax.service.biz.eval; import cn.fw.ehr.sdk.api.enums.StaffStatusEnum; import cn.fw.morax.common.utils.PublicUtil; import cn.fw.morax.domain.db.eval.EvalGroup; import cn.fw.morax.domain.db.eval.EvalGroupUser; import cn.fw.morax.domain.dto.query.ShopsPostsQueryDTO; import cn.fw.morax.domain.enums.EvalScopeEnum; import cn.fw.morax.domain.enums.KpiIgnoreCauseEnum; import cn.fw.morax.domain.vo.eval.EvalGroupUserVO; import cn.fw.morax.rpc.ehr.EhrRpcService; import cn.fw.morax.rpc.ehr.dto.PerformanceStaffDTO; import cn.fw.morax.service.data.eval.EvalGroupService; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; import static cn.fw.common.businessvalidator.Validator.BV; /** * @author jiangchao * @des: 考评组人员 * @date 2023/1/12 14:40 */ @Service @Slf4j @RequiredArgsConstructor public class EvalGroupUserBizService { private final EvalGroupService evalGroupService; private final EhrRpcService ehrRpcService; private final StringRedisTemplate stringRedisTemplate; @Value("${spring.cache.custom.global-prefix}:kpi:anticipated_date:") @Getter private String startKpiKey; @Value("${spring.cache.custom.global-prefix}:kpi:await_job:anticipated_date:") @Getter private String awaitJobAfterStartKpiKey; /** * 所有集团生效的绩效组配置 */ public List postEffectEvalGroups(LocalDate queryDate){ List evalGroups = evalGroupService.getAllEffectGroups(queryDate); if (PublicUtil.isEmpty(evalGroups)) { log.info("时间:{},没有正在生效中的考评组配置", DateTimeFormatter.ofPattern("yyyy-MM-dd").format(queryDate)); return new ArrayList<>(); } //过滤岗位为空考评组 List postEvalGroups = evalGroups.stream() .filter(evalGroup -> PublicUtil.isNotEmpty(evalGroup.getPostIds())).collect(Collectors.toList()); return postEvalGroups; } /** * 构建绩效组人员 * * @return */ @Transactional(rollbackFor = Exception.class) public List fetchBuildKpiUser(Long postId, EvalGroup evalGroup, LocalDate localDate){ List evalGroupUsers = this.fetchPostUsers(postId, evalGroup.getShopIds(), localDate); //构建考评组信息 for (EvalGroupUser user : evalGroupUsers) { user.setEvalGroupRankId(evalGroup.getEvalGroupRankId()); user.setEvalGroupRankStageId(evalGroup.getEvalGroupRankStageId()); user.setEvalGroupId(evalGroup.getId()); user.setEgc(evalGroup.getEgc()); } return evalGroupUsers; } /** * 获取岗位人员 * * @param postId * @param shopIds * @param dataDate * @return */ public List fetchPostUsers(Long postId, List shopIds, LocalDate dataDate) { List staffs = ehrRpcService.queryKpiStaff(postId, shopIds); if (PublicUtil.isEmpty(staffs)) { return new ArrayList<>(); } List result = Lists.newArrayListWithCapacity(staffs.size()); for (PerformanceStaffDTO staff : staffs) { EvalGroupUser evalGroupUser = this.convertToGroupUser(staff, dataDate); //处理绩效考核数据 this.handleStaffKpi(staff, evalGroupUser); result.add(evalGroupUser); } return result; } /** * 获取岗位人员 * * @param postIds * @param shopIds * @param dataDate * @return */ public List fetchPostUsers(List postIds, List shopIds, LocalDate dataDate) { List staffs = Lists.newArrayListWithCapacity(100); for (Long postId : postIds) { staffs.addAll(ehrRpcService.queryKpiStaff(postId, shopIds)); } if (PublicUtil.isEmpty(staffs)) { return new ArrayList<>(); } List result = Lists.newArrayListWithCapacity(staffs.size()); for (PerformanceStaffDTO staff : staffs) { EvalGroupUser evalGroupUser = this.convertToGroupUser(staff, dataDate); //处理绩效考核数据 this.handleStaffKpi(staff, evalGroupUser); result.add(evalGroupUser); } return result; } /** * 构建绩效组人员 * * @return */ @Transactional(rollbackFor = Exception.class) public List fetchBuildImportUser(Long postId, List shopIds, LocalDate localDate){ List staffs = ehrRpcService.queryKpiStaff(postId, new ArrayList<>(shopIds)); if (PublicUtil.isEmpty(staffs)) { return new ArrayList<>(); } List result = Lists.newArrayListWithCapacity(staffs.size()); for (PerformanceStaffDTO staff : staffs) { EvalGroupUser evalGroupUser = this.convertToGroupUser(staff, localDate); //处理绩效考核数据 this.handleStaffKpi(staff, evalGroupUser); result.add(evalGroupUser); } return result; } /** * 处理员工绩效考核数据 * 试用期、转正两个月内、调岗两个月内、待岗两个月内、申请不进行绩效考核 * * @param staff * @param evalUser */ public void handleStaffKpi(PerformanceStaffDTO staff, EvalGroupUser evalUser) { LocalDate currentTime = LocalDate.now(); KpiIgnoreCauseEnum ignoreCauseEnum = null; LocalDate startKpiDate = null; Long staffId = staff.getId(); try { //试用期不计算绩效 绩效开始时间为试用期转正的两个月后 if (StaffStatusEnum.PROBATION.getValue().equals(staff.getStaffStatus())) { startKpiDate = PublicUtil.getKpiDateOfProbationStaff(PublicUtil.isNotEmpty(staff.getEntryDate()) ? staff.getEntryDate() : LocalDate.now()); ignoreCauseEnum = KpiIgnoreCauseEnum.PROBATION; } //转正两个月内不计算绩效 绩效开始时间为转正的两个月后 if (StaffStatusEnum.REGULAR.getValue().equals(staff.getStaffStatus())) { LocalDate regularDate = PublicUtil.isNotEmpty(staff.getRegularDate()) ? staff.getRegularDate() : staff.getEntryDate(); LocalDate newStartKpiDate = PublicUtil.getKpiDateOfRegularStaff(regularDate); if (this.checkNewStartKpiDateValid(startKpiDate, newStartKpiDate, currentTime)) { ignoreCauseEnum = KpiIgnoreCauseEnum.TURN_POSITIVE_AFTER; startKpiDate = newStartKpiDate; } } //绩效组变动不足完整2个月 绩效开始时间为获得岗位的两个月后 LocalDate postChangeStartKpiDate = this.getStartKpiDate(staffId); if (PublicUtil.isNotEmpty(postChangeStartKpiDate)) { if (this.checkNewStartKpiDateValid(startKpiDate, postChangeStartKpiDate, currentTime)) { ignoreCauseEnum = KpiIgnoreCauseEnum.TRANSFER_NEW_POSITION_AFTER; startKpiDate = postChangeStartKpiDate; } } //主动申请不计算绩效 绩效开始时间为设置的申请开始绩效考核时间 // KpiStarSpecialRule specialRule = userRuleMap.get(staffId); //开始考核日期 // if (PublicUtil.isNotEmpty(specialRule) && PublicUtil.isNotEmpty(specialRule.getEarliestStartingMonthly())) { // LocalDate newStartKpiDate = specialRule.getEarliestStartingMonthly(); // //当前时间在开始考核时间之之前 审批通过后,前面月份的绩效不纳入后续计算 // if (this.checkNewStartKpiDateValid(startKpiDate, newStartKpiDate, currentTime)) { // ignoreCauseEnum = KpiIgnoreCauseEnum.APPLY_NO_ASSESSMENT; // startKpiDate = newStartKpiDate; // } // } //待岗期间 if (PublicUtil.isNotEmpty(staff.getAwaitJob()) && staff.getAwaitJob()) { ignoreCauseEnum = KpiIgnoreCauseEnum.AWAIT_JOB_DURATION; //@todo 人事系统待岗中人员没有待岗结束时间 startKpiDate = null; } //待岗结束两个月期间 LocalDate afterAwaitJobStartKpiDate = this.getAfterAwaitJobStartKpiDate(staffId); if (PublicUtil.isNotEmpty(afterAwaitJobStartKpiDate) ) { if (this.checkNewStartKpiDateValid(startKpiDate, afterAwaitJobStartKpiDate, currentTime)) { ignoreCauseEnum = KpiIgnoreCauseEnum.AWAIT_JOB_AFTER; startKpiDate = afterAwaitJobStartKpiDate; } } if (PublicUtil.isNotEmpty(ignoreCauseEnum)) { this.handleIgnoredKpiGroupUser(evalUser, ignoreCauseEnum, startKpiDate); return; } //都未命中上面的条件就计算绩效,开始计算时间为转正两月后、获得新岗位两月后、申请开始考核时间任一 evalUser.setIgnored(Boolean.FALSE); }catch (Exception e) { evalUser.setIgnored(Boolean.TRUE); log.error("处理绩效组人员数据异常,员工信息:{}", staff, e); } } /** * 判断新绩效生效日期 是否有效 * * @param oldStartKpiDate * @param newStartKpiDate * @return */ private Boolean checkNewStartKpiDateValid(LocalDate oldStartKpiDate, LocalDate newStartKpiDate, LocalDate currentTime) { //当前时间在生效日期之后,计算绩效 if (currentTime.isAfter(newStartKpiDate)) { return Boolean.FALSE; } //旧生效时间为空 或者 新生效日期在旧生效日期之后 if (PublicUtil.isEmpty(oldStartKpiDate) || newStartKpiDate.isAfter(oldStartKpiDate)) { return Boolean.TRUE; } return Boolean.FALSE; } /** * 处理绩效忽略数据 * * @param evalUser * @param ignoreCauseEnum * @param anticipatedDate */ private void handleIgnoredKpiGroupUser(EvalGroupUser evalUser, KpiIgnoreCauseEnum ignoreCauseEnum, LocalDate anticipatedDate) { evalUser.setIgnored(Boolean.TRUE); if (PublicUtil.isNotEmpty(anticipatedDate)) { evalUser.setAnticipatedDate(anticipatedDate); } evalUser.setIgnoreCause(ignoreCauseEnum); evalUser.setIgnoreCauseDesc(ignoreCauseEnum.getName()); } /** * 检查员工是否在待岗保护期 * * @param userId */ public LocalDate getAfterAwaitJobStartKpiDate(Long userId) { String key = getAwaitJobAfterStartKpiKey() + userId; if (! stringRedisTemplate.hasKey(key)) { return null; } BoundValueOperations strOps = stringRedisTemplate.boundValueOps(key); LocalDate startKpiDate = LocalDate.parse(strOps.get()); LocalDate currentDate = LocalDate.now(); //当前时间在开始计算绩效时间 之后,已经开始计算绩效 if (currentDate.isAfter(startKpiDate)) { return null; } return startKpiDate; } /** * 获取开始计算绩效时间 * * @param userId */ public LocalDate getStartKpiDate(Long userId) { String key = getStartKpiKey() + userId; if (! stringRedisTemplate.hasKey(key)) { return null; } BoundValueOperations strOps = stringRedisTemplate.boundValueOps(key); LocalDate startKpiDate = LocalDate.parse(strOps.get()); LocalDate currentDate = LocalDate.now(); //当前时间在开始计算绩效时间 之后,已经开始计算绩效 if (currentDate.isAfter(startKpiDate)) { return null; } return startKpiDate; } /** * 转换员工基础信息 * * @param staff * @return */ private EvalGroupUser convertToGroupUser(PerformanceStaffDTO staff, LocalDate now){ EvalGroupUser evalGroupUser = EvalGroupUser.builder() .userId(staff.getId()) .userName(staff.getName()) .postId(staff.getPostId()) .postName(staff.getPostName()) .shopId(staff.getShopId()) .shopName(staff.getShopName()) .groupId(staff.getGroupId()) .build(); if (PublicUtil.isNotEmpty(now)) { evalGroupUser.setDataDate(now); } return evalGroupUser; } /** * 查询考评组人员 * * @param evalGroupId * @return */ public List queryEvalGroupStaffs(Long evalGroupId) { EvalGroup evalGroup = evalGroupService.getById(evalGroupId); BV.notNull(evalGroup, "考评配置不存在,请重试"); List evalGroupUsers = this.fetchPostUsers(evalGroup.getPostIds(), evalGroup.getShopIds(), null); List evalGroupUserVOS = PublicUtil.copyList(evalGroupUsers, EvalGroupUserVO.class); return evalGroupUserVOS; } /** * 查询考评组排名所有人员 * * @param kpiGroupIds * @return */ public List queryRankStaffs(List kpiGroupIds) { List evalGroupUserVOS = Lists.newArrayListWithCapacity(100); for (Long kpiGroupId : kpiGroupIds) { evalGroupUserVOS.addAll(this.queryEvalGroupStaffs(kpiGroupId)); } return evalGroupUserVOS; } /** * 查询考评组排名人员 * * @param dto * @return */ public List queryShopPostStaffs(ShopsPostsQueryDTO dto) { List evalGroupUsers = this.fetchPostUsers(dto.getPostIds(), dto.getShopIds(), null); List evalGroupUserVOS = PublicUtil.copyList(evalGroupUsers, EvalGroupUserVO.class); return evalGroupUserVOS; } }