|
@@ -1,20 +1,24 @@
|
|
|
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.AfterIncomegroup;
|
|
|
-import com.imed.costaccount.model.CostIncomeGroup;
|
|
|
-import com.imed.costaccount.model.IncomeCollection;
|
|
|
+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;
|
|
@@ -33,10 +37,19 @@ public class IncomeCollectionServiceImpl
|
|
|
extends ServiceImpl<IncomeCollectionMapper, IncomeCollection>
|
|
|
implements IncomeCollectionService {
|
|
|
|
|
|
+ private final RedisLock redisLock;
|
|
|
+
|
|
|
private final CostIncomeGroupMapper incomeGroupMapper;
|
|
|
|
|
|
- public IncomeCollectionServiceImpl(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;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -83,65 +96,84 @@ public class IncomeCollectionServiceImpl
|
|
|
@Override
|
|
|
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
|
|
public void collect(Integer year, Integer month, Long hospId) {
|
|
|
- // 可能几十万次数据 需要分段异步多线程处理,分布式锁处理,
|
|
|
- List<CostIncomeGroup> costIncomeGroups = incomeGroupMapper.selectList(
|
|
|
- new LambdaQueryWrapper<CostIncomeGroup>()
|
|
|
- .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);
|
|
|
- }
|
|
|
- // 根据会计科目和成本项目归纳所有数据
|
|
|
- Map<String, List<CostIncomeGroup>> collectMap = costIncomeGroups.stream()
|
|
|
- .collect(Collectors.groupingBy(i -> i.getAccountCode() + "cost" + i.getProductCode()));
|
|
|
-
|
|
|
- // 遍历map 组装成List保存
|
|
|
- List<IncomeCollection> list = new LinkedList<>();
|
|
|
- Set<Map.Entry<String, List<CostIncomeGroup>>> 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());
|
|
|
- // 开单中心的数据
|
|
|
- incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
|
|
|
- incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
|
|
|
- incomeCollection.setResponsibilityCode(afterIncomegroup.getResponsibilityCode());
|
|
|
- incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
|
|
|
-
|
|
|
- list.add(incomeCollection);
|
|
|
- // 执行科室数据
|
|
|
- incomeCollection.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
|
|
|
- incomeCollection.setDepartmentName(afterIncomegroup.getStartDepartmentName());
|
|
|
- incomeCollection.setResponsibilityCode(afterIncomegroup.getResponsibilityCode());
|
|
|
- incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
|
|
|
- list.add(incomeCollection);
|
|
|
- });
|
|
|
|
|
|
+ // 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<CostIncomeGroup> costIncomeGroups = incomeGroupMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<CostIncomeGroup>()
|
|
|
+ .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<IncomeCollection>().eq(IncomeCollection::getYear, year).eq(IncomeCollection::getMonth, month).eq(IncomeCollection::getHospId, hospId)
|
|
|
+ );
|
|
|
+ // 根据会计科目和成本项目归纳所有数据
|
|
|
+ Map<String, List<CostIncomeGroup>> collectMap = costIncomeGroups.stream()
|
|
|
+ .collect(Collectors.groupingBy(i -> i.getAccountCode() + "cost" + i.getProductCode()));
|
|
|
+
|
|
|
+ // 遍历map 组装成List保存
|
|
|
+ List<IncomeCollection> list = new LinkedList<>();
|
|
|
+ Set<Map.Entry<String, List<CostIncomeGroup>>> 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());
|
|
|
+ // 开单中心的数据
|
|
|
+ incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
|
|
|
+ incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
|
|
|
+ incomeCollection.setResponsibilityCode(afterIncomegroup.getResponsibilityCode());
|
|
|
+ incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
|
|
|
+
|
|
|
+ list.add(incomeCollection);
|
|
|
+ // 执行科室数据
|
|
|
+ incomeCollection.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
|
|
|
+ incomeCollection.setDepartmentName(afterIncomegroup.getStartDepartmentName());
|
|
|
+ incomeCollection.setResponsibilityCode(afterIncomegroup.getResponsibilityCode());
|
|
|
+ incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
|
|
|
+ list.add(incomeCollection);
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ // TODO: 2021/8/10 几十万条数据如何处理 待测试处理
|
|
|
+ this.saveBatch(list);
|
|
|
+ } else {
|
|
|
+ throw new CostException("已有人在操作,请稍后重试");
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ redisLock.release(key, timestamp);
|
|
|
+ }
|
|
|
|
|
|
- // TODO: 2021/8/10 几十万条数据如何处理 待测试处理
|
|
|
- this.saveBatch(list);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -184,4 +216,88 @@ public class IncomeCollectionServiceImpl
|
|
|
|
|
|
return new PageUtils(list, count, pageSize, current, totalAmount);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 数据报表
|
|
|
+ *
|
|
|
+ * @param collectDTO
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public CollectDataFormVO collectDataForm(CollectDTO collectDTO) {
|
|
|
+ Long hospId = collectDTO.getHospId();
|
|
|
+ String date = collectDTO.getDate();
|
|
|
+ // 1. 得到所有的标题栏 责任中心代码
|
|
|
+ List<CodeAndNameVO> responsibilities = this.baseMapper.getResponsibility(hospId, collectDTO.getResponsibilityCode(), date).stream().distinct().collect(Collectors.toList());
|
|
|
+ if (responsibilities.isEmpty()) {
|
|
|
+ return new CollectDataFormVO();
|
|
|
+ }
|
|
|
+ List<String> responsibilityCodes = responsibilities.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
|
|
|
+ responsibilities.add(0, new CodeAndNameVO("#", "#"));
|
|
|
+ responsibilities.add(responsibilities.size(), new CodeAndNameVO("合计", "合计"));
|
|
|
+ List<String> titleData = responsibilities.stream().map(CodeAndNameVO::getName).collect(Collectors.toList());
|
|
|
+ Map<Integer, String> titleMap = new HashMap<>();
|
|
|
+ for (int i = 0; i < titleData.size(); i++) {
|
|
|
+ titleMap.put(i + 1, titleData.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 所有的数据格式
|
|
|
+ List<CodeAndNameVO> realData = this.baseMapper.getAccount(hospId, collectDTO.getAccountingCode(), date);
|
|
|
+ if (realData.isEmpty()) {
|
|
|
+ return new CollectDataFormVO();
|
|
|
+ }
|
|
|
+ // realData [#,123,123,"合计"]
|
|
|
+ List<Map<Integer, Object>> realDatas = new ArrayList<>();
|
|
|
+ List<CodeAndNameVO> accounts = realData.stream().distinct().collect(Collectors.toList());
|
|
|
+
|
|
|
+ for (CodeAndNameVO account : accounts) {
|
|
|
+ Map<Integer, Object> map = new HashMap<>();
|
|
|
+ for (int i = 0; i < responsibilities.size(); i++) {
|
|
|
+ if (i == 0) {
|
|
|
+ map.put(i + 1, accounts.get(i).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<IncomeCollection> list = this.list(
|
|
|
+ new LambdaQueryWrapper<IncomeCollection>().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<String> accountingCodes = realData.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
|
|
|
+ Map<Integer, Object> 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);
|
|
|
+ }
|
|
|
}
|