|
@@ -52,6 +52,7 @@ import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Propagation;
|
|
import org.springframework.transaction.annotation.Propagation;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.util.CollectionUtils;
|
|
import org.springframework.util.CollectionUtils;
|
|
|
|
+import org.springframework.util.ObjectUtils;
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
|
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
|
@@ -1208,6 +1209,192 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
|
|
//记录最后一次 损益计算日期
|
|
//记录最后一次 损益计算日期
|
|
computeLastProfitDateRepository.saveLastComputeDate(hospId, computeDate);
|
|
computeLastProfitDateRepository.saveLastComputeDate(hospId, computeDate);
|
|
}
|
|
}
|
|
|
|
+ /**
|
|
|
|
+ * 处理指定月份及关联月份的计算(当前月、下个月、明年同月)
|
|
|
|
+ *
|
|
|
|
+ * @param hospId 院区ID
|
|
|
|
+ * @param computeDate 日期字符串,格式:YYYY-MM
|
|
|
|
+ * @param shareType 报表类型
|
|
|
|
+ */
|
|
|
|
+ public void handleSpecificMonthsCalculation(Long hospId, String computeDate, String shareType) {
|
|
|
|
+
|
|
|
|
+ // 解析输入的年月
|
|
|
|
+ int currentYear = Integer.parseInt(computeDate.substring(0, 4));
|
|
|
|
+ int currentMonth = Integer.parseInt(computeDate.substring(5, 7));
|
|
|
|
+
|
|
|
|
+ // 计算下个月
|
|
|
|
+ int nextMonth = currentMonth + 1;
|
|
|
|
+ int nextMonthYear = currentYear;
|
|
|
|
+
|
|
|
|
+ if (nextMonth > 12) {
|
|
|
|
+ nextMonth = 1;
|
|
|
|
+ nextMonthYear = currentYear + 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 计算明年同月
|
|
|
|
+ int nextYearMonth = currentMonth;
|
|
|
|
+ int nextYearMonthYear = currentYear + 1;
|
|
|
|
+
|
|
|
|
+ // 检查各月份是否有数据
|
|
|
|
+ boolean currentExists = checkDataExistence(hospId, shareType, currentYear, currentMonth);
|
|
|
|
+ boolean nextMonthExists = checkDataExistence(hospId, shareType, nextMonthYear, nextMonth);
|
|
|
|
+ boolean nextYearMonthExists = checkDataExistence(hospId, shareType, nextYearMonthYear, nextYearMonth);
|
|
|
|
+
|
|
|
|
+ // 执行当前月份计算
|
|
|
|
+ if (currentExists) {
|
|
|
|
+ calculatePeriodComparisonOptimized(hospId, shareType, currentYear, currentMonth);
|
|
|
|
+ }
|
|
|
|
+ // 执行下个月计算
|
|
|
|
+ if (nextMonthExists) {
|
|
|
|
+ calculatePeriodComparisonOptimized(hospId, shareType, nextMonthYear, nextMonth);
|
|
|
|
+ }
|
|
|
|
+ // 执行明年同月计算
|
|
|
|
+ if (nextYearMonthExists) {
|
|
|
|
+ calculatePeriodComparisonOptimized(hospId, shareType, nextYearMonthYear, nextYearMonth);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 检查指定条件的数据是否存在
|
|
|
|
+ *
|
|
|
|
+ * @param hospId 院区ID
|
|
|
|
+ * @param shareType 报表类型
|
|
|
|
+ * @param year 年份
|
|
|
|
+ * @param month 月份
|
|
|
|
+ * @return 是否存在数据
|
|
|
|
+ */
|
|
|
|
+ private boolean checkDataExistence(Long hospId, String shareType, int year, int month) {
|
|
|
|
+ LambdaQueryWrapper<CostDepartmentProfit> last = new QueryWrapper<CostDepartmentProfit>().lambda()
|
|
|
|
+ .eq(CostDepartmentProfit::getHospId, hospId)
|
|
|
|
+ .eq(CostDepartmentProfit::getShareType, shareType)
|
|
|
|
+ .eq(CostDepartmentProfit::getYear, year)
|
|
|
|
+ .eq(CostDepartmentProfit::getMonth, month)
|
|
|
|
+ .eq(CostDepartmentProfit::getDeleteTime, 0)
|
|
|
|
+ .last("LIMIT 1");
|
|
|
|
+ CostDepartmentProfit one = this.getOne(last);
|
|
|
|
+ return !ObjectUtils.isEmpty(one);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 批量计算同环比及同期金额
|
|
|
|
+ *
|
|
|
|
+ * @param hospId 院区ID
|
|
|
|
+ * @param shareType 报表类型
|
|
|
|
+ * @param year 年份
|
|
|
|
+ * @param month 月份
|
|
|
|
+ */
|
|
|
|
+ public void calculatePeriodComparisonOptimized(Long hospId, String shareType, Integer year, Integer month) {
|
|
|
|
+ // 查询符合条件的数据
|
|
|
|
+ List<CostDepartmentProfit> records = getPeriodRecords(hospId, shareType, year, month);
|
|
|
|
+ // 如果没有记录,直接返回
|
|
|
|
+ if (CollectionUtils.isEmpty(records)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 构建上期和同期的时间参数
|
|
|
|
+ int prevPeriodYear = year;
|
|
|
|
+ int prevPeriodMonth = month - 1;
|
|
|
|
+ if (month == 1) {
|
|
|
|
+ prevPeriodYear = year - 1;
|
|
|
|
+ prevPeriodMonth = 12;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int lastYearYear = year - 1;
|
|
|
|
+
|
|
|
|
+ // 批量查询上期数据
|
|
|
|
+ List<CostDepartmentProfit> prevPeriodRecords = getPeriodRecords(hospId, shareType, prevPeriodYear, prevPeriodMonth);
|
|
|
|
+
|
|
|
|
+ // 批量查询同期数据
|
|
|
|
+ List<CostDepartmentProfit> lastYearRecords = getPeriodRecords(hospId, shareType, lastYearYear, month);
|
|
|
|
+
|
|
|
|
+ // 将上期数据转换为映射
|
|
|
|
+ Map<String, CostDepartmentProfit> prevPeriodMap = new HashMap<>();
|
|
|
|
+ for (CostDepartmentProfit record : prevPeriodRecords) {
|
|
|
|
+ String key = buildRecordKey(record);
|
|
|
|
+ prevPeriodMap.put(key, record);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 将同期数据转换为映射
|
|
|
|
+ Map<String, CostDepartmentProfit> lastYearMap = new HashMap<>();
|
|
|
|
+ for (CostDepartmentProfit record : lastYearRecords) {
|
|
|
|
+ String key = buildRecordKey(record);
|
|
|
|
+ lastYearMap.put(key, record);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 批量更新数据
|
|
|
|
+ for (CostDepartmentProfit record : records) {
|
|
|
|
+ // 构建上期和同期的键
|
|
|
|
+ String prevKey = String.format("%d_%d_%s_%d",
|
|
|
|
+ prevPeriodYear, prevPeriodMonth, record.getResponsibilityCode(), record.getReportNum());
|
|
|
|
+ String lastYearKey = String.format("%d_%d_%s_%d",
|
|
|
|
+ lastYearYear, month, record.getResponsibilityCode(), record.getReportNum());
|
|
|
|
+
|
|
|
|
+ // 获取上期和同期记录
|
|
|
|
+ CostDepartmentProfit prevRecord = prevPeriodMap.get(prevKey);
|
|
|
|
+ CostDepartmentProfit lastYearRecord = lastYearMap.get(lastYearKey);
|
|
|
|
+
|
|
|
|
+ // 设置上期和同期金额
|
|
|
|
+ record.setPrevPeriodAmount(prevRecord != null ? prevRecord.getAmount() : BigDecimal.ZERO);
|
|
|
|
+ record.setSamePeriodAmount(lastYearRecord != null ? lastYearRecord.getAmount() : BigDecimal.ZERO);
|
|
|
|
+
|
|
|
|
+ // 计算环比
|
|
|
|
+ record.setMomRate(calculateProfitRate(record.getAmount(), record.getPrevPeriodAmount()));
|
|
|
|
+ // 计算同比
|
|
|
|
+ record.setYoyRate(calculateProfitRate(record.getAmount(), record.getSamePeriodAmount()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 批量更新数据库
|
|
|
|
+ this.updateBatchById(records);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取指定期间的数据
|
|
|
|
+ *
|
|
|
|
+ * @param hospId 院区ID
|
|
|
|
+ * @param shareType 报表类型
|
|
|
|
+ * @param year 年份
|
|
|
|
+ * @param month 月份
|
|
|
|
+ * @return 符合条件的数据列表
|
|
|
|
+ */
|
|
|
|
+ private List<CostDepartmentProfit> getPeriodRecords(Long hospId, String shareType, int year, int month) {
|
|
|
|
+ return this.baseMapper.selectList(new QueryWrapper<CostDepartmentProfit>().lambda()
|
|
|
|
+ .eq(CostDepartmentProfit::getHospId, hospId)
|
|
|
|
+ .eq(CostDepartmentProfit::getShareType, shareType)
|
|
|
|
+ .eq(CostDepartmentProfit::getYear, year)
|
|
|
|
+ .eq(CostDepartmentProfit::getMonth, month)
|
|
|
|
+ .eq(CostDepartmentProfit::getDeleteTime, 0));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 构建记录的唯一标识键
|
|
|
|
+ *
|
|
|
|
+ * @param record 记录对象
|
|
|
|
+ * @return 唯一标识键
|
|
|
|
+ */
|
|
|
|
+ private String buildRecordKey(CostDepartmentProfit record) {
|
|
|
|
+ return String.format("%d_%d_%s_%d",
|
|
|
|
+ record.getYear(), record.getMonth(), record.getResponsibilityCode(), record.getReportNum());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 安全地计算百分比:避免除零错误
|
|
|
|
+ *
|
|
|
|
+ * @param current 当前值
|
|
|
|
+ * @param base 基准值
|
|
|
|
+ * @return 计算结果,保留4位小数
|
|
|
|
+ */
|
|
|
|
+ private BigDecimal calculateProfitRate(BigDecimal current, BigDecimal base) {
|
|
|
|
+ if (base == null || base.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
|
+ return BigDecimal.ZERO;
|
|
|
|
+ }
|
|
|
|
+ // 先计算到4位小数,再四舍五入到4位
|
|
|
|
+ BigDecimal rate = current.divide(base, 4, RoundingMode.HALF_UP);
|
|
|
|
+ return rate.setScale(4, RoundingMode.HALF_UP);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
|
|
/**
|
|
/**
|
|
* 执行科室损益后续脚本
|
|
* 执行科室损益后续脚本
|