IncomeCollectionServiceImpl.java 22 KB

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