package com.imed.costaccount.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.imed.costaccount.common.enums.CalcTypeEnum; import com.imed.costaccount.common.enums.ReportTypeEnum; import com.imed.costaccount.common.exception.CostException; import com.imed.costaccount.common.util.BeanUtil; import com.imed.costaccount.common.util.PageUtils; import com.imed.costaccount.model.*; import com.imed.costaccount.model.vo.HospProfitVO; import com.imed.costaccount.model.vo.RelationVO; import com.imed.costaccount.service.*; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.imed.costaccount.mapper.HospProfitAndLossMapper; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @Service("hospProfitAndLossService") public class HospProfitAndLossServiceImpl extends ServiceImpl implements HospProfitAndLossService { private final ReportFormService reportFormService; private final IncomeCollectionService collectionService; private final AllocationQueryService allocationQueryService; private final AllocationService allocationService; private final ReportRelationService reportRelationService; private final CostShareLevelService shareLevelService; private final CostOtherPaymentsDataService otherPaymentsDataService; public HospProfitAndLossServiceImpl(ReportFormService reportFormService, IncomeCollectionService collectionService, AllocationQueryService allocationQueryService, AllocationService allocationService, ReportRelationService reportRelationService, CostShareLevelService shareLevelService, CostOtherPaymentsDataService otherPaymentsDataService) { this.reportFormService = reportFormService; this.collectionService = collectionService; this.allocationQueryService = allocationQueryService; this.allocationService = allocationService; this.reportRelationService = reportRelationService; this.shareLevelService = shareLevelService; this.otherPaymentsDataService = otherPaymentsDataService; } /** * 计算全院损益 * * @param date yyyy-MM-dd 时间 * @param hospId 医院id */ @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class) public void calc(String date, Long hospId) { DateTime parse = DateUtil.parse(date); int year = DateUtil.year(parse); int month = DateUtil.month(parse) + 1; this.remove(new LambdaQueryWrapper().eq(HospProfitAndLoss::getDateYear, year).eq(HospProfitAndLoss::getDateMonth, month).eq(HospProfitAndLoss::getHospId, hospId)); // 得到全院损益计算报表 List reportForms = reportFormService.getListByReportType(hospId, ReportTypeEnum.HOSP_PROFIT_LOSS.getType()); if (CollUtil.isEmpty(reportForms)) { throw new CostException("医院未设置全院损益计算报表"); } // 得到这个月所有收入数据 List incomes = collectionService.getCollectionsByDate(year, month, hospId); if (incomes.isEmpty()) { throw new CostException("医院未归集本月收入数据"); } // 得到这个月的所有成本数据 List allocationQueries = allocationQueryService.getAllByDate(hospId, year, month); if (allocationQueries.isEmpty()) { throw new CostException("医院未分摊本月数据"); } List list = new ArrayList<>(); reportForms.forEach(i -> { Integer calcType = i.getCalcType(); if (calcType == CalcTypeEnum.BY_ACCOUNT.getType()) { // 按会计科目计算 HospProfitAndLoss loss = calcByAccount(hospId, i, incomes, allocationQueries); if (Objects.isNull(loss)) { return; } loss.setDateMonth(month); loss.setDateYear(year); list.add(loss); return; } else if (calcType == CalcTypeEnum.BY_SHARE_LEVEL.getType()) { // 分摊层级计算 HospProfitAndLoss loss = calcByShareLevel(hospId, i, year, month); if (Objects.isNull(loss)) { return; } loss.setDateMonth(month); loss.setDateYear(year); list.add(loss); return; } else if (calcType == CalcTypeEnum.LITTER_COUNT.getType()) { return; } else if (calcType == CalcTypeEnum.CALC_FORMULA.getType()) { // todo 留在最后加减 return; } else if (calcType == CalcTypeEnum.BY_RESPONSIBILITY.getType()) { // 责任中心 HospProfitAndLoss loss = calcByResponsibility(hospId, i, incomes, allocationQueries); if (loss == null) { return; } loss.setDateMonth(month); loss.setDateYear(year); list.add(loss); } else { // 不设置不计算 return; } }); // 先处理按公式 calcByFormula(year, month, reportForms, list); // 处理小计 calcByLitterCount(year, month, reportForms, list, hospId); // 处理医院其他收支 List otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId); if (!otherPaymentsDatas.isEmpty()) { otherPaymentsDatas.forEach(ele -> { HospProfitAndLoss loss = new HospProfitAndLoss(); loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum(ele.getId().intValue()) .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId); // if (ele.getPaymentsType() == 2) { // loss.setAmount(BigDecimal.ZERO.subtract(ele.getTotalAmount())); // } list.add(loss); }); } this.saveBatch(list); } // 计算公式中钱 private BigDecimal calcAmount(List list, String calcFormula) { String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ","); ArrayList numList = CollUtil.newArrayList(replace.split(",")); // 得到数字 Map numMap = new ConcurrentHashMap<>(); for (int j = 0; j < numList.size(); j++) { numMap.put(j, numList.get(j)); } List expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0) .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList()); // 得到预算表达式 Map expressionMap = new ConcurrentHashMap<>(); for (int k = 0; k < expressions.size(); k++) { expressionMap.put(k, expressions.get(k)); } Set numSet = numMap.keySet(); List nums = new ArrayList<>(numSet); Map map = new HashMap<>(); for (int l = 0; l < nums.size(); l++) { for (HospProfitAndLoss z : list) { if (z.getReportNum().equals(nums.get(l))) { map.put(l, z.getAmount()); } } } Set integers = map.keySet(); List mapList = new ArrayList<>(integers); AtomicReference total = new AtomicReference<>(); total.set(BigDecimal.ZERO); for (int x = 0; x < mapList.size(); x++) { BigDecimal bigDecimal = map.get(x); if (expressionMap.get(x).equals("+")) { total.set(total.get().add(bigDecimal)); } else { total.set(total.get().subtract(bigDecimal)); } } return total.get(); } /** * 按责任中心计算 * * @param hospId 医院id * @param reportForm 报表 * @param incomes 归集收入数据 * @param allocationQueries 分摊成本数据 */ private HospProfitAndLoss calcByResponsibility(Long hospId, ReportForm reportForm, List incomes, List allocationQueries) { List responsibilities = reportRelationService.getAccountRelation(reportForm.getId(), hospId); if (responsibilities.isEmpty()) { return null; } List accountCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList()); AtomicReference calcTotal = new AtomicReference<>(); calcTotal.set(BigDecimal.ZERO); accountCodes.forEach(i -> { BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); calcTotal.set(calcTotal.get().add(costAmount)); }); HospProfitAndLoss loss = new HospProfitAndLoss(); return loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum()) .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId); } /** * 按计算公式计算 */ private void calcByFormula(Integer year, Integer month, List reportForms, List list) { List calcFormulas = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.CALC_FORMULA.getType()).collect(Collectors.toList()); calcFormulas.forEach(i -> { String calcFormula = i.getCalcFormula(); // TODO: 2021/8/27 校验公式合法性 if (StrUtil.isBlank(calcFormula)) { throw new CostException("reportForm名称为" + i.getReportName() + "计算公式不正确"); } BigDecimal bigDecimal = calcAmount(list, calcFormula); HospProfitAndLoss loss = new HospProfitAndLoss(); loss.setDateYear(year).setDateMonth(month).setReportNum(i.getNum()).setReportName(i.getReportName()) .setAmount(bigDecimal).setCreateTime(System.currentTimeMillis()).setHospId(i.getHospId()); list.add(loss); }); } /** * 按小计计算 */ private void calcByLitterCount(Integer year, Integer month, List reportForms, List list, Long hospId) { List litterCounts = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.LITTER_COUNT.getType()).collect(Collectors.toList()); litterCounts.forEach(reportForm -> { Long parentId = reportForm.getParentId(); List reportFormByParents = reportFormService.getByParentId(hospId, parentId); List reportNums = new ArrayList<>(); reportFormByParents.forEach(form -> { // 去掉自己 if (form.getId().equals(reportForm.getId())) { return; } reportNums.add(form.getNum()); }); AtomicReference total = new AtomicReference<>(); total.set(BigDecimal.ZERO); reportNums.forEach(num -> { list.forEach(item -> { if (num.equals(item.getReportNum())) { total.set(total.get().add(item.getAmount())); } }); }); HospProfitAndLoss loss = new HospProfitAndLoss(); loss.setDateYear(year).setDateMonth(month).setReportNum(reportForm.getNum()) .setReportName(reportForm.getReportName()).setAmount(total.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId); list.add(loss); }); } /** * 按分摊层级计算 * * @param hospId 医院id * @param reportForm 报表 * @param year * @param month */ private HospProfitAndLoss calcByShareLevel(Long hospId, ReportForm reportForm, int year, int month) { List shareLevels = reportRelationService.getAccountRelation(reportForm.getId(), hospId); List shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).collect(Collectors.toList()); if (CollUtil.isEmpty(shareLevelId)) { return null; } List costShareLevels = shareLevelService.listByIds(shareLevelId); if (costShareLevels.isEmpty()) { throw new CostException("医院分摊层级设置错误," + reportForm.getReportName()); } List levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList()); List allocations = allocationService.getByDate(year, month, hospId, levelSorts); if (allocations.isEmpty()) { throw new CostException("医院未分摊本月数据"); } BigDecimal reduce = allocations.stream().map(Allocation::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); HospProfitAndLoss loss = new HospProfitAndLoss(); return loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum()) .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId); } /** * 计算按会计科目下的科目名称 * * @param hospId 医院id * @param reportForm 报表 * @param incomes 归集收入数据 * @param allocationQueries 分摊成本数据 */ private HospProfitAndLoss calcByAccount(Long hospId, ReportForm reportForm, List incomes, List allocationQueries) { List accountRelations = reportRelationService.getAccountRelation(reportForm.getId(), hospId); if (accountRelations.isEmpty()) { return null; } List accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList()); AtomicReference calcTotal = new AtomicReference<>(); calcTotal.set(BigDecimal.ZERO); accountCodes.forEach(i -> { BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal total = incomeAmount.add(costAmount); calcTotal.set(calcTotal.get().add(total)); }); HospProfitAndLoss loss = new HospProfitAndLoss(); return loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum()) .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId); } /** * 全院损益列表 * * @param current 当前页 * @param pageSize 每页展示数据大小 * @param date 日期 * @param hospId 医院id * @return PageUtils */ @Override public PageUtils getHospProfits(Integer current, Integer pageSize, String date, Long hospId) { DateTime parse = DateUtil.parse(date); int year = DateUtil.year(parse); int month = DateUtil.month(parse) + 1; Page page = new Page<>(current, pageSize); Page pages = this.page( page, new LambdaQueryWrapper() .eq(HospProfitAndLoss::getHospId, hospId) .eq(HospProfitAndLoss::getDateMonth, month) .eq(HospProfitAndLoss::getDateYear, year) ); List records = pages.getRecords(); List hospProfitVOS = BeanUtil.convertList(records, HospProfitVO.class); PageUtils pageUtils = new PageUtils(pages); pageUtils.setList(hospProfitVOS); return pageUtils; } }