package com.imed.costaccount.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; 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.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.CodeAndNameVO; import com.imed.costaccount.model.vo.CollectDataFormVO; import com.imed.costaccount.model.vo.CollectedVO; import com.imed.costaccount.model.vo.CollectionVO; import com.imed.costaccount.service.AccountingService; import com.imed.costaccount.service.IncomeCollectionService; import com.imed.costaccount.service.ResponsibilityService; 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 ResponsibilityService responsibilityService; private final AccountingService accountingService; public IncomeCollectionServiceImpl(RedisLock redisLock, CostIncomeGroupMapper incomeGroupMapper, ResponsibilityService responsibilityService, AccountingService accountingService) { this.redisLock = redisLock; this.incomeGroupMapper = incomeGroupMapper; this.responsibilityService = responsibilityService; this.accountingService = accountingService; } /** * 获取收入归集分页列表 * * @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.getAccountCode() + "cost" + i.getProductCode())); // 遍历map 组装成List保存 List list = new LinkedList<>(); Set>> entries = collectMap.entrySet(); entries.stream().map(Map.Entry::getValue).flatMap(Collection::stream).forEach(costIncomeGroup -> { 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.setAmount(costIncomeGroup.getAmount()); incomeCollection.setHospId(hospId); // todo 来源id暂时不确定 incomeCollection.setFileId(costIncomeGroup.getFileId()); incomeCollection.setCreateTime(System.currentTimeMillis()); String afterIncomeGroupStr = costIncomeGroup.getAfterIncomeGroup(); AfterIncomegroup afterIncomegroup = JSONUtil.toBean(afterIncomeGroupStr, AfterIncomegroup.class); if (Objects.isNull(afterIncomegroup)) { throw new CostException("未能正确归集对应的...."); } // 转换一下其他的 if (afterIncomegroup.getDirectStatus() == 2) { afterIncomegroup.setDirectStatus(0); } incomeCollection.setIsDirectIncome(afterIncomegroup.getDirectStatus()); String responsibilityCode = afterIncomegroup.getResponsibilityCode(); if (StrUtil.isBlank(responsibilityCode)) { responsibilityCode = "-"; afterIncomegroup.setResponsibilityName("-"); } // 开单中心的数据 incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode()); incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName()); incomeCollection.setResponsibilityCode(responsibilityCode); incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName()); list.add(incomeCollection); // 执行科室数据 String startDepartmentCode = afterIncomegroup.getStartDepartmentCode(); incomeCollection.setDepartmentCode(startDepartmentCode); incomeCollection.setDepartmentName(afterIncomegroup.getStartDepartmentName()); incomeCollection.setResponsibilityCode(responsibilityCode); incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName()); list.add(incomeCollection); }); // TODO: 2021/8/10 几十万条数据如何处理 待测试处理 this.saveBatch(list); } 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, collectDTO.getAccountingCode(), 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); } }