فهرست منبع

全院损益计算添加同环比计算

JammeyJiang 1 ماه پیش
والد
کامیت
e8b8f80ab2

+ 7 - 2
src/main/java/com/kcim/common/constants/ParameterConstant.java

@@ -58,7 +58,12 @@ public interface ParameterConstant {
     Long SHOW_PERCENT=1851077044079824896L;
 
     /**
-     * 同环比计算方式 1代码计算 2脚本计算
+     * 科室损益同环比计算方式 1代码计算 2脚本计算
      */
-    Long YM_CALC_TYPE=1943243153054240768L;
+    Long DEPT_YM_CALC_TYPE =1943243153054240768L;
+
+    /**
+     * 全院损益同环比计算方式 1代码计算 2脚本计算
+     */
+    Long HOSP_YM_CALC_TYPE =1945667397503553536L;
 }

+ 30 - 0
src/main/java/com/kcim/dao/model/HospProfitAndLoss.java

@@ -95,4 +95,34 @@ public class HospProfitAndLoss implements Serializable {
     private String parentResponsibilityCode;
     @TableField(exist = false)
     private String parentResponsibilityName;
+
+    /**
+     * 预算金额
+     */
+    private BigDecimal budgetAmount;
+
+    /**
+     * 上期金额
+     */
+    private BigDecimal prevPeriodAmount;
+
+    /**
+     * 同期金额
+     */
+    private BigDecimal samePeriodAmount;
+
+    /**
+     * 完成率
+     */
+    private BigDecimal completionRate;
+
+    /**
+     * 环比
+     */
+    private BigDecimal momRate;
+
+    /**
+     * 同比
+     */
+    private BigDecimal yoyRate;
 }

+ 1 - 1
src/main/java/com/kcim/service/impl/CostDepartmentProfitServiceImpl.java

@@ -2900,7 +2900,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
      * @return
      */
     public boolean IsNeedCalcYM() {
-        String parameterValue = centerService.getParameterValue(ParameterConstant.YM_CALC_TYPE);
+        String parameterValue = centerService.getParameterValue(ParameterConstant.DEPT_YM_CALC_TYPE);
         return NumberConstant.ONE_S.equals(parameterValue);
     }
 

+ 239 - 9
src/main/java/com/kcim/service/impl/HospProfitAndLossServiceImpl.java

@@ -13,25 +13,20 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.ImmutableMap;
 import com.kcim.common.constants.Constant;
 import com.kcim.common.constants.NumberConstant;
+import com.kcim.common.constants.ParameterConstant;
 import com.kcim.common.constants.SQLParameter;
 import com.kcim.common.enums.*;
 import com.kcim.common.exception.CostException;
 import com.kcim.common.file.MinioConfig;
 import com.kcim.common.file.MinioFileUtil;
-import com.kcim.common.util.BeanUtil;
-import com.kcim.common.util.DateUtils;
-import com.kcim.common.util.PageUtils;
-import com.kcim.common.util.UserContext;
+import com.kcim.common.util.*;
 import com.kcim.common.util.excel.ExcelPoiUtil;
 import com.kcim.common.util.excel.entity.ColEntity;
 import com.kcim.common.util.excel.entity.TitleEntity;
 import com.kcim.dao.mapper.HospProfitAndLossMapper;
 import com.kcim.dao.model.*;
 import com.kcim.service.*;
-import com.kcim.vo.AllocationQueryReportVO;
-import com.kcim.vo.HospProfitAndLossVo;
-import com.kcim.vo.HospProfitVO;
-import com.kcim.vo.RelationVO;
+import com.kcim.vo.*;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.fileupload.FileItem;
 import org.apache.commons.fileupload.FileItemFactory;
@@ -79,6 +74,8 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
     private final FileRecordService fileRecordService;
     private final SqlService sqlService;
 
+    private final CenterService centerService;
+
     private final MinioConfig minioConfig;
 
     private final MinioFileUtil minioFileUtil;
@@ -91,7 +88,13 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
                                         CostShareLevelService shareLevelService,
                                         CostOtherPaymentsDataService otherPaymentsDataService,
                                         ResponsibilityService responsibilityService,
-                                        CostAccountShareService accountShareService, FileRecordService fileRecordService, MinioConfig minioConfig, MinioFileUtil minioFileUtil,SqlService sqlService) {
+                                        CostAccountShareService accountShareService,
+                                        FileRecordService fileRecordService,
+                                        MinioConfig minioConfig,
+                                        MinioFileUtil minioFileUtil,
+                                        SqlService sqlService,
+                                        CenterService centerService
+    ) {
         this.reportFormService = reportFormService;
         this.collectionService = collectionService;
         this.allocationQueryService = allocationQueryService;
@@ -105,6 +108,7 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
         this.minioConfig = minioConfig;
         this.minioFileUtil = minioFileUtil;
         this.sqlService=sqlService;
+        this.centerService=centerService;
     }
 
 
@@ -196,6 +200,10 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
     @Override
     public void calcHospProfit(String date, Long hospId, Integer reportType){
         calcHospProfitAction(date,hospId,reportType);
+        //需要用代码计算同环比时
+        if(IsNeedCalcYM()){
+            handleSpecificMonthsCalculation(hospId, date, reportType);
+        }
         execHospProfitSql(date,date);
     }
     @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
@@ -399,6 +407,228 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
 
     }
 
+    /**
+     * 是否需要计算同环比
+     * @return
+     */
+    public boolean IsNeedCalcYM() {
+        String parameterValue = centerService.getParameterValue(ParameterConstant.HOSP_YM_CALC_TYPE);
+        return NumberConstant.ONE_S.equals(parameterValue);
+    }
+
+    /**
+     * 处理指定月份及关联月份的计算(当前月、下个月、明年同月)
+     *
+     * @param hospId      院区ID
+     * @param computeDate 日期字符串,格式:YYYY-MM
+     * @param shareType   报表类型
+     */
+    public void handleSpecificMonthsCalculation(Long hospId, String computeDate, Integer shareType) {
+        // 解析输入的年月
+        Integer currentYear = ComputeDateUtils.getComputeYear(computeDate);
+        Integer currentMonth = ComputeDateUtils.getComputeMonth(computeDate);
+        // 计算需要处理的期间信息
+        List<PeriodInfoVO> periods = calculateAllRelatedPeriods(currentYear, currentMonth);
+
+        // 获取所有相关的期间数据
+        Map<String, List<HospProfitAndLoss>> allDataMap = loadAllPeriodData(hospId, shareType, periods);
+
+        // 对每个期间执行计算
+        for (PeriodInfoVO period : periods) {
+            String currentKey = buildPeriodKey(period.getYear(), period.getMonth());
+            if (allDataMap.containsKey(currentKey) && !CollectionUtils.isEmpty(allDataMap.get(currentKey))) {
+                List<HospProfitAndLoss> currentRecords = allDataMap.get(currentKey);
+                List<HospProfitAndLoss> prevRecords = getPeriodData(allDataMap, period.getPrevYear(), period.getPrevMonth());
+                List<HospProfitAndLoss> lastYearRecords = getPeriodData(allDataMap, period.getLastYear(), period.getLastMonth());
+
+                // 执行批量计算
+                calculatePeriodComparisonWithPreloadedData(currentRecords, prevRecords, lastYearRecords);
+            }
+        }
+    }
+
+    /**
+     * 构建所有需要处理的期间信息
+     */
+    private List<PeriodInfoVO> calculateAllRelatedPeriods(int baseYear, int baseMonth) {
+        List<PeriodInfoVO> result = new ArrayList<>();
+
+        // 添加当前期间
+        result.add(calculatePeriodInfo(baseYear, baseMonth));
+
+        // 下个月
+        int nextMonth = baseMonth + 1;
+        int nextMonthYear = baseYear;
+        if (baseMonth == 12) {
+            nextMonth = 1;
+            nextMonthYear += 1;
+        }
+        result.add(calculatePeriodInfo(nextMonthYear, nextMonth));
+
+        // 明年同月
+        result.add(calculatePeriodInfo(baseYear + 1, baseMonth));
+
+        return result;
+    }
+
+    /**
+     * 构建单个期间的信息(当前月、上月、去年同期)
+     */
+    private PeriodInfoVO calculatePeriodInfo(int year, int month) {
+        PeriodInfoVO info = new PeriodInfoVO();
+
+        info.setYear( year);
+        info.setMonth( month);
+
+        // 上期
+        if (month == 1) {
+            info.setPrevYear(year - 1);
+            info.setPrevMonth(12);
+        } else {
+            info.setPrevYear( year);
+            info.setPrevMonth(month - 1);
+        }
+
+        // 同期
+        info.setLastYear(year - 1);
+        info.setLastMonth( month);
+
+        return info;
+    }
+
+    /**
+     * 加载所有相关期间的数据(去重处理)
+     */
+    private Map<String, List<HospProfitAndLoss>> loadAllPeriodData(Long hospId, Integer shareType, List<PeriodInfoVO> periods) {
+        Set<String> allPeriodKeys = new HashSet<>();
+
+        // 收集所有需要查询的年月组合
+        for (PeriodInfoVO period : periods) {
+            allPeriodKeys.add(buildPeriodKey(period.getYear(), period.getMonth()));
+            allPeriodKeys.add(buildPeriodKey(period.getPrevYear(), period.getPrevMonth()));
+            allPeriodKeys.add(buildPeriodKey(period.getLastYear(), period.getLastMonth()));
+        }
+
+        // 去重后查询
+        Map<String, List<HospProfitAndLoss>> result = new HashMap<>();
+        Set<String> processedKeys = new HashSet<>(); // 已处理的键集合
+
+        for (String key : allPeriodKeys) {
+            if (!processedKeys.contains(key)) {
+                int[] ym = parsePeriodKey(key);
+                List<HospProfitAndLoss> records = getPeriodRecords(hospId, shareType, ym[0], ym[1]);
+                result.put(key, records);
+                processedKeys.add(key); // 标记为已处理
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * 获取指定期间的数据
+     *
+     * @param hospId    院区ID
+     * @param shareType 报表类型
+     * @param year      年份
+     * @param month     月份
+     * @return 符合条件的数据列表
+     */
+    private List<HospProfitAndLoss> getPeriodRecords(Long hospId, Integer shareType, int year, int month) {
+        return this.list(new QueryWrapper<HospProfitAndLoss>().lambda()
+                .eq(HospProfitAndLoss::getHospId, hospId)
+                .eq(HospProfitAndLoss::getReportType, shareType)
+                .eq(HospProfitAndLoss::getDateYear, year)
+                .eq(HospProfitAndLoss::getDateMonth, month)
+                .eq(HospProfitAndLoss::getDeleteTime, 0));
+    }
+
+    /**
+     * 根据年月获取对应的数据(从内存中查找)
+     */
+    private List<HospProfitAndLoss> getPeriodData(Map<String, List<HospProfitAndLoss>> dataMap, int year, int month) {
+        String key = buildPeriodKey(year, month);
+        return dataMap.getOrDefault(key, Collections.emptyList());
+    }
+
+    /**
+     * 批量计算同环比及同期金额(使用预加载数据)
+     */
+    public void calculatePeriodComparisonWithPreloadedData(
+            List<HospProfitAndLoss> currentRecords,
+            List<HospProfitAndLoss> prevRecords,
+            List<HospProfitAndLoss> lastYearRecords) {
+
+        if (CollectionUtils.isEmpty(currentRecords)) {
+            return;
+        }
+
+        // 创建报表编号到记录的映射
+        Map<String, HospProfitAndLoss> prevMap = new HashMap<>();
+        for (HospProfitAndLoss record : prevRecords) {
+            String key = buildRecordKey(record);
+            prevMap.put(key, record);
+        }
+
+        Map<String, HospProfitAndLoss> lastYearMap = new HashMap<>();
+        for (HospProfitAndLoss record : lastYearRecords) {
+            String key = buildRecordKey(record);
+            lastYearMap.put(key, record);
+        }
+
+        // 批量更新数据
+        for (HospProfitAndLoss record : currentRecords) {
+            String key = buildRecordKey(record);
+            HospProfitAndLoss prevRecord = prevMap.get(key);
+            HospProfitAndLoss lastYearRecord = lastYearMap.get(key);
+
+            // 设置上期和同期金额
+            record.setPrevPeriodAmount(prevRecord != null ? prevRecord.getAmount() : BigDecimal.ZERO);
+            record.setSamePeriodAmount(lastYearRecord != null ? lastYearRecord.getAmount() : BigDecimal.ZERO);
+
+            // 计算环比和同比
+            record.setMomRate(calculateProfitRate(record.getAmount().subtract(record.getSamePeriodAmount()), record.getPrevPeriodAmount()));
+            record.setYoyRate(calculateProfitRate(record.getAmount().subtract(record.getSamePeriodAmount()), record.getSamePeriodAmount()));
+        }
+
+        // 批量更新数据库
+        this.updateBatchById(currentRecords);
+    }
+
+    /**
+     * 构建期间键(用于存储和检索数据)
+     */
+    private String buildPeriodKey(int year, int month) {
+        return year + "_" + month;
+    }
+
+    /**
+     * 解析期间键为年月数组
+     */
+    private int[] parsePeriodKey(String key) {
+        String[] parts = key.split("_");
+        return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])};
+    }
+
+    /**
+     * 构建记录的唯一标识键(责任码 + 报表编号)
+     */
+    private String buildRecordKey(HospProfitAndLoss record) {
+        return String.valueOf(record.getReportNum());
+    }
+
+    /**
+     * 安全地计算百分比:避免除零错误
+     */
+    private BigDecimal calculateProfitRate(BigDecimal current, BigDecimal base) {
+        if (base == null || base.compareTo(BigDecimal.ZERO) == 0) {
+            return BigDecimal.ZERO;
+        }
+        BigDecimal bigDecimal = current.divide(base, 4, RoundingMode.HALF_UP).setScale(4, RoundingMode.HALF_UP);
+        return bigDecimal;
+    }
+
     /**
      * 执行全院损益后续脚本
      * @param date