package cn.fw.morax.service.biz.kpi; import cn.fw.common.exception.BusinessException; import cn.fw.common.web.annotation.DisLock; import cn.fw.common.web.auth.LoginAuthBean; import cn.fw.morax.common.constant.Constant; import cn.fw.morax.common.pojo.event.ApprovalResultEvent; import cn.fw.morax.common.pojo.event.KpiStarAdjustmentEvent; import cn.fw.morax.common.utils.EventBusUtil; import cn.fw.morax.common.utils.PublicUtil; import cn.fw.morax.domain.db.ApprovalRecord; import cn.fw.morax.domain.db.kpi.KpiPool; import cn.fw.morax.domain.db.kpi.KpiStarSpecialRule; import cn.fw.morax.domain.db.salary.SalaryClosure; import cn.fw.morax.domain.dto.kpi.KpiStarSpecialRuleDTO; import cn.fw.morax.domain.dto.query.SalaryGroupStaffQueryDTO; import cn.fw.morax.domain.enums.SettingStatusEnum; import cn.fw.morax.domain.enums.StarLevelEnum; import cn.fw.morax.domain.vo.kpi.KpiStarSpecialRuleVO; import cn.fw.morax.domain.vo.salary.StaffSelectVO; import cn.fw.morax.rpc.ehr.EhrRpcService; import cn.fw.morax.rpc.ehr.dto.PerformanceStaffDTO; import cn.fw.morax.rpc.ehr.dto.StaffBaseInfoDTO; import cn.fw.morax.service.biz.ApprovalBizService; import cn.fw.morax.service.biz.salary.SalaryGeneralSettinBizService; import cn.fw.morax.service.biz.salary.SalarySettingCommonService; import cn.fw.morax.service.data.ApprovalRecordService; import cn.fw.morax.service.data.kpi.KpiPoolService; import cn.fw.morax.service.data.kpi.KpiStarSpecialRuleService; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.time.ZoneId; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; import static cn.fw.common.businessvalidator.Validator.BV; @RequiredArgsConstructor @Service @Slf4j public class KpiStarSpecialRuleBizService { private final SalarySettingCommonService salarySettingCommonService; private final SalaryGeneralSettinBizService salaryGeneralSettinBizService; private final KpiStarSpecialRuleService kpiStarSpecialRuleService; private final ApprovalRecordService approvalRecordService; private final ApprovalBizService approvalBizService; private final KpiPoolService kpiPoolService; private final KpiPoolBizService kpiPoolBizService; private final EhrRpcService ehrRpcService; @Value("${spring.cache.custom.global-prefix}:kpi:star:adjustment:") @Getter private String savePrefix; /** * 申请绩效星级特殊调整 * * @param dto */ @Transactional(rollbackFor = Exception.class) @DisLock(prefix = "#this.getSavePrefix()", key = "#dto.getUserId()", message = "保存中,请勿重复操作") public void applyKpiStarAdjustment(KpiStarSpecialRuleDTO dto, LoginAuthBean user) { Date expTime = this.checkAppealDate(dto.getUserId()); this.checkAdjustment(dto.getUserId()); KpiStarSpecialRule specialRule = this.convertToPo(dto); Boolean result = kpiStarSpecialRuleService.save(specialRule); BV.isTrue(result, Constant.SAVE_FAIL_RETRY_PROMPT); approvalBizService.applyKpiStarAdjustment(specialRule, user, expTime); } /** * 查询薪酬系统配置 * * @param * @return */ public Date checkAppealDate(Long userId) { StaffBaseInfoDTO staffBaseInfo = ehrRpcService.queryStaffBaseInfo(userId); BV.notNull(staffBaseInfo, "员工信息为空"); SalaryClosure salaryClosure = salarySettingCommonService.queryShopClosure(staffBaseInfo.getShopId(), YearMonth.now().minusMonths(1)); if (PublicUtil.isEmpty(salaryClosure)) { throw new BusinessException("上月还未关单,不能申请"); } LocalDateTime expTime = salaryClosure.getApproveEndTime().atTime(23,59,59); return Date.from(expTime.atZone(ZoneId.systemDefault()).toInstant()); } /** * 检查是否有审批中的星级特殊调整 * * @param userId */ public void checkAdjustment(Long userId) { Integer approvingRecords = kpiStarSpecialRuleService.count(Wrappers.lambdaQuery() .eq(KpiStarSpecialRule::getStatus, SettingStatusEnum.APPROVING) .eq(KpiStarSpecialRule::getUserId, userId) .eq(KpiStarSpecialRule::getYn, Boolean.TRUE) ); BV.isFalse(approvingRecords > 0, "已有申请中的星级考核调整"); } /** * 检查并转换dto * * @param dto * @return */ private KpiStarSpecialRule convertToPo(KpiStarSpecialRuleDTO dto) { Long groupId = dto.getGroupId(); KpiStarSpecialRule kpiStarSpecialRule = new KpiStarSpecialRule(); kpiStarSpecialRule.setUserId(dto.getUserId()); kpiStarSpecialRule.setUserName(dto.getUserName()); //几月开始绩效考核 if (PublicUtil.isNotEmpty(dto.getStartingKpiEnum())) { switch (dto.getStartingKpiEnum()) { case CURRENT_MONTH: { kpiStarSpecialRule.setEarliestStartingMonthly(PublicUtil.getCurMonthFirstDay()); break; } case NEXT_MONTH: { kpiStarSpecialRule.setEarliestStartingMonthly(PublicUtil.getNextMonthFirstDay()); break; } } } //未核发工资月度星级调整 if (PublicUtil.isNotEmpty(dto.getRevisedMonthly())) { kpiStarSpecialRule.setRevisedMonthly(dto.getRevisedMonthly().atDay(1)); kpiStarSpecialRule.setRevisedLevel(dto.getRevisedLevel()); } //上个月绩效考核不算,按C级计算工资 if (PublicUtil.isNotEmpty(dto.getExclusionLastMonth()) && dto.getExclusionLastMonth()) { kpiStarSpecialRule.setExclusionMonthly(PublicUtil.getCurMonthEndDay().plusMonths(-1)); } //免除D级次数 if (PublicUtil.isNotEmpty(dto.getRevokedTimes())) { Boolean revokedCorrect = dto.getRevokedTimes() >= 1 && dto.getRevokedTimes() <= 3; BV.isTrue(revokedCorrect, "免除D级次数只能在1-3次之间"); kpiStarSpecialRule.setRevokedTimes(dto.getRevokedTimes()); } kpiStarSpecialRule.setReason(dto.getReason()); kpiStarSpecialRule.setAttachment(dto.getAttachment()); kpiStarSpecialRule.setGroupId(groupId); kpiStarSpecialRule.setStatus(SettingStatusEnum.APPROVING); return kpiStarSpecialRule; } /** * 绩效星级考核调整审批 * * @param approvalRecord * @param resultEvent */ @Transactional(rollbackFor = Exception.class) public void approvalKpiStarAdjustment(ApprovalRecord approvalRecord, ApprovalResultEvent resultEvent) { log.info("收到绩效星级考核调整审批信息:{}", JSON.toJSONString(resultEvent)); boolean agree = Boolean.TRUE.equals(resultEvent.getAgree()); KpiStarSpecialRule specialRule = kpiStarSpecialRuleService.getById(approvalRecord.getDataId()); if (Objects.isNull(specialRule)) { return; } if (!agree) { specialRule.setStatus(SettingStatusEnum.INEFFECTIVE); } else { specialRule.setStatus(SettingStatusEnum.EFFECTIVE); } kpiStarSpecialRuleService.updateById(specialRule); this.adjustmentKpiData(specialRule); } /** * 调整绩效星级 * 1. 开始绩效考核月度 * 2. 未核发工资月度星级调整 * 3. 上个月绩效考核不算,按C级计算工资 * 4. 免除D级次数 * * @param specialRule */ @Transactional(rollbackFor = Exception.class) public void adjustmentKpiData(KpiStarSpecialRule specialRule) { if (!SettingStatusEnum.EFFECTIVE.equals(specialRule.getStatus())) { return; } Long userId = specialRule.getUserId(); //未设置开始绩效考核月度 Boolean noSetKpiStartMonthRule = true; //开始考核日期 if (PublicUtil.isNotEmpty(specialRule.getEarliestStartingMonthly())) { noSetKpiStartMonthRule = false; this.resetKpiDate(userId, YearMonth.from(specialRule.getEarliestStartingMonthly())); } //修正未核发工资月度星级 if (PublicUtil.isNotEmpty(specialRule.getRevisedMonthly())) { this.starAdjustmentEvent(specialRule.getRevisedLevel(), userId, YearMonth.from(specialRule.getRevisedMonthly())); } //哪个月绩效考核不算,按C级计算工资(只能算上月) if (PublicUtil.isNotEmpty(specialRule.getExclusionMonthly())) { this.starAdjustmentEvent(StarLevelEnum.C, userId, YearMonth.from(specialRule.getExclusionMonthly())); } //免除D级次数 if (PublicUtil.isNotEmpty(specialRule.getRevokedTimes())) { this.revokedKpiTimes(specialRule); } //没有开始绩效考核月度设置,就将绩效星级规则状态设置为失效 if (noSetKpiStartMonthRule) { kpiStarSpecialRuleService.update(Wrappers.lambdaUpdate() .set(KpiStarSpecialRule::getStatus, SettingStatusEnum.INEFFECTIVE) .eq(KpiStarSpecialRule::getId, specialRule.getId()) ); } } /** * 失效旧绩效星级特殊规则(开始绩效考核月度设置 使用) * * @param userId */ @Transactional(rollbackFor = Exception.class) public void inEffectiveOldSpecialRule(Long userId, Long specialRuleId) { List specialRules = kpiStarSpecialRuleService.list(Wrappers.lambdaQuery() .eq(KpiStarSpecialRule::getStatus, SettingStatusEnum.EFFECTIVE) .eq(KpiStarSpecialRule::getUserId, userId) .eq(KpiStarSpecialRule::getYn, Boolean.TRUE) .ne(KpiStarSpecialRule::getId, specialRuleId) ); if (PublicUtil.isEmpty(specialRules)) { return; } List inEffectiveRulesIds = Lists.newArrayList(); for (KpiStarSpecialRule rule : specialRules) { if (PublicUtil.isNotEmpty(rule.getEarliestStartingMonthly())) { inEffectiveRulesIds.add(rule.getId()); } } if (PublicUtil.isNotEmpty(inEffectiveRulesIds)) { kpiStarSpecialRuleService.update(Wrappers.lambdaUpdate() .set(KpiStarSpecialRule::getStatus, SettingStatusEnum.INEFFECTIVE) .in(KpiStarSpecialRule::getId, inEffectiveRulesIds) ); } } /** * 免除D级次数(最近一年之内) * * @param specialRule */ @Transactional(rollbackFor = Exception.class) public void revokedKpiTimes(KpiStarSpecialRule specialRule) { Long userId = specialRule.getUserId(); int revokedTimes = specialRule.getRevokedTimes(); YearMonth yearMonth = YearMonth.now().minusMonths(1L); List list = kpiPoolService.list(Wrappers.lambdaQuery() .eq(KpiPool::getGroupId, specialRule.getGroupId()) .eq(KpiPool::getUserId, userId) .eq(KpiPool::getInclusion, Boolean.TRUE) .eq(KpiPool::getYn, Boolean.TRUE) .le(KpiPool::getMonthly, yearMonth) .orderByDesc(KpiPool::getMonthly) .last(" limit 12 ") ); for (KpiPool kpiPool : list) { if (StarLevelEnum.D.equals(kpiPool.getStarLevel()) && revokedTimes > 0) { kpiPool.setRevoked(Boolean.TRUE); revokedTimes--; } } kpiPoolService.updateBatchById(list); } /** * 绩效星级特殊规则详情 * * @param kpiStarSpecialRuleId * @return */ public KpiStarSpecialRuleVO getKpiStarSpecialRuleDetail(Long kpiStarSpecialRuleId) { KpiStarSpecialRule kpiStarSpecialRule = kpiStarSpecialRuleService.getById(kpiStarSpecialRuleId); BV.notNull(kpiStarSpecialRule, "绩效星级特殊规则不存在"); KpiStarSpecialRuleVO kpiStarSpecialRuleVo = PublicUtil.copy(kpiStarSpecialRule, KpiStarSpecialRuleVO.class); KpiPool kpiPool = kpiPoolService.getOne(Wrappers.lambdaUpdate() .eq(KpiPool::getUserId, kpiStarSpecialRule.getUserId()) .eq(KpiPool::getYn, Boolean.TRUE) .orderByDesc(KpiPool::getId) .last("limit 1") // .ge(KpiPool::getMonthly, kpiStarSpecialRule.getRevisedMonthly()) ); kpiStarSpecialRuleVo.setRevisedMonthLevel(kpiPool.getStarLevel()); return kpiStarSpecialRuleVo; } /** * 绩效星级特殊调整人员 * * @param dto * @return */ public List queryStarAdjustmentStaffs(SalaryGroupStaffQueryDTO dto) { List staffs = ehrRpcService.queryKpiStaffByName(dto.getPostId(), dto.getShopIds(), dto.getStaffName()); LocalDate lastMonth = LocalDate.now().plusMonths(-1); List poolList = kpiPoolService.list(Wrappers.lambdaQuery() .eq(KpiPool::getPostId, dto.getPostId()) .in(KpiPool::getShopId, dto.getShopIds()) .eq(KpiPool::getMonthly, lastMonth) .like(PublicUtil.isNotEmpty(dto.getStaffName()), KpiPool::getUserName, dto.getStaffName()) .eq(KpiPool::getYn, Boolean.TRUE) ); Set staffIds = Sets.newHashSetWithExpectedSize(staffs.size() + poolList.size()); List selectVOS = staffs.stream().map(staffDTO -> { StaffSelectVO staffSelectVO = new StaffSelectVO(); staffIds.add(staffDTO.getId()); staffSelectVO.setId(staffDTO.getId()); staffSelectVO.setName(staffDTO.getName()); return staffSelectVO; }).collect(Collectors.toList()); poolList.forEach(pool -> { if (staffIds.contains(pool.getUserId())) { return; } StaffSelectVO staffSelectVO = new StaffSelectVO(); staffIds.add(pool.getUserId()); staffSelectVO.setId(pool.getUserId()); staffSelectVO.setName(pool.getUserName()); selectVOS.add(staffSelectVO); }); return selectVOS; } /** * 绩效星级特殊调整人员 * * @param user * @return */ public List queryLastMonthManageStaffs(LoginAuthBean user) { Long userId = user.getUserId(); this.checkAppealDate(userId); List managerStaffs = ehrRpcService.queryManageStaffs(userId); if (PublicUtil.isEmpty(managerStaffs)) { managerStaffs = new ArrayList<>(); } LocalDateTime lastMonth = LocalDateTime.now().minusMonths(1); Date firstDayOfLastMonth = Date.from(lastMonth.with(TemporalAdjusters.firstDayOfMonth()).atZone(ZoneId.systemDefault()).toInstant()); Date endDayOfLastMonth = Date.from(lastMonth.with(TemporalAdjusters.lastDayOfMonth()).atZone(ZoneId.systemDefault()).toInstant()); List leaveManagerStaffs = ehrRpcService.queryLeaveManageStaffs(userId, firstDayOfLastMonth, endDayOfLastMonth); if (PublicUtil.isNotEmpty(leaveManagerStaffs)) { managerStaffs.addAll(leaveManagerStaffs); } List selectVOS = managerStaffs.stream() .filter(staff -> (!staff.getId().equals(userId))) .map(staff -> new StaffSelectVO(staff.getId(), staff.getName())).collect(Collectors.toList()); return selectVOS; } /** * 查询员工绩效星级 * * @param userId * @return */ public String getStaffKpiStar(Long userId, YearMonth yearMonth) { KpiPool pool = kpiPoolService.getOne(Wrappers.lambdaQuery() .eq(KpiPool::getMonthly, yearMonth) .eq(KpiPool::getUserId, userId) .eq(KpiPool::getInclusion, Boolean.TRUE) .eq(KpiPool::getYn, Boolean.TRUE) .orderByAsc(KpiPool::getId) .last("limit 1") ); if (PublicUtil.isEmpty(pool)) { return null; } //当实际星级 和 绩效星级不一致,可能绩效星级已经被调整,返回实际星级 if (PublicUtil.isNotEmpty(pool.getActualStar()) && (!pool.getStarLevel().equals(pool.getActualStar()))) { return pool.getActualStar().getName(); } return pool.getStarLevel().getName(); } /** * 绩效星级特殊调整详情 * * @param approvalNo */ public KpiStarSpecialRuleVO getKpiStarAdjustment(String approvalNo) { ApprovalRecord approvalRecord = approvalRecordService.getOne(Wrappers.lambdaQuery() .eq(ApprovalRecord::getApprovalNo, approvalNo)); BV.notNull(approvalRecord, "绩效星级特殊调整审批不存在"); KpiStarSpecialRule specialRule = kpiStarSpecialRuleService.getById(approvalRecord.getDataId()); BV.notNull(specialRule, "绩效星级特殊调整审批不存在"); KpiStarSpecialRuleVO kpiStarSpecialRuleVO = PublicUtil.copy(specialRule, KpiStarSpecialRuleVO.class); if (PublicUtil.isNotEmpty(specialRule.getRevisedMonthly())) { KpiPool kpiPool = kpiPoolService.getOne(Wrappers.lambdaUpdate() .eq(KpiPool::getUserId, specialRule.getUserId()) .eq(KpiPool::getMonthly, YearMonth.from(specialRule.getRevisedMonthly())) .eq(KpiPool::getYn, Boolean.TRUE) .orderByDesc(KpiPool::getId) .last("limit 1") // .ge(KpiPool::getMonthly, kpiStarSpecialRule.getRevisedMonthly()) ); if (PublicUtil.isNotEmpty(kpiPool)) { kpiStarSpecialRuleVO.setRevisedMonthLevel(kpiPool.getStarLevel()); } } return kpiStarSpecialRuleVO; } /** * 修正未核发工资月度星级 * * @param revisedLevel * @param userId * @param monthly */ @Transactional(rollbackFor = Exception.class) public void starAdjustmentEvent(StarLevelEnum revisedLevel, Long userId, YearMonth monthly) { kpiPoolService.update(Wrappers.lambdaUpdate() .set(KpiPool::getActualStar, revisedLevel) .eq(KpiPool::getUserId, userId) .eq(KpiPool::getMonthly, monthly) ); EventBusUtil.asyncPost(KpiStarAdjustmentEvent.builder() .userId(userId) .yearMonth(monthly) .build()); } /** * 几月开始考核,将几月之前绩效设置为不参与计算 * * @param userId * @param monthly */ @Transactional(rollbackFor = Exception.class) public void resetKpiDate(Long userId, YearMonth monthly) { kpiPoolService.update(Wrappers.lambdaUpdate() .set(KpiPool::getInclusion, Boolean.FALSE) .eq(KpiPool::getUserId, userId) .lt(KpiPool::getMonthly, monthly) .eq(KpiPool::getYn, Boolean.TRUE) ); } }