IncomeCollectionServiceImpl.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. package com.imed.costaccount.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.date.DateUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  6. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7. import com.imed.costaccount.common.exception.CostException;
  8. import com.imed.costaccount.common.util.BeanUtil;
  9. import com.imed.costaccount.common.util.JacksonUtil;
  10. import com.imed.costaccount.common.util.PageUtils;
  11. import com.imed.costaccount.common.util.RedisLock;
  12. import com.imed.costaccount.mapper.CostIncomeGroupMapper;
  13. import com.imed.costaccount.mapper.IncomeCollectionMapper;
  14. import com.imed.costaccount.model.*;
  15. import com.imed.costaccount.model.dto.CollectDTO;
  16. import com.imed.costaccount.model.vo.*;
  17. import com.imed.costaccount.service.CostIncomeGroupSetService;
  18. import com.imed.costaccount.service.IncomeCollectionService;
  19. import lombok.extern.slf4j.Slf4j;
  20. import org.springframework.stereotype.Service;
  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.stream.Collectors;
  26. import static com.imed.costaccount.common.constants.Constant.LIMIT;
  27. @Slf4j
  28. @Service("incomeCollectionService")
  29. public class IncomeCollectionServiceImpl
  30. extends ServiceImpl<IncomeCollectionMapper, IncomeCollection>
  31. implements IncomeCollectionService {
  32. private final RedisLock redisLock;
  33. private final CostIncomeGroupMapper incomeGroupMapper;
  34. private final CostIncomeGroupSetService incomeGroupSetService;
  35. public IncomeCollectionServiceImpl(RedisLock redisLock, CostIncomeGroupMapper incomeGroupMapper, CostIncomeGroupSetService incomeGroupSetService) {
  36. this.redisLock = redisLock;
  37. this.incomeGroupMapper = incomeGroupMapper;
  38. this.incomeGroupSetService = incomeGroupSetService;
  39. }
  40. /**
  41. * 获取收入归集分页列表
  42. *
  43. * @param current 当前页
  44. * @param pageSize 页码数据大小
  45. * @param date 日期 yyyyMM
  46. * @param hospId 医院id
  47. * @return {@link PageUtils} 分页对象
  48. */
  49. @Override
  50. public PageUtils getCollections(Integer current, Integer pageSize, String date, Long hospId) {
  51. current = current - 1;
  52. Integer startIndex = current * pageSize;
  53. List<CollectionVO> list = incomeGroupMapper.getCollections(startIndex, pageSize, date, hospId);
  54. int count = incomeGroupMapper.getCollectionCount(date, hospId);
  55. // 设置是否归集字段
  56. list.forEach(i -> {
  57. i.setIsCollection(true);
  58. IncomeCollection one = this.getOne(
  59. new LambdaQueryWrapper<IncomeCollection>().select(IncomeCollection::getId)
  60. .eq(IncomeCollection::getYear, i.getYear())
  61. .eq(IncomeCollection::getMonth, i.getMonth())
  62. .last(LIMIT)
  63. );
  64. if (Objects.isNull(one)) {
  65. i.setIsCollection(false);
  66. }
  67. });
  68. return new PageUtils(list, count, pageSize, current + 1);
  69. }
  70. /**
  71. * 按年月归集数据
  72. *
  73. * @param year 年 数字类型
  74. * @param month 月 数字
  75. * @param hospId 医院id
  76. */
  77. @Override
  78. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  79. public void collect(Integer year, Integer month, Long hospId) {
  80. // todo 可能几十万次数据 需要分段异步多线程处理,
  81. // 1个小时
  82. String key = "collect" + hospId;
  83. String timestamp = DateUtil.offsetMillisecond(DateUtil.date(), 60 * 60).getTime() + "";
  84. boolean lock = redisLock.lock(key, timestamp);
  85. try {
  86. if (lock) {
  87. List<CostIncomeGroup> costIncomeGroups = incomeGroupMapper.selectList(
  88. new LambdaQueryWrapper<CostIncomeGroup>()
  89. .eq(CostIncomeGroup::getDateYear, year)
  90. .eq(CostIncomeGroup::getDateMonth, month)
  91. .eq(CostIncomeGroup::getHospId, hospId)
  92. );
  93. if (costIncomeGroups.isEmpty()) {
  94. String format = StrUtil.format("{}年{}月数据不存在,请先导入数据", year, month);
  95. throw new CostException(format);
  96. }
  97. // 删掉已归集的数据
  98. this.remove(
  99. new LambdaQueryWrapper<IncomeCollection>().eq(IncomeCollection::getYear, year).eq(IncomeCollection::getMonth, month).eq(IncomeCollection::getHospId, hospId)
  100. );
  101. // 根据会计科目和成本项目归纳所有数据
  102. Map<String, List<CostIncomeGroup>> collectMap = costIncomeGroups.stream()
  103. .collect(Collectors.groupingBy(i -> i.getOpenDepartmentCode() + "cost" + i.getStartDepartmentCode() + "cost" + i.getProductCode()));
  104. // 遍历map 组装成List保存
  105. List<IncomeCollection> list = new LinkedList<>();
  106. Set<Map.Entry<String, List<CostIncomeGroup>>> entries = collectMap.entrySet();
  107. entries.stream().map(Map.Entry::getValue).flatMap(Collection::stream).forEach(costIncomeGroup -> {
  108. //TODO 统计总金额
  109. IncomeCollection incomeCollection = new IncomeCollection();
  110. incomeCollection.setYear(year);
  111. incomeCollection.setMonth(month);
  112. incomeCollection.setAccountingCode(costIncomeGroup.getAccountCode());
  113. incomeCollection.setAccountingName(costIncomeGroup.getAccountName());
  114. incomeCollection.setProductName(costIncomeGroup.getProductName());
  115. incomeCollection.setProductCode(costIncomeGroup.getProductCode());
  116. incomeCollection.setHospId(hospId);
  117. // todo 来源id暂时不确定
  118. incomeCollection.setFileId(costIncomeGroup.getFileId());
  119. incomeCollection.setCreateTime(System.currentTimeMillis());
  120. String afterIncomeGroupStr = costIncomeGroup.getAfterIncomeGroup();
  121. AfterIncomeGroup afterIncomegroup = JacksonUtil.str2Obj(afterIncomeGroupStr, AfterIncomeGroup.class);
  122. if (Objects.isNull(afterIncomegroup)) {
  123. throw new CostException("未能正确归集对应的....");
  124. }
  125. // 转换一下其他的
  126. if (afterIncomegroup.getOpenDepartmentStatus() == 1 && afterIncomegroup.getStartDepartmentStatus() == 1) {
  127. // 排除会计科目
  128. List<CostIncomeGroupSet> sets = incomeGroupSetService.list(
  129. new LambdaQueryWrapper<CostIncomeGroupSet>()
  130. .eq(CostIncomeGroupSet::getOpenDepartmentStatus, 1).eq(CostIncomeGroupSet::getStartDepartmentStatus, 1).eq(CostIncomeGroupSet::getHospId, hospId)
  131. );
  132. if (!sets.isEmpty()) {
  133. for (CostIncomeGroupSet set : sets) {
  134. String accountCode = set.getAccountCode();
  135. if (StrUtil.isBlank(accountCode)) {
  136. continue;
  137. }
  138. String[] split = accountCode.split(StrUtil.COMMA);
  139. boolean contains = CollUtil.newArrayList(split).contains(costIncomeGroup.getAccountCode());
  140. if (contains) {
  141. // 钱全给执行科室
  142. incomeCollection.setAmount(new BigDecimal("0.0000"));
  143. incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
  144. incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
  145. incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode());
  146. incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName());
  147. list.add(incomeCollection);
  148. // 执行科室数据
  149. IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class);
  150. startCollect.setAmount(costIncomeGroup.getAmount());
  151. startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
  152. startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName());
  153. startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode());
  154. startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName());
  155. log.info("start:{}", startCollect);
  156. log.info("open:{}", incomeCollection);
  157. list.add(startCollect);
  158. } else {
  159. if (afterIncomegroup.getDirectStatus() == 2) {
  160. afterIncomegroup.setDirectStatus(0);
  161. }
  162. incomeCollection.setIsDirectIncome(afterIncomegroup.getDirectStatus());
  163. String responsibilityCode = afterIncomegroup.getResponsibilityCode();
  164. if (StrUtil.isNotBlank(responsibilityCode)) {
  165. incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName() + "|" + afterIncomegroup.getStartDepartmentName());
  166. incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode() + "|" + afterIncomegroup.getStartDepartmentCode());
  167. incomeCollection.setResponsibilityCode(responsibilityCode);
  168. incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
  169. incomeCollection.setAmount(afterIncomegroup.getOtherResponsibilityDecimal());
  170. list.add(incomeCollection);
  171. } else {
  172. // 开单中心的数据
  173. incomeCollection.setAmount(afterIncomegroup.getOpenDepartmentDecimal());
  174. incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
  175. incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
  176. incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode());
  177. incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName());
  178. list.add(incomeCollection);
  179. // 执行科室数据
  180. IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class);
  181. startCollect.setAmount(afterIncomegroup.getStartDepartmentDecimal());
  182. startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
  183. startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName());
  184. startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode());
  185. startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName());
  186. log.info("start:{}", startCollect);
  187. log.info("open:{}", incomeCollection);
  188. list.add(startCollect);
  189. }
  190. }
  191. }
  192. }
  193. return;
  194. }
  195. if (afterIncomegroup.getDirectStatus() == 2) {
  196. afterIncomegroup.setDirectStatus(0);
  197. }
  198. incomeCollection.setIsDirectIncome(afterIncomegroup.getDirectStatus());
  199. String responsibilityCode = afterIncomegroup.getResponsibilityCode();
  200. if (StrUtil.isNotBlank(responsibilityCode)) {
  201. incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName() + "|" + afterIncomegroup.getStartDepartmentName());
  202. incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode() + "|" + afterIncomegroup.getStartDepartmentCode());
  203. incomeCollection.setResponsibilityCode(responsibilityCode);
  204. incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
  205. incomeCollection.setAmount(afterIncomegroup.getOtherResponsibilityDecimal());
  206. list.add(incomeCollection);
  207. } else {
  208. // 开单中心的数据
  209. incomeCollection.setAmount(afterIncomegroup.getOpenDepartmentDecimal());
  210. incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
  211. incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
  212. incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode());
  213. incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName());
  214. list.add(incomeCollection);
  215. // 执行科室数据
  216. IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class);
  217. startCollect.setAmount(afterIncomegroup.getStartDepartmentDecimal());
  218. startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
  219. startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName());
  220. startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode());
  221. startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName());
  222. log.info("start:{}", startCollect);
  223. log.info("open:{}", incomeCollection);
  224. list.add(startCollect);
  225. }
  226. });
  227. // TODO: 2021/8/10 几十万条数据如何处理 待测试处理
  228. Map<String, List<IncomeCollection>> collect = list.stream().collect(Collectors.groupingBy(i -> i.getDepartmentCode() + "cost" + i.getResponsibilityCode() + "cost" + i.getAccountingCode() + "cost" + i.getProductCode()));
  229. List<IncomeCollection> realList = new LinkedList<>();
  230. Set<String> strings = collect.keySet();
  231. for (String str : strings) {
  232. List<IncomeCollection> incomeCollections = collect.get(str);
  233. if (CollUtil.isNotEmpty(incomeCollections)) {
  234. BigDecimal reduce = incomeCollections.stream().map(IncomeCollection::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  235. IncomeCollection incomeCollection = incomeCollections.get(0);
  236. incomeCollection.setAmount(reduce);
  237. realList.add(incomeCollection);
  238. }
  239. }
  240. this.saveBatch(realList);
  241. } else {
  242. throw new CostException("已有人在操作,请稍后重试");
  243. }
  244. } finally {
  245. redisLock.release(key, timestamp);
  246. }
  247. }
  248. /**
  249. * 按年月撤销归集
  250. *
  251. * @param year 年 数字类型
  252. * @param month 月 数字
  253. * @param hospId 医院id
  254. */
  255. @Override
  256. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  257. public void cancelCollect(Integer year, Integer month, Long hospId) {
  258. LambdaQueryWrapper<IncomeCollection> wrapper = new LambdaQueryWrapper<IncomeCollection>()
  259. .eq(IncomeCollection::getYear, year)
  260. .eq(IncomeCollection::getMonth, month)
  261. .eq(IncomeCollection::getHospId, hospId);
  262. this.remove(wrapper);
  263. }
  264. /**
  265. * 归集后数据分页列表
  266. *
  267. * @param collectDTO {@link CollectDTO} 查询相关参数
  268. * @return {@link PageUtils}
  269. */
  270. @Override
  271. public PageUtils collectList(CollectDTO collectDTO) {
  272. // 分页数据初始化
  273. Integer current = collectDTO.getCurrent();
  274. current = (Objects.isNull(current) || current == 0) ? 0 : current - 1;
  275. Integer pageSize = collectDTO.getPageSize();
  276. pageSize = Objects.isNull(pageSize) ? 10 : pageSize;
  277. collectDTO.setCurrent(current * pageSize);
  278. collectDTO.setPageSize(pageSize);
  279. // 查询需要的数据 DB
  280. List<CollectedVO> list = baseMapper.getCollectList(collectDTO);
  281. int count = baseMapper.getCollectListCount(collectDTO);
  282. BigDecimal totalAmount = baseMapper.getTotalAmount(collectDTO);
  283. return new PageUtils(list, count, pageSize, current + 1, totalAmount);
  284. }
  285. /**
  286. * 数据报表
  287. *
  288. * @param collectDTO
  289. * @return
  290. */
  291. @Override
  292. public CollectDataFormVO collectDataForm(CollectDTO collectDTO) {
  293. Long hospId = collectDTO.getHospId();
  294. String date = collectDTO.getDate();
  295. // 1. 得到所有的标题栏 责任中心代码
  296. List<CodeAndNameVO> responsibilities = this.baseMapper.getResponsibility(hospId, collectDTO.getResponsibilityCode(), date).stream().distinct().collect(Collectors.toList());
  297. if (responsibilities.isEmpty()) {
  298. return new CollectDataFormVO();
  299. }
  300. List<String> responsibilityCodes = responsibilities.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
  301. responsibilities.add(0, new CodeAndNameVO("#", "#"));
  302. responsibilities.add(responsibilities.size(), new CodeAndNameVO("合计", "合计"));
  303. List<String> titleData = responsibilities.stream().map(CodeAndNameVO::getName).collect(Collectors.toList());
  304. Map<Integer, String> titleMap = new HashMap<>();
  305. for (int i = 0; i < titleData.size(); i++) {
  306. titleMap.put(i + 1, titleData.get(i));
  307. }
  308. // 所有的数据格式
  309. List<CodeAndNameVO> realData = this.baseMapper.getAccount(hospId, date);
  310. if (realData.isEmpty()) {
  311. return new CollectDataFormVO();
  312. }
  313. // realData [#,123,123,"合计"]
  314. List<Map<Integer, Object>> realDatas = new ArrayList<>();
  315. List<CodeAndNameVO> accounts = realData.stream().distinct().collect(Collectors.toList());
  316. for (CodeAndNameVO account : accounts) {
  317. Map<Integer, Object> map = new HashMap<>();
  318. for (int i = 0; i < responsibilities.size(); i++) {
  319. if (i == 0) {
  320. map.put(i + 1, account.getName());
  321. continue;
  322. } else if (i == responsibilities.size() - 1) {
  323. // TODO: 2021/8/12 合计
  324. BigDecimal amount = this.baseMapper.getCountAccountAndResponsibilities(hospId, date, account.getCode(), responsibilityCodes);
  325. map.put(i + 1, amount);
  326. continue;
  327. }
  328. List<IncomeCollection> list = this.list(
  329. new LambdaQueryWrapper<IncomeCollection>().select(IncomeCollection::getAmount)
  330. .eq(IncomeCollection::getHospId, hospId)
  331. .eq(IncomeCollection::getYear, date.substring(0, 4))
  332. .eq(IncomeCollection::getMonth, date.substring(4))
  333. .eq(IncomeCollection::getAccountingCode, account.getCode())
  334. .eq(IncomeCollection::getResponsibilityCode, responsibilities.get(i).getCode())
  335. );
  336. if (list.isEmpty()) {
  337. map.put(i + 1, new BigDecimal("0.0000"));
  338. } else {
  339. BigDecimal reduce = list.stream().map(IncomeCollection::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  340. map.put(i + 1, reduce);
  341. }
  342. }
  343. realDatas.add(map);
  344. }
  345. //
  346. // 合计栏计算
  347. // 长度一致的
  348. List<String> accountingCodes = realData.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
  349. Map<Integer, Object> map = new HashMap<>();
  350. for (int i = 0; i < responsibilities.size(); i++) {
  351. if (i == 0) {
  352. map.put(i + 1, "合计");
  353. continue;
  354. } else if (i == responsibilities.size() - 1) {
  355. // 统计问题
  356. BigDecimal bigDecimal = this.baseMapper.getCountByResponsibilitiesAndAccounts(responsibilityCodes, accountingCodes, hospId, date);
  357. map.put(i + 1, bigDecimal);
  358. continue;
  359. }
  360. BigDecimal bigDecimal = this.baseMapper.getCountByResponseAndAccounts(responsibilities.get(i).getCode(), accountingCodes, hospId, date);
  361. map.put(i + 1, bigDecimal);
  362. }
  363. return new CollectDataFormVO(titleMap, realDatas, map);
  364. }
  365. }