package com.kcim.service.impl; import com.kcim.common.constants.Constant; import com.kcim.common.constants.NumberConstant; import com.kcim.common.constants.ParameterConstant; import com.kcim.common.enums.EmpCostComputeTypeEnum; import com.kcim.common.exception.CostException; import com.kcim.common.util.BeanUtil; import com.kcim.common.util.UserContext; import com.kcim.dao.model.ComputeEmpCost; import com.kcim.dao.model.EmpCostMap; import com.kcim.dao.model.EmpCostType; import com.kcim.dao.model.ImportEmpCost; import com.kcim.dao.repository.ComputeEmpCostRepository; import com.kcim.dao.repository.EmpCostMapRepository; import com.kcim.dao.repository.EmpCostTypeRepository; import com.kcim.dao.repository.ImportEmpCostRepository; import com.kcim.service.CenterService; import com.kcim.service.EmpCostCalculateService; import com.kcim.vo.CommonParameterVo; import com.kcim.vo.DictDataVo; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.stream.Collectors; import static com.kcim.common.enums.EmpCostComputeTypeEnum.POSITION; /** * @program: CostAccount * @description: 单位人事成本计算实现类 * @author: Wang.YS * @create: 2023-10-23 16:11 **/ @Service("EmpCostCalculateService") @Slf4j @AllArgsConstructor public class EmpCostCalculateServiceImpl implements EmpCostCalculateService { /** * 月度人事成本计算 */ ComputeEmpCostRepository repository; /** * 人事分类字典 */ EmpCostTypeRepository empCostTypeRepository; /** * 人事分类与人员对照 */ EmpCostMapRepository empCostMapRepository; /** * 人事成本导入数据 */ ImportEmpCostRepository importEmpCostRepository; CenterService centerService; /** * 成本核算-单位人事成本计算-获取计算列表 * * @param computeDate 核算年月 * @param filter 过滤 人事分类名称 * @return 列表 */ @Override public Object getEmpCostCalculateList(String computeDate, String filter) { if(!StringUtils.isEmpty(filter)){ List filterList = repository.getByComputeDate(computeDate, filter); if(!CollectionUtils.isEmpty(filterList)){ List parentList = repository.getByParentComputeDate(computeDate); Map parentMap = parentList.stream().collect(Collectors.toMap(ComputeEmpCost::getCode, computeEmpCost -> computeEmpCost, (a, b) -> b)); //先取出过滤条件中的 上层数据 List collect = filterList.stream().filter(f -> f.getParentCode().equals(NumberConstant.ZERO_S)).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(collect)){ filterList.removeAll(collect); } //子集去除上层数据 是否还有数据 if(!CollectionUtils.isEmpty(filterList)){ //找出符合条件的 子集 没有上层的数据 在所有上层数据中找到父类用于显示 for (ComputeEmpCost computeEmpCost : filterList) { //组装成符合条件的上层数据中 collect.add(parentMap.get(computeEmpCost.getParentCode())); } //可能存在重复 去重 用于显示的最外层 List collectParent = collect.stream().distinct().collect(Collectors.toList()); Map> map = filterList.stream().collect(Collectors.groupingBy(ComputeEmpCost::getParentCode)); for (ComputeEmpCost empCost : collectParent) { List childList = map.get(empCost.getCode()); if(!CollectionUtils.isEmpty(childList)){ empCost.setChildList(childList); } } return collectParent; }else { return collect; } }else { return new ArrayList<>(); } }else { List list = repository.getByComputeDate(computeDate); if(!CollectionUtils.isEmpty(list)){ Map> map = list.stream().collect(Collectors.groupingBy(ComputeEmpCost::getParentCode)); List parent = map.get(NumberConstant.ZERO_S); for (ComputeEmpCost empCost : parent) { List childList = map.get(empCost.getCode()); if(!CollectionUtils.isEmpty(childList)){ empCost.setChildList(childList); } } return parent; }else { return new ArrayList<>(); } } } /** * 成本核算-单位人事成本计算-计算 * * @param computeDate 核算年月 */ @Override public void empCostCalculate(String computeDate) { List list = empCostTypeRepository.getList(); if(CollectionUtils.isEmpty(list)){ log.error("当前院区未维护人事分类,计算中止"); throw new CostException("当前院区未维护人事分类,计算中止"); } List empCostMapList = empCostMapRepository.getList(); if(CollectionUtils.isEmpty(empCostMapList)){ log.info("当前院区人事分类未对照人员信息,计算中止"); throw new CostException("当前院区人事分类未对照人员信息,计算中止"); } List importEmpCosts = importEmpCostRepository.getByComputeDate(computeDate); if(CollectionUtils.isEmpty(importEmpCosts)){ log.info("当前核算年月【"+computeDate+"】未导入人事成本数据"); throw new CostException("当前核算年月【"+computeDate+"】未导入人事成本数据,计算中止"); } //获取中台科室字典 Map department = centerService.getDepartment(); if(CollectionUtils.isEmpty(department)){ throw new CostException("未获取到当前院区科室信息,计算中止"); } //获取中台岗位字典 DictDataVo dict = centerService.getCenterDict(Constant.POSITION); List dataVoList = dict.getDataVoList(); Map positionMap = dataVoList.stream().collect(Collectors.toMap(DictDataVo::getCode, DictDataVo::getName, (a, b) -> b)); //获取中台应工作天数字典 DictDataVo monthWorkdays = centerService.getCenterDict(Constant.MONTH_WORKDAYS); Map monthWorkdayMap = monthWorkdays.getDataVoList().stream().collect(Collectors.toMap(DictDataVo::getCode, DictDataVo::getExpandOne, (a, b) -> b)); String currentWorkDay = monthWorkdayMap.get(computeDate); //获取每日工时参数 CommonParameterVo parameter = centerService.getParameter(ParameterConstant.DAY_HOUR); String dayHour = parameter.getValue(); if(StringUtils.isEmpty(dayHour)){ throw new CostException("中台未找到每日工时参数, 请确认参数数据是否正确"); } Map importEmpCostMap = importEmpCosts.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b)); Map> costTypeGroup = empCostMapList.stream().collect(Collectors.groupingBy(EmpCostMap::getCostTypeCode)); ComputeEmpCost computeEmpCost = new ComputeEmpCost(); computeEmpCost.setHospId(UserContext.getHospId()); computeEmpCost.setComputeDate(computeDate); computeEmpCost.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId())); computeEmpCost.setCreateTime(new Date()); List saveList = new ArrayList<>(); for (EmpCostType empCostType : list) { ComputeEmpCost empCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class); String code = empCostType.getCostTypeCode(); empCost.setCode(code); empCost.setName(empCostType.getCostTypeName()); empCost.setParentCode(NumberConstant.ZERO_S); empCost.setEmpType(code); String computeType = empCostType.getComputeType(); if(!CollectionUtils.isEmpty(costTypeGroup)){ //有子类 计算子类项目 List empCostMaps = costTypeGroup.get(code); if(!CollectionUtils.isEmpty(empCostMaps)){ List collect = empCostMaps.stream().map(EmpCostMap::getAccount).collect(Collectors.toList()); List costTypeEmpCost = collect.stream().map(importEmpCostMap::get).filter(Objects::nonNull).collect(Collectors.toList()); log.info("----for-8----"+costTypeEmpCost.toString()); if(!CollectionUtils.isEmpty(costTypeEmpCost)){ // 实际编制人数 int empNum = costTypeEmpCost.size(); empCost.setEmpNum(empNum); setSaveCost(currentWorkDay, dayHour, empCost, empNum, costTypeEmpCost); if(computeType.equals(EmpCostComputeTypeEnum.DEPARTMENT.getCode())){ //按科室计算子类 Map empCostMap = costTypeEmpCost.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b)); Map> departGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getDepartmentCode)); for (String s : departGroup.keySet()) { ComputeEmpCost empChildCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class); //单个科室所有人员 List empCostMaps1 = departGroup.get(s); //人员工号 List collect1 = empCostMaps1.stream().map(EmpCostMap::getAccount).collect(Collectors.toList()); //获取实院导入数据 List costTypeChildEmpCost = collect1.stream().map(empCostMap::get).filter(Objects::nonNull).collect(Collectors.toList()); empChildCost.setCode(s); empChildCost.setParentCode(code); empChildCost.setEmpType(code); empChildCost.setName(department.get(s)); if(!CollectionUtils.isEmpty(costTypeChildEmpCost)){ empChildCost.setEmpNum(costTypeChildEmpCost.size());// 人数 setSaveCost(currentWorkDay, dayHour, empChildCost, costTypeChildEmpCost.size(), costTypeChildEmpCost); saveList.add(empChildCost); } } } else if (computeType.equals(POSITION.getCode())) { //按岗位计算子类 Map empCostMap = costTypeEmpCost.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b)); Map> positionGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getPosition)); for (String s : positionGroup.keySet()) { ComputeEmpCost empChildCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class); //单个科室所有人员 List empCostMaps1 = positionGroup.get(s); //人员工号 List collect1 = empCostMaps1.stream().map(EmpCostMap::getAccount).collect(Collectors.toList()); //获取实院导入数据 List costTypeChildEmpCost = collect1.stream().map(empCostMap::get).filter(Objects::nonNull).collect(Collectors.toList()); empChildCost.setCode(s); empChildCost.setParentCode(code); empChildCost.setEmpType(code); empChildCost.setName(positionMap.get(s)); if(!CollectionUtils.isEmpty(costTypeChildEmpCost)){ empChildCost.setEmpNum(costTypeChildEmpCost.size());// 实际总工时 setSaveCost(currentWorkDay, dayHour, empChildCost, costTypeChildEmpCost.size(), costTypeChildEmpCost); saveList.add(empChildCost); } } } } } }else { //子类人员为空 无子集 empCost.setEmpNum(NumberConstant.ZERO); // 实际总工时 empCost.setActualHour(BigDecimal.ZERO); // 预计总工时 预计总工时=应上班天数*实际编制人数*每日工时 empCost.setExpectHour(BigDecimal.ZERO); // 总成本 empCost.setTotalCost(BigDecimal.ZERO); // 人均成本 empCost.setAvgCost(BigDecimal.ZERO); // 每分钟成本 empCost.setMinuteCost(BigDecimal.ZERO); } saveList.add(empCost); } if(!CollectionUtils.isEmpty(saveList)){ //判断当前核算年月是否有数据 有 作废 无保存 repository.removeByComputeDate(computeDate); repository.saveBatch(saveList); } } private void setSaveCost(String currentWorkDay, String dayHour, ComputeEmpCost empCost, int empNum, List costTypeEmpCost) { // 实际总工时 BigDecimal totalHour = computeTotalHour(costTypeEmpCost); empCost.setActualHour(totalHour); // 预计总工时 预计总工时=应上班天数*实际编制人数*每日工时 BigDecimal expectHour = computeExpectHour(empNum, currentWorkDay, dayHour); empCost.setExpectHour(expectHour); // 总成本 BigDecimal totalCost = computeTotalCost(costTypeEmpCost); empCost.setTotalCost(totalCost); // 人均成本 BigDecimal avgCost = computeAvgCost(totalCost, empNum); empCost.setAvgCost(avgCost); // 每分钟成本 BigDecimal minuteCost = computeMinuteCost(totalCost, totalHour); empCost.setMinuteCost(minuteCost); } private BigDecimal computeExpectHour(int size, String i, String v) { double hour = size * Double.parseDouble(i) * Double.parseDouble(v); return BigDecimal.valueOf(hour); } /** * 计算每分钟成本 总成本/实际工时/60 * @param total 总成本 * @param totalHour 总工时 * @return 每分钟成本 */ private BigDecimal computeMinuteCost(BigDecimal total, BigDecimal totalHour) { return total.divide(totalHour,4,RoundingMode.HALF_UP).divide(BigDecimal.valueOf(60),4,RoundingMode.HALF_UP).setScale(4,RoundingMode.HALF_UP); } /** * 计算总工时 * @param costTypeEmpCost 导入数据 * @return 总工时 */ private BigDecimal computeTotalHour(List costTypeEmpCost) { BigDecimal total = new BigDecimal("0.0000"); for (ImportEmpCost importEmpCost : costTypeEmpCost) { total = total.add(importEmpCost.getHour()); } return total; } /** * 计算人均成本 总成本/实际编制人数 * @param total 总成本 * @param size 实际编制人数 * @return 人均成本 */ private BigDecimal computeAvgCost(BigDecimal total, int size) { return total.divide(BigDecimal.valueOf(size),4,RoundingMode.HALF_UP); } /** * 计算总成本 * @param costTypeEmpCost 列表数据 * @return 总成本 */ private BigDecimal computeTotalCost(List costTypeEmpCost) { BigDecimal total = new BigDecimal("0.0000"); for (ImportEmpCost importEmpCost : costTypeEmpCost) { total = total.add(importEmpCost.getCost()); } return total; } }