HospProfitAndLossServiceImpl.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package com.imed.costaccount.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.date.DateTime;
  4. import cn.hutool.core.date.DateUtil;
  5. import cn.hutool.core.util.ReUtil;
  6. import cn.hutool.core.util.StrUtil;
  7. import com.imed.costaccount.common.enums.CalcTypeEnum;
  8. import com.imed.costaccount.common.enums.ReportTypeEnum;
  9. import com.imed.costaccount.common.exception.CostException;
  10. import com.imed.costaccount.model.*;
  11. import com.imed.costaccount.model.vo.RelationVO;
  12. import com.imed.costaccount.service.*;
  13. import org.springframework.stereotype.Service;
  14. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  15. import com.imed.costaccount.mapper.HospProfitAndLossMapper;
  16. import org.springframework.transaction.annotation.Propagation;
  17. import org.springframework.transaction.annotation.Transactional;
  18. import java.math.BigDecimal;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.concurrent.ConcurrentHashMap;
  23. import java.util.concurrent.atomic.AtomicReference;
  24. import java.util.stream.Collectors;
  25. @Service("hospProfitAndLossService")
  26. public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossMapper, HospProfitAndLoss> implements HospProfitAndLossService {
  27. private final ReportFormService reportFormService;
  28. private final IncomeCollectionService collectionService;
  29. private final AllocationQueryService allocationQueryService;
  30. private final AllocationService allocationService;
  31. private final ReportRelationService reportRelationService;
  32. private final CostShareLevelService shareLevelService;
  33. public HospProfitAndLossServiceImpl(ReportFormService reportFormService,
  34. IncomeCollectionService collectionService,
  35. AllocationQueryService allocationQueryService,
  36. AllocationService allocationService, ReportRelationService reportRelationService, CostShareLevelService shareLevelService) {
  37. this.reportFormService = reportFormService;
  38. this.collectionService = collectionService;
  39. this.allocationQueryService = allocationQueryService;
  40. this.allocationService = allocationService;
  41. this.reportRelationService = reportRelationService;
  42. this.shareLevelService = shareLevelService;
  43. }
  44. /**
  45. * 计算全院损益
  46. *
  47. * @param date yyyy-MM-dd 时间
  48. * @param hospId 医院id
  49. */
  50. @Override
  51. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
  52. public void calc(String date, Long hospId) {
  53. // 得到全院损益计算报表
  54. List<ReportForm> reportForms = reportFormService.getListByReportType(hospId, ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
  55. if (CollUtil.isEmpty(reportForms)) {
  56. throw new CostException("医院未设置全院损益计算报表");
  57. }
  58. DateTime parse = DateUtil.parse(date);
  59. int year = DateUtil.year(parse);
  60. int month = DateUtil.month(parse) + 1;
  61. // 得到这个月所有收入数据
  62. List<IncomeCollection> incomes = collectionService.getCollectionsByDate(year, month, hospId);
  63. if (incomes.isEmpty()) {
  64. throw new CostException("医院未归集本月收入数据");
  65. }
  66. // 得到这个月的所有成本数据
  67. List<AllocationQuery> allocationQueries = allocationQueryService.getAllByDate(hospId, year, month);
  68. if (allocationQueries.isEmpty()) {
  69. throw new CostException("医院未分摊本月数据");
  70. }
  71. List<HospProfitAndLoss> list = new ArrayList<>();
  72. reportForms.forEach(i -> {
  73. Integer calcType = i.getCalcType();
  74. if (calcType == CalcTypeEnum.BY_ACCOUNT.getType()) {
  75. // 按会计科目计算
  76. HospProfitAndLoss loss = new HospProfitAndLoss();
  77. calcByAccount(hospId, i, loss, incomes, allocationQueries);
  78. loss.setDateMonth(month);
  79. loss.setDateYear(year);
  80. list.add(loss);
  81. return;
  82. } else if (calcType == CalcTypeEnum.BY_SHARE_LEVEL.getType()) {
  83. // 分摊层级计算
  84. HospProfitAndLoss loss = new HospProfitAndLoss();
  85. calcByShareLevel(hospId, i, loss, year, month);
  86. loss.setDateMonth(month);
  87. loss.setDateYear(year);
  88. list.add(loss);
  89. return;
  90. } else if (calcType == CalcTypeEnum.LITTER_COUNT.getType()) {
  91. // todo 留在最后直接加 减
  92. // HospProfitAndLoss loss = new HospProfitAndLoss();
  93. // loss.setDateYear(year).setDateMonth(month).setAmount(BigDecimal.ZERO)
  94. // .setReportName(i.getReportName()).setReportNum(i.getNum())
  95. // .setCreateTime(System.currentTimeMillis());
  96. // list.add(loss);
  97. return;
  98. } else if (calcType == CalcTypeEnum.CALC_FORMULA.getType()) {
  99. // todo 留在最后加减
  100. // HospProfitAndLoss loss = new HospProfitAndLoss();
  101. // loss.setDateYear(year).setDateMonth(month).setAmount(BigDecimal.ZERO)
  102. // .setReportName(i.getReportName()).setReportNum(i.getNum())
  103. // .setCreateTime(System.currentTimeMillis());
  104. // list.add(loss);
  105. return;
  106. } else if (calcType == CalcTypeEnum.BY_RESPONSIBILITY.getType()) {
  107. // 责任中心
  108. HospProfitAndLoss loss = new HospProfitAndLoss();
  109. calcByResponsibility(hospId, i, loss, incomes, allocationQueries);
  110. loss.setDateMonth(month);
  111. loss.setDateYear(year);
  112. list.add(loss);
  113. } else {
  114. // 不设置不计算
  115. return;
  116. }
  117. });
  118. // 先处理按公式
  119. List<ReportForm> calcFormulas = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.CALC_FORMULA.getType()).collect(Collectors.toList());
  120. calcFormulas.forEach(i -> {
  121. String calcFormula = i.getCalcFormula();
  122. // TODO: 2021/8/27 校验公式合法性
  123. if (StrUtil.isBlank(calcFormula)) {
  124. throw new CostException("reportForm名称为" + i.getReportName() + "计算公式不正确");
  125. }
  126. String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
  127. // ArrayList<String> strings = CollUtil.newArrayList(replace.split(StrUtil.COMMA));
  128. ArrayList<String> strings = CollUtil.newArrayList(replace.split(","));
  129. Map<Integer, Object> map = new ConcurrentHashMap<>();
  130. for (int j = 0; j < strings.size(); j++) {
  131. map.put(j, strings.get(j));
  132. }
  133. // ReUtil.contains(, "+" + calcFormula);
  134. });
  135. // 处理小计
  136. this.saveBatch(list);
  137. }
  138. /**
  139. * 按责任中心计算
  140. *
  141. * @param hospId 医院id
  142. * @param reportForm 报表
  143. * @param loss 创建的对象
  144. * @param incomes 归集收入数据
  145. * @param allocationQueries 分摊成本数据
  146. */
  147. private void calcByResponsibility(Long hospId, ReportForm reportForm, HospProfitAndLoss loss, List<IncomeCollection> incomes, List<AllocationQuery> allocationQueries) {
  148. List<RelationVO> responsibilities = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  149. if (responsibilities.isEmpty()) {
  150. return;
  151. }
  152. List<String> accountCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
  153. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  154. calcTotal.set(BigDecimal.ZERO);
  155. accountCodes.forEach(i -> {
  156. // BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(IncomeCollection::getAmount)
  157. // .reduce(BigDecimal.ZERO, BigDecimal::add);
  158. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
  159. .reduce(BigDecimal.ZERO, BigDecimal::add);
  160. // BigDecimal total = incomeAmount.subtract(costAmount);
  161. calcTotal.set(calcTotal.get().add(costAmount));
  162. });
  163. loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  164. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis());
  165. }
  166. /**
  167. * 按计算公式计算
  168. *
  169. * @param hospId 医院id
  170. * @param reportForm 报表
  171. * @param loss 创建的对象
  172. * @param incomes 归集收入数据
  173. * @param allocationQueries 分摊成本数据
  174. */
  175. private void calcByFormula(Long hospId, ReportForm reportForm, HospProfitAndLoss loss, List<IncomeCollection> incomes, List<AllocationQuery> allocationQueries) {
  176. String calcFormula = reportForm.getCalcFormula();
  177. // TODO: 2021/8/27 校验公式合法性
  178. if (StrUtil.isBlank(calcFormula)) {
  179. throw new CostException("reportForm名称为" + reportForm.getReportName() + "计算公式不正确");
  180. }
  181. // 得到11 + 12 - 1
  182. String sub = StrUtil.removeAny(calcFormula, "[", "]");
  183. // TODO: 2021/8/27
  184. }
  185. /**
  186. * 按小计计算
  187. *
  188. * @param hospId 医院id
  189. * @param reportForm 报表
  190. * @param loss 创建的对象
  191. * @param incomes 归集收入数据
  192. * @param allocationQueries 分摊成本数据
  193. */
  194. private void calcByLitterCount(Long hospId, ReportForm reportForm, HospProfitAndLoss loss, List<IncomeCollection> incomes, List<AllocationQuery> allocationQueries) {
  195. Long parentId = reportForm.getParentId();
  196. List<ReportForm> forms = reportFormService.getByParentId(hospId, parentId);
  197. forms.forEach(i -> {
  198. // 排除自己
  199. if (i.getId().equals(reportForm.getId())) {
  200. return;
  201. }
  202. // 计算其他几项之和
  203. // TODO: 2021/8/27
  204. });
  205. }
  206. /**
  207. * 按分摊层级计算
  208. *
  209. * @param hospId 医院id
  210. * @param reportForm 报表
  211. * @param loss 创建的对象
  212. * @param year
  213. * @param month
  214. */
  215. private void calcByShareLevel(Long hospId, ReportForm reportForm, HospProfitAndLoss loss, int year, int month) {
  216. List<RelationVO> shareLevels = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  217. List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).collect(Collectors.toList());
  218. List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
  219. if (costShareLevels.isEmpty()) {
  220. throw new CostException("医院分摊层级设置错误," + reportForm.getReportName());
  221. }
  222. List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
  223. List<Allocation> allocations = allocationService.getByDate(year, month, hospId, levelSorts);
  224. if (allocations.isEmpty()) {
  225. throw new CostException("医院未分摊本月数据");
  226. }
  227. BigDecimal reduce = allocations.stream().map(Allocation::getAmount).reduce(BigDecimal.ZERO, BigDecimal::subtract);
  228. loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  229. .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setDateYear(year).setDateMonth(month);
  230. }
  231. /**
  232. * 计算按会计科目下的科目名称
  233. *
  234. * @param hospId 医院id
  235. * @param reportForm 报表
  236. * @param loss 创建的对象
  237. * @param incomes 归集收入数据
  238. * @param allocationQueries 分摊成本数据
  239. */
  240. private void calcByAccount(Long hospId, ReportForm reportForm, HospProfitAndLoss loss, List<IncomeCollection> incomes, List<AllocationQuery> allocationQueries) {
  241. List<RelationVO> accountRelations = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  242. if (accountRelations.isEmpty()) {
  243. return;
  244. }
  245. List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
  246. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  247. calcTotal.set(BigDecimal.ZERO);
  248. accountCodes.forEach(i -> {
  249. BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
  250. .reduce(BigDecimal.ZERO, BigDecimal::add);
  251. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
  252. .reduce(BigDecimal.ZERO, BigDecimal::add);
  253. BigDecimal total = incomeAmount.subtract(costAmount);
  254. calcTotal.set(calcTotal.get().add(total));
  255. });
  256. loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  257. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis());
  258. }
  259. }