EvalGroupUserBizService.java 14.5 KB
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<EvalGroup> postEffectEvalGroups(LocalDate queryDate){
        List<EvalGroup> evalGroups = evalGroupService.getAllEffectGroups(queryDate);
        if (PublicUtil.isEmpty(evalGroups)) {
            log.info("时间:{},没有正在生效中的考评组配置", DateTimeFormatter.ofPattern("yyyy-MM-dd").format(queryDate));
            return new ArrayList<>();
        }
        //过滤岗位为空考评组
        List<EvalGroup> postEvalGroups = evalGroups.stream()
                .filter(evalGroup -> PublicUtil.isNotEmpty(evalGroup.getPostIds())).collect(Collectors.toList());
        return postEvalGroups;
    }

    /**
     * 构建绩效组人员
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public List<EvalGroupUser> fetchBuildKpiUser(Long postId, EvalGroup evalGroup, LocalDate localDate){
        List<EvalGroupUser> 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<EvalGroupUser> fetchPostUsers(Long postId, List<Long> shopIds, LocalDate dataDate) {
        List<PerformanceStaffDTO> staffs = ehrRpcService.queryKpiStaff(postId, shopIds);
        if (PublicUtil.isEmpty(staffs)) {
            return new ArrayList<>();
        }
        List<EvalGroupUser> 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<EvalGroupUser> fetchPostUsers(List<Long> postIds, List<Long> shopIds, LocalDate dataDate) {
        List<PerformanceStaffDTO> staffs = Lists.newArrayListWithCapacity(100);
        for (Long postId : postIds) {
            staffs.addAll(ehrRpcService.queryKpiStaff(postId, shopIds));
        }
        if (PublicUtil.isEmpty(staffs)) {
            return new ArrayList<>();
        }
        List<EvalGroupUser> 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<EvalGroupUser> fetchBuildImportUser(Long postId, List<Long> shopIds, LocalDate localDate){
        List<PerformanceStaffDTO> staffs = ehrRpcService.queryKpiStaff(postId, new ArrayList<>(shopIds));
        if (PublicUtil.isEmpty(staffs)) {
            return new ArrayList<>();
        }
        List<EvalGroupUser> 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<String, String> 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<String, String> 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<EvalGroupUserVO> queryEvalGroupStaffs(Long evalGroupId) {
        EvalGroup evalGroup = evalGroupService.getById(evalGroupId);
        BV.notNull(evalGroup, "考评配置不存在,请重试");
        List<EvalGroupUser> evalGroupUsers = this.fetchPostUsers(evalGroup.getPostIds(), evalGroup.getShopIds(), null);
        List<EvalGroupUserVO> evalGroupUserVOS = PublicUtil.copyList(evalGroupUsers, EvalGroupUserVO.class);
        return evalGroupUserVOS;
    }


    /**
     * 查询考评组排名所有人员
     *
     * @param kpiGroupIds
     * @return
     */
    public List<EvalGroupUserVO> queryRankStaffs(List<Long> kpiGroupIds) {
        List<EvalGroupUserVO> evalGroupUserVOS = Lists.newArrayListWithCapacity(100);
        for (Long kpiGroupId : kpiGroupIds) {
            evalGroupUserVOS.addAll(this.queryEvalGroupStaffs(kpiGroupId));
        }
        return evalGroupUserVOS;
    }

    /**
     * 查询考评组排名人员
     *
     * @param dto
     * @return
     */
    public List<EvalGroupUserVO> queryShopPostStaffs(ShopsPostsQueryDTO dto) {
        List<EvalGroupUser> evalGroupUsers = this.fetchPostUsers(dto.getPostIds(), dto.getShopIds(), null);
        List<EvalGroupUserVO> evalGroupUserVOS = PublicUtil.copyList(evalGroupUsers, EvalGroupUserVO.class);
        return evalGroupUserVOS;
    }

}