HospProfitAndLossServiceImpl.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  8. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  9. import com.imed.costaccount.common.enums.CalcTypeEnum;
  10. import com.imed.costaccount.common.enums.ReportTypeEnum;
  11. import com.imed.costaccount.common.exception.CostException;
  12. import com.imed.costaccount.common.util.BeanUtil;
  13. import com.imed.costaccount.common.util.PageUtils;
  14. import com.imed.costaccount.model.*;
  15. import com.imed.costaccount.model.vo.HospProfitVO;
  16. import com.imed.costaccount.model.vo.RelationVO;
  17. import com.imed.costaccount.service.*;
  18. import org.springframework.stereotype.Service;
  19. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  20. import com.imed.costaccount.mapper.HospProfitAndLossMapper;
  21. import org.springframework.transaction.annotation.Propagation;
  22. import org.springframework.transaction.annotation.Transactional;
  23. import java.math.BigDecimal;
  24. import java.util.*;
  25. import java.util.concurrent.ConcurrentHashMap;
  26. import java.util.concurrent.atomic.AtomicReference;
  27. import java.util.stream.Collectors;
  28. @Service("hospProfitAndLossService")
  29. public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossMapper, HospProfitAndLoss> implements HospProfitAndLossService {
  30. private final ReportFormService reportFormService;
  31. private final IncomeCollectionService collectionService;
  32. private final AllocationQueryService allocationQueryService;
  33. private final AllocationService allocationService;
  34. private final ReportRelationService reportRelationService;
  35. private final CostShareLevelService shareLevelService;
  36. private final CostOtherPaymentsDataService otherPaymentsDataService;
  37. public HospProfitAndLossServiceImpl(ReportFormService reportFormService,
  38. IncomeCollectionService collectionService,
  39. AllocationQueryService allocationQueryService,
  40. AllocationService allocationService,
  41. ReportRelationService reportRelationService,
  42. CostShareLevelService shareLevelService,
  43. CostOtherPaymentsDataService otherPaymentsDataService) {
  44. this.reportFormService = reportFormService;
  45. this.collectionService = collectionService;
  46. this.allocationQueryService = allocationQueryService;
  47. this.allocationService = allocationService;
  48. this.reportRelationService = reportRelationService;
  49. this.shareLevelService = shareLevelService;
  50. this.otherPaymentsDataService = otherPaymentsDataService;
  51. }
  52. /**
  53. * 计算全院损益
  54. *
  55. * @param date yyyy-MM-dd 时间
  56. * @param hospId 医院id
  57. */
  58. @Override
  59. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
  60. public void calc(String date, Long hospId) {
  61. DateTime parse = DateUtil.parse(date);
  62. int year = DateUtil.year(parse);
  63. int month = DateUtil.month(parse) + 1;
  64. this.remove(new LambdaQueryWrapper<HospProfitAndLoss>().eq(HospProfitAndLoss::getDateYear, year).eq(HospProfitAndLoss::getDateMonth, month).eq(HospProfitAndLoss::getHospId, hospId));
  65. // 得到全院损益计算报表
  66. List<ReportForm> reportForms = reportFormService.getListByReportType(hospId, ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
  67. if (CollUtil.isEmpty(reportForms)) {
  68. throw new CostException("医院未设置全院损益计算报表");
  69. }
  70. // 得到这个月所有收入数据
  71. List<IncomeCollection> incomes = collectionService.getCollectionsByDate(year, month, hospId);
  72. if (incomes.isEmpty()) {
  73. throw new CostException("医院未归集本月收入数据");
  74. }
  75. // 得到这个月的所有成本数据
  76. List<AllocationQuery> allocationQueries = allocationQueryService.getAllByDate(hospId, year, month);
  77. if (allocationQueries.isEmpty()) {
  78. throw new CostException("医院未分摊本月数据");
  79. }
  80. List<HospProfitAndLoss> list = new ArrayList<>();
  81. reportForms.forEach(i -> {
  82. Integer calcType = i.getCalcType();
  83. if (calcType == CalcTypeEnum.BY_ACCOUNT.getType()) {
  84. // 按会计科目计算
  85. HospProfitAndLoss loss = calcByAccount(hospId, i, incomes, allocationQueries);
  86. if (Objects.isNull(loss)) {
  87. return;
  88. }
  89. loss.setDateMonth(month);
  90. loss.setDateYear(year);
  91. list.add(loss);
  92. return;
  93. } else if (calcType == CalcTypeEnum.BY_SHARE_LEVEL.getType()) {
  94. // 分摊层级计算
  95. HospProfitAndLoss loss = calcByShareLevel(hospId, i, year, month);
  96. if (Objects.isNull(loss)) {
  97. return;
  98. }
  99. loss.setDateMonth(month);
  100. loss.setDateYear(year);
  101. list.add(loss);
  102. return;
  103. } else if (calcType == CalcTypeEnum.LITTER_COUNT.getType()) {
  104. return;
  105. } else if (calcType == CalcTypeEnum.CALC_FORMULA.getType()) {
  106. // todo 留在最后加减
  107. return;
  108. } else if (calcType == CalcTypeEnum.BY_RESPONSIBILITY.getType()) {
  109. // 责任中心
  110. HospProfitAndLoss loss = calcByResponsibility(hospId, i, incomes, allocationQueries);
  111. if (loss == null) {
  112. return;
  113. }
  114. loss.setDateMonth(month);
  115. loss.setDateYear(year);
  116. list.add(loss);
  117. } else {
  118. // 不设置不计算
  119. return;
  120. }
  121. });
  122. // 先处理按公式
  123. calcByFormula(year, month, reportForms, list);
  124. // 处理小计
  125. calcByLitterCount(year, month, reportForms, list, hospId);
  126. // 处理医院其他收支
  127. List<CostOtherPaymentsData> otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId);
  128. if (!otherPaymentsDatas.isEmpty()) {
  129. otherPaymentsDatas.forEach(ele -> {
  130. HospProfitAndLoss loss = new HospProfitAndLoss();
  131. loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum(ele.getId().intValue())
  132. .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId);
  133. // if (ele.getPaymentsType() == 2) {
  134. // loss.setAmount(BigDecimal.ZERO.subtract(ele.getTotalAmount()));
  135. // }
  136. list.add(loss);
  137. });
  138. }
  139. this.saveBatch(list);
  140. }
  141. // 计算公式中钱
  142. private BigDecimal calcAmount(List<HospProfitAndLoss> list, String calcFormula) {
  143. String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
  144. ArrayList<String> numList = CollUtil.newArrayList(replace.split(","));
  145. // 得到数字
  146. Map<Integer, Object> numMap = new ConcurrentHashMap<>();
  147. for (int j = 0; j < numList.size(); j++) {
  148. numMap.put(j, numList.get(j));
  149. }
  150. List<String> expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0)
  151. .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
  152. // 得到预算表达式
  153. Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
  154. for (int k = 0; k < expressions.size(); k++) {
  155. expressionMap.put(k, expressions.get(k));
  156. }
  157. Set<Integer> numSet = numMap.keySet();
  158. List<Integer> nums = new ArrayList<>(numSet);
  159. Map<Integer, BigDecimal> map = new HashMap<>();
  160. for (int l = 0; l < nums.size(); l++) {
  161. for (HospProfitAndLoss z : list) {
  162. if (z.getReportNum().equals(nums.get(l))) {
  163. map.put(l, z.getAmount());
  164. }
  165. }
  166. }
  167. Set<Integer> integers = map.keySet();
  168. List<Integer> mapList = new ArrayList<>(integers);
  169. AtomicReference<BigDecimal> total = new AtomicReference<>();
  170. total.set(BigDecimal.ZERO);
  171. for (int x = 0; x < mapList.size(); x++) {
  172. BigDecimal bigDecimal = map.get(x);
  173. if (expressionMap.get(x).equals("+")) {
  174. total.set(total.get().add(bigDecimal));
  175. } else {
  176. total.set(total.get().subtract(bigDecimal));
  177. }
  178. }
  179. return total.get();
  180. }
  181. /**
  182. * 按责任中心计算
  183. *
  184. * @param hospId 医院id
  185. * @param reportForm 报表
  186. * @param incomes 归集收入数据
  187. * @param allocationQueries 分摊成本数据
  188. */
  189. private HospProfitAndLoss calcByResponsibility(Long hospId, ReportForm reportForm, List<IncomeCollection> incomes, List<AllocationQuery> allocationQueries) {
  190. List<RelationVO> responsibilities = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  191. if (responsibilities.isEmpty()) {
  192. return null;
  193. }
  194. List<String> accountCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
  195. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  196. calcTotal.set(BigDecimal.ZERO);
  197. accountCodes.forEach(i -> {
  198. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
  199. .reduce(BigDecimal.ZERO, BigDecimal::add);
  200. calcTotal.set(calcTotal.get().add(costAmount));
  201. });
  202. HospProfitAndLoss loss = new HospProfitAndLoss();
  203. return loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  204. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
  205. }
  206. /**
  207. * 按计算公式计算
  208. */
  209. private void calcByFormula(Integer year, Integer month, List<ReportForm> reportForms, List<HospProfitAndLoss> list) {
  210. List<ReportForm> calcFormulas = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.CALC_FORMULA.getType()).collect(Collectors.toList());
  211. calcFormulas.forEach(i -> {
  212. String calcFormula = i.getCalcFormula();
  213. // TODO: 2021/8/27 校验公式合法性
  214. if (StrUtil.isBlank(calcFormula)) {
  215. throw new CostException("reportForm名称为" + i.getReportName() + "计算公式不正确");
  216. }
  217. BigDecimal bigDecimal = calcAmount(list, calcFormula);
  218. HospProfitAndLoss loss = new HospProfitAndLoss();
  219. loss.setDateYear(year).setDateMonth(month).setReportNum(i.getNum()).setReportName(i.getReportName())
  220. .setAmount(bigDecimal).setCreateTime(System.currentTimeMillis()).setHospId(i.getHospId());
  221. list.add(loss);
  222. });
  223. }
  224. /**
  225. * 按小计计算
  226. */
  227. private void calcByLitterCount(Integer year, Integer month, List<ReportForm> reportForms, List<HospProfitAndLoss> list, Long hospId) {
  228. List<ReportForm> litterCounts = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.LITTER_COUNT.getType()).collect(Collectors.toList());
  229. litterCounts.forEach(reportForm -> {
  230. Long parentId = reportForm.getParentId();
  231. List<ReportForm> reportFormByParents = reportFormService.getByParentId(hospId, parentId);
  232. List<Integer> reportNums = new ArrayList<>();
  233. reportFormByParents.forEach(form -> {
  234. // 去掉自己
  235. if (form.getId().equals(reportForm.getId())) {
  236. return;
  237. }
  238. reportNums.add(form.getNum());
  239. });
  240. AtomicReference<BigDecimal> total = new AtomicReference<>();
  241. total.set(BigDecimal.ZERO);
  242. reportNums.forEach(num -> {
  243. list.forEach(item -> {
  244. if (num.equals(item.getReportNum())) {
  245. total.set(total.get().add(item.getAmount()));
  246. }
  247. });
  248. });
  249. HospProfitAndLoss loss = new HospProfitAndLoss();
  250. loss.setDateYear(year).setDateMonth(month).setReportNum(reportForm.getNum())
  251. .setReportName(reportForm.getReportName()).setAmount(total.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
  252. list.add(loss);
  253. });
  254. }
  255. /**
  256. * 按分摊层级计算
  257. *
  258. * @param hospId 医院id
  259. * @param reportForm 报表
  260. * @param year
  261. * @param month
  262. */
  263. private HospProfitAndLoss calcByShareLevel(Long hospId, ReportForm reportForm, int year, int month) {
  264. List<RelationVO> shareLevels = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  265. List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).collect(Collectors.toList());
  266. if (CollUtil.isEmpty(shareLevelId)) {
  267. return null;
  268. }
  269. List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
  270. if (costShareLevels.isEmpty()) {
  271. throw new CostException("医院分摊层级设置错误," + reportForm.getReportName());
  272. }
  273. List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
  274. List<Allocation> allocations = allocationService.getByDate(year, month, hospId, levelSorts);
  275. if (allocations.isEmpty()) {
  276. throw new CostException("医院未分摊本月数据");
  277. }
  278. BigDecimal reduce = allocations.stream().map(Allocation::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  279. HospProfitAndLoss loss = new HospProfitAndLoss();
  280. return loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  281. .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
  282. }
  283. /**
  284. * 计算按会计科目下的科目名称
  285. *
  286. * @param hospId 医院id
  287. * @param reportForm 报表
  288. * @param incomes 归集收入数据
  289. * @param allocationQueries 分摊成本数据
  290. */
  291. private HospProfitAndLoss calcByAccount(Long hospId, ReportForm reportForm, List<IncomeCollection> incomes, List<AllocationQuery> allocationQueries) {
  292. List<RelationVO> accountRelations = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  293. if (accountRelations.isEmpty()) {
  294. return null;
  295. }
  296. List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
  297. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  298. calcTotal.set(BigDecimal.ZERO);
  299. accountCodes.forEach(i -> {
  300. BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
  301. .reduce(BigDecimal.ZERO, BigDecimal::add);
  302. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
  303. .reduce(BigDecimal.ZERO, BigDecimal::add);
  304. BigDecimal total = incomeAmount.add(costAmount);
  305. calcTotal.set(calcTotal.get().add(total));
  306. });
  307. HospProfitAndLoss loss = new HospProfitAndLoss();
  308. return loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  309. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
  310. }
  311. /**
  312. * 全院损益列表
  313. *
  314. * @param current 当前页
  315. * @param pageSize 每页展示数据大小
  316. * @param date 日期
  317. * @param hospId 医院id
  318. * @return PageUtils
  319. */
  320. @Override
  321. public PageUtils getHospProfits(Integer current, Integer pageSize, String date, Long hospId) {
  322. DateTime parse = DateUtil.parse(date);
  323. int year = DateUtil.year(parse);
  324. int month = DateUtil.month(parse) + 1;
  325. Page<HospProfitAndLoss> page = new Page<>(current, pageSize);
  326. Page<HospProfitAndLoss> pages = this.page(
  327. page,
  328. new LambdaQueryWrapper<HospProfitAndLoss>()
  329. .eq(HospProfitAndLoss::getHospId, hospId)
  330. .eq(HospProfitAndLoss::getDateMonth, month)
  331. .eq(HospProfitAndLoss::getDateYear, year)
  332. );
  333. List<HospProfitAndLoss> records = pages.getRecords();
  334. List<HospProfitVO> hospProfitVOS = BeanUtil.convertList(records, HospProfitVO.class);
  335. PageUtils pageUtils = new PageUtils(pages);
  336. pageUtils.setList(hospProfitVOS);
  337. return pageUtils;
  338. }
  339. }