package com.imed.costaccount.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.imed.costaccount.common.exception.CostException; import com.imed.costaccount.common.util.BeanUtil; import com.imed.costaccount.common.util.JacksonUtil; import com.imed.costaccount.common.util.PageUtils; import com.imed.costaccount.common.util.RedisLock; import com.imed.costaccount.mapper.CostIncomeGroupMapper; import com.imed.costaccount.mapper.IncomeCollectionMapper; import com.imed.costaccount.model.*; import com.imed.costaccount.model.dto.CollectDTO; import com.imed.costaccount.model.vo.*; import com.imed.costaccount.service.CostIncomeGroupSetService; import com.imed.costaccount.service.IncomeCollectionService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; import static com.imed.costaccount.common.constants.Constant.LIMIT; @Slf4j @Service("incomeCollectionService") public class IncomeCollectionServiceImpl extends ServiceImpl implements IncomeCollectionService { private final RedisLock redisLock; private final CostIncomeGroupMapper incomeGroupMapper; private final CostIncomeGroupSetService incomeGroupSetService; public IncomeCollectionServiceImpl(RedisLock redisLock, CostIncomeGroupMapper incomeGroupMapper, CostIncomeGroupSetService incomeGroupSetService) { this.redisLock = redisLock; this.incomeGroupMapper = incomeGroupMapper; this.incomeGroupSetService = incomeGroupSetService; } /** * 获取收入归集分页列表 * * @param current 当前页 * @param pageSize 页码数据大小 * @param date 日期 yyyyMM * @param hospId 医院id * @return {@link PageUtils} 分页对象 */ @Override public PageUtils getCollections(Integer current, Integer pageSize, String date, Long hospId) { current = current - 1; Integer startIndex = current * pageSize; List list = incomeGroupMapper.getCollections(startIndex, pageSize, date, hospId); int count = incomeGroupMapper.getCollectionCount(date, hospId); // 设置是否归集字段 list.forEach(i -> { i.setIsCollection(true); IncomeCollection one = this.getOne( new LambdaQueryWrapper().select(IncomeCollection::getId) .eq(IncomeCollection::getYear, i.getYear()) .eq(IncomeCollection::getMonth, i.getMonth()) .last(LIMIT) ); if (Objects.isNull(one)) { i.setIsCollection(false); } }); return new PageUtils(list, count, pageSize, current + 1); } /** * 按年月归集数据 * * @param year 年 数字类型 * @param month 月 数字 * @param hospId 医院id */ @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void collect(Integer year, Integer month, Long hospId) { // todo 可能几十万次数据 需要分段异步多线程处理, // 1个小时 String key = "collect" + hospId; String timestamp = DateUtil.offsetMillisecond(DateUtil.date(), 60 * 60).getTime() + ""; boolean lock = redisLock.lock(key, timestamp); try { if (lock) { List costIncomeGroups = incomeGroupMapper.selectList( new LambdaQueryWrapper() .eq(CostIncomeGroup::getDateYear, year) .eq(CostIncomeGroup::getDateMonth, month) .eq(CostIncomeGroup::getHospId, hospId) ); if (costIncomeGroups.isEmpty()) { String format = StrUtil.format("{}年{}月数据不存在,请先导入数据", year, month); throw new CostException(format); } // 删掉已归集的数据 this.remove( new LambdaQueryWrapper().eq(IncomeCollection::getYear, year).eq(IncomeCollection::getMonth, month).eq(IncomeCollection::getHospId, hospId) ); // 根据会计科目和成本项目归纳所有数据 Map> collectMap = costIncomeGroups.stream() .collect(Collectors.groupingBy(i -> i.getOpenDepartmentCode() + "cost" + i.getStartDepartmentCode() + "cost" + i.getProductCode())); // 遍历map 组装成List保存 List list = new LinkedList<>(); Set>> entries = collectMap.entrySet(); entries.stream().map(Map.Entry::getValue).flatMap(Collection::stream).forEach(costIncomeGroup -> { //TODO 统计总金额 IncomeCollection incomeCollection = new IncomeCollection(); incomeCollection.setYear(year); incomeCollection.setMonth(month); incomeCollection.setAccountingCode(costIncomeGroup.getAccountCode()); incomeCollection.setAccountingName(costIncomeGroup.getAccountName()); incomeCollection.setProductName(costIncomeGroup.getProductName()); incomeCollection.setProductCode(costIncomeGroup.getProductCode()); incomeCollection.setHospId(hospId); // todo 来源id暂时不确定 incomeCollection.setFileId(costIncomeGroup.getFileId()); incomeCollection.setCreateTime(System.currentTimeMillis()); String afterIncomeGroupStr = costIncomeGroup.getAfterIncomeGroup(); AfterIncomeGroup afterIncomegroup = JacksonUtil.str2Obj(afterIncomeGroupStr, AfterIncomeGroup.class); if (Objects.isNull(afterIncomegroup)) { throw new CostException("未能正确归集对应的...."); } // 转换一下其他的 if (afterIncomegroup.getOpenDepartmentStatus() == 1 && afterIncomegroup.getStartDepartmentStatus() == 1) { // 排除会计科目 List sets = incomeGroupSetService.list( new LambdaQueryWrapper() .eq(CostIncomeGroupSet::getOpenDepartmentStatus, 1).eq(CostIncomeGroupSet::getStartDepartmentStatus, 1).eq(CostIncomeGroupSet::getHospId, hospId) ); if (!sets.isEmpty()) { for (CostIncomeGroupSet set : sets) { String accountCode = set.getAccountCode(); if (StrUtil.isBlank(accountCode)) { continue; } String[] split = accountCode.split(StrUtil.COMMA); boolean contains = CollUtil.newArrayList(split).contains(costIncomeGroup.getAccountCode()); if (contains) { // 钱全给执行科室 incomeCollection.setAmount(new BigDecimal("0.0000")); incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode()); incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName()); incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode()); incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName()); list.add(incomeCollection); // 执行科室数据 IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class); startCollect.setAmount(costIncomeGroup.getAmount()); startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode()); startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName()); startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode()); startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName()); log.info("start:{}", startCollect); log.info("open:{}", incomeCollection); list.add(startCollect); } else { if (afterIncomegroup.getDirectStatus() == 2) { afterIncomegroup.setDirectStatus(0); } incomeCollection.setIsDirectIncome(afterIncomegroup.getDirectStatus()); String responsibilityCode = afterIncomegroup.getResponsibilityCode(); if (StrUtil.isNotBlank(responsibilityCode)) { incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName() + "|" + afterIncomegroup.getStartDepartmentName()); incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode() + "|" + afterIncomegroup.getStartDepartmentCode()); incomeCollection.setResponsibilityCode(responsibilityCode); incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName()); incomeCollection.setAmount(afterIncomegroup.getOtherResponsibilityDecimal()); list.add(incomeCollection); } else { // 开单中心的数据 incomeCollection.setAmount(afterIncomegroup.getOpenDepartmentDecimal()); incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode()); incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName()); incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode()); incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName()); list.add(incomeCollection); // 执行科室数据 IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class); startCollect.setAmount(afterIncomegroup.getStartDepartmentDecimal()); startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode()); startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName()); startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode()); startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName()); log.info("start:{}", startCollect); log.info("open:{}", incomeCollection); list.add(startCollect); } } } } return; } if (afterIncomegroup.getDirectStatus() == 2) { afterIncomegroup.setDirectStatus(0); } incomeCollection.setIsDirectIncome(afterIncomegroup.getDirectStatus()); String responsibilityCode = afterIncomegroup.getResponsibilityCode(); if (StrUtil.isNotBlank(responsibilityCode)) { incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName() + "|" + afterIncomegroup.getStartDepartmentName()); incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode() + "|" + afterIncomegroup.getStartDepartmentCode()); incomeCollection.setResponsibilityCode(responsibilityCode); incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName()); incomeCollection.setAmount(afterIncomegroup.getOtherResponsibilityDecimal()); list.add(incomeCollection); } else { // 开单中心的数据 incomeCollection.setAmount(afterIncomegroup.getOpenDepartmentDecimal()); incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode()); incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName()); incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode()); incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName()); list.add(incomeCollection); // 执行科室数据 IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class); startCollect.setAmount(afterIncomegroup.getStartDepartmentDecimal()); startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode()); startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName()); startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode()); startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName()); log.info("start:{}", startCollect); log.info("open:{}", incomeCollection); list.add(startCollect); } }); // TODO: 2021/8/10 几十万条数据如何处理 待测试处理 Map> collect = list.stream().collect(Collectors.groupingBy(i -> i.getDepartmentCode() + "cost" + i.getResponsibilityCode() + "cost" + i.getAccountingCode() + "cost" + i.getProductCode())); List realList = new LinkedList<>(); Set strings = collect.keySet(); for (String str : strings) { List incomeCollections = collect.get(str); if (CollUtil.isNotEmpty(incomeCollections)) { BigDecimal reduce = incomeCollections.stream().map(IncomeCollection::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); IncomeCollection incomeCollection = incomeCollections.get(0); incomeCollection.setAmount(reduce); realList.add(incomeCollection); } } this.saveBatch(realList); } else { throw new CostException("已有人在操作,请稍后重试"); } } finally { redisLock.release(key, timestamp); } } /** * 按年月撤销归集 * * @param year 年 数字类型 * @param month 月 数字 * @param hospId 医院id */ @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void cancelCollect(Integer year, Integer month, Long hospId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(IncomeCollection::getYear, year) .eq(IncomeCollection::getMonth, month) .eq(IncomeCollection::getHospId, hospId); this.remove(wrapper); } /** * 归集后数据分页列表 * * @param collectDTO {@link CollectDTO} 查询相关参数 * @return {@link PageUtils} */ @Override public PageUtils collectList(CollectDTO collectDTO) { // 分页数据初始化 Integer current = collectDTO.getCurrent(); current = (Objects.isNull(current) || current == 0) ? 0 : current - 1; Integer pageSize = collectDTO.getPageSize(); pageSize = Objects.isNull(pageSize) ? 10 : pageSize; collectDTO.setCurrent(current * pageSize); collectDTO.setPageSize(pageSize); // 查询需要的数据 DB List list = baseMapper.getCollectList(collectDTO); int count = baseMapper.getCollectListCount(collectDTO); BigDecimal totalAmount = baseMapper.getTotalAmount(collectDTO); return new PageUtils(list, count, pageSize, current + 1, totalAmount); } /** * 数据报表 * * @param collectDTO * @return */ @Override public CollectDataFormVO collectDataForm(CollectDTO collectDTO) { Long hospId = collectDTO.getHospId(); String date = collectDTO.getDate(); // 1. 得到所有的标题栏 责任中心代码 List responsibilities = this.baseMapper.getResponsibility(hospId, collectDTO.getResponsibilityCode(), date).stream().distinct().collect(Collectors.toList()); if (responsibilities.isEmpty()) { return new CollectDataFormVO(); } List responsibilityCodes = responsibilities.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList()); responsibilities.add(0, new CodeAndNameVO("#", "#")); responsibilities.add(responsibilities.size(), new CodeAndNameVO("合计", "合计")); List titleData = responsibilities.stream().map(CodeAndNameVO::getName).collect(Collectors.toList()); Map titleMap = new HashMap<>(); for (int i = 0; i < titleData.size(); i++) { titleMap.put(i + 1, titleData.get(i)); } // 所有的数据格式 List realData = this.baseMapper.getAccount(hospId, date); if (realData.isEmpty()) { return new CollectDataFormVO(); } // realData [#,123,123,"合计"] List> realDatas = new ArrayList<>(); List accounts = realData.stream().distinct().collect(Collectors.toList()); for (CodeAndNameVO account : accounts) { Map map = new HashMap<>(); for (int i = 0; i < responsibilities.size(); i++) { if (i == 0) { map.put(i + 1, account.getName()); continue; } else if (i == responsibilities.size() - 1) { // TODO: 2021/8/12 合计 BigDecimal amount = this.baseMapper.getCountAccountAndResponsibilities(hospId, date, account.getCode(), responsibilityCodes); map.put(i + 1, amount); continue; } List list = this.list( new LambdaQueryWrapper().select(IncomeCollection::getAmount) .eq(IncomeCollection::getHospId, hospId) .eq(IncomeCollection::getYear, date.substring(0, 4)) .eq(IncomeCollection::getMonth, date.substring(4)) .eq(IncomeCollection::getAccountingCode, account.getCode()) .eq(IncomeCollection::getResponsibilityCode, responsibilities.get(i).getCode()) ); if (list.isEmpty()) { map.put(i + 1, new BigDecimal("0.0000")); } else { BigDecimal reduce = list.stream().map(IncomeCollection::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); map.put(i + 1, reduce); } } realDatas.add(map); } // // 合计栏计算 // 长度一致的 List accountingCodes = realData.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList()); Map map = new HashMap<>(); for (int i = 0; i < responsibilities.size(); i++) { if (i == 0) { map.put(i + 1, "合计"); continue; } else if (i == responsibilities.size() - 1) { // 统计问题 BigDecimal bigDecimal = this.baseMapper.getCountByResponsibilitiesAndAccounts(responsibilityCodes, accountingCodes, hospId, date); map.put(i + 1, bigDecimal); continue; } BigDecimal bigDecimal = this.baseMapper.getCountByResponseAndAccounts(responsibilities.get(i).getCode(), accountingCodes, hospId, date); map.put(i + 1, bigDecimal); } return new CollectDataFormVO(titleMap, realDatas, map); } }