IncomeCollectionServiceImpl.java 23 KB

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