24 Commits 1dd3df452d ... a2716f4c3a

Author SHA1 Message Date
  JammeyJiang a2716f4c3a 添加按年月排序 1 month ago
  JammeyJiang aa70eb8cda 添加是否月累计及审计月的标志控制 1 month ago
  JammeyJiang 8fe0fc24e8 添加参数控制哪些损益报表需要用脚本计算 1 month ago
  JammeyJiang 2667250bb7 添加是否允许下钻字段出参 1 month ago
  JammeyJiang 7778ec45fd 添加全院损益多月份查询接口功能 1 month ago
  JammeyJiang 5264281e02 添加多月份全院损益数据查询接口代码 1 month ago
  JammeyJiang e8b8f80ab2 全院损益计算添加同环比计算 1 month ago
  JammeyJiang c271558d6d 同环比改成同环比增长率 1 month ago
  JammeyJiang 2008351c9d 1、添加月累计的数值计算及样式组装 2、多sheet页导入无法关闭文件流的问题处理 1 month ago
  JammeyJiang 43f77f8eb0 科室损益计算添加同环比计算逻辑 1 month ago
  JammeyJiang 689703e16c 科室损益计算添加同环比计算逻辑 1 month ago
  JammeyJiang a8f8ce540e 调整分摊算法的事务控制 1 month ago
  JammeyJiang ada478edc7 按会计科目分摊逻辑添加责任中心并行分摊逻辑 1 month ago
  JammeyJiang 47ac9b7778 添加一个Excel的多个sheet页导入功能 1 month ago
  JammeyJiang bb04d4185d 添加一个Excel的多个sheet页导入功能 1 month ago
  JammeyJiang 8d6ae26f61 添加获取年度科室损益数据相关接口 1 month ago
  JammeyJiang 1f0277af43 添加获取年度科室损益数据相关接口 1 month ago
  JammeyJiang e949206868 添加科室损益计算-报表数据不显示占比时的金额层处理 3 months ago
  JammeyJiang 2ec0b3c624 添加损益报表的数据格式控制 3 months ago
  JammeyJiang d63c6835b9 添加指定目标责任中心分摊的相关功能 4 months ago
  JammeyJiang 32e94bb1c1 添加按会计科目分摊相关代码 4 months ago
  JammeyJiang 041d1fb5d7 添加按会计科目相关代码 4 months ago
  JammeyJiang 8762a2f3a2 添加分摊设置改成主从形式相关代码 4 months ago
  JammeyJiang 1076c5c799 添加参数控制项目成本计算流程的计算方式 5 months ago
57 changed files with 3531 additions and 129 deletions
  1. 5 0
      src/main/java/com/kcim/common/constants/CommonConstant.java
  2. 23 0
      src/main/java/com/kcim/common/constants/ParameterConstant.java
  3. 133 0
      src/main/java/com/kcim/common/constants/SplitConstant.java
  4. 5 0
      src/main/java/com/kcim/dao/mapper/AllocationMapper.java
  5. 16 0
      src/main/java/com/kcim/dao/mapper/CostAccountShareDetailMapper.java
  6. 16 0
      src/main/java/com/kcim/dao/mapper/CostAccountShareParamMapper.java
  7. 16 0
      src/main/java/com/kcim/dao/mapper/CostAccountShareTargetMapper.java
  8. 15 0
      src/main/java/com/kcim/dao/mapper/CostDepartmentProfitMapper.java
  9. 14 5
      src/main/java/com/kcim/dao/model/Allocation.java
  10. 74 0
      src/main/java/com/kcim/dao/model/CostAccountShareDetail.java
  11. 85 0
      src/main/java/com/kcim/dao/model/CostAccountShareParam.java
  12. 74 0
      src/main/java/com/kcim/dao/model/CostAccountShareTarget.java
  13. 30 0
      src/main/java/com/kcim/dao/model/CostDepartmentProfit.java
  14. 30 0
      src/main/java/com/kcim/dao/model/HospProfitAndLoss.java
  15. 22 0
      src/main/java/com/kcim/dao/model/ReportForm.java
  16. 18 0
      src/main/java/com/kcim/dao/model/dto/ReportFormEditDTO.java
  17. 18 0
      src/main/java/com/kcim/dao/model/dto/ReportFormSaveDTO.java
  18. 78 0
      src/main/java/com/kcim/dao/repository/CostAccountShareDetailRepository.java
  19. 77 0
      src/main/java/com/kcim/dao/repository/CostAccountShareParamRepository.java
  20. 77 0
      src/main/java/com/kcim/dao/repository/CostAccountShareTargetRepository.java
  21. 7 0
      src/main/java/com/kcim/service/CenterService.java
  22. 15 0
      src/main/java/com/kcim/service/CostAccountShareDetailService.java
  23. 15 0
      src/main/java/com/kcim/service/CostAccountShareParamService.java
  24. 15 0
      src/main/java/com/kcim/service/CostAccountShareService.java
  25. 15 0
      src/main/java/com/kcim/service/CostAccountShareTargetService.java
  26. 2 0
      src/main/java/com/kcim/service/CostDepartmentProfitService.java
  27. 2 0
      src/main/java/com/kcim/service/HospProfitAndLossService.java
  28. 11 0
      src/main/java/com/kcim/service/KpiComputeImportService.java
  29. 503 28
      src/main/java/com/kcim/service/impl/AllocationServiceImpl.java
  30. 25 0
      src/main/java/com/kcim/service/impl/CenterServiceImpl.java
  31. 11 0
      src/main/java/com/kcim/service/impl/ComputeMedicalDepartmentProfitServiceImpl.java
  32. 14 0
      src/main/java/com/kcim/service/impl/CostAccountShareDetailServiceImpl.java
  33. 14 0
      src/main/java/com/kcim/service/impl/CostAccountShareParamServiceImpl.java
  34. 258 11
      src/main/java/com/kcim/service/impl/CostAccountShareServiceImpl.java
  35. 15 0
      src/main/java/com/kcim/service/impl/CostAccountShareTargetService.java
  36. 549 24
      src/main/java/com/kcim/service/impl/CostDepartmentProfitServiceImpl.java
  37. 591 18
      src/main/java/com/kcim/service/impl/HospProfitAndLossServiceImpl.java
  38. 228 9
      src/main/java/com/kcim/service/impl/KpiComputeImportServiceImpl.java
  39. 27 13
      src/main/java/com/kcim/service/impl/ProjectCostServiceImpl.java
  40. 7 0
      src/main/java/com/kcim/service/impl/ReportFormServiceImpl.java
  41. 27 13
      src/main/java/com/kcim/service/impl/ShareParamServiceImpl.java
  42. 14 0
      src/main/java/com/kcim/vo/CommonTitleReportVo.java
  43. 45 0
      src/main/java/com/kcim/vo/CostProfitVo.java
  44. 15 0
      src/main/java/com/kcim/vo/HospProfitVO.java
  45. 39 0
      src/main/java/com/kcim/vo/PeriodInfoVO.java
  46. 20 0
      src/main/java/com/kcim/vo/ReportFormVO.java
  47. 23 0
      src/main/java/com/kcim/vo/ShareTargetMapVo.java
  48. 19 0
      src/main/java/com/kcim/vo/SheetImportResultVO.java
  49. 25 1
      src/main/java/com/kcim/web/CostAccountShareController.java
  50. 11 0
      src/main/java/com/kcim/web/CostDepartmentProfitController.java
  51. 9 0
      src/main/java/com/kcim/web/HospProfitAndLossController.java
  52. 11 0
      src/main/java/com/kcim/web/PublicImportController.java
  53. 20 0
      src/main/java/com/kcim/web/reponse/BatchCostProfitResponse.java
  54. 5 5
      src/main/resources/application-dev.yml
  55. 37 0
      src/main/resources/mapper/AllocationMapper.xml
  56. 57 1
      src/main/resources/mapper/CostDepartmentProfitMapper.xml
  57. 4 1
      src/main/resources/mapper/CostReportFormMapper.xml

+ 5 - 0
src/main/java/com/kcim/common/constants/CommonConstant.java

@@ -91,6 +91,11 @@ public final class CommonConstant {
      */
     public static final String SEPARATOR_BlankSpace = " ";
 
+    /**
+     * 分割符-竖杆
+     */
+    public static final String SEPARATOR_VERTICAL = "\\|";
+
 
     /**
      * 常量A

+ 23 - 0
src/main/java/com/kcim/common/constants/ParameterConstant.java

@@ -42,5 +42,28 @@ public interface ParameterConstant {
      */
     Long USER_HOSP_ITEM_CALC=1889925898824716288L;
 
+    /**
+     * 项目成本计算方式
+     */
+    Long ITEM_CALC_TYPE=1902923430831984640L;
 
+    /**
+     * 成本分摊计算的分摊方式 1按责任中心分摊 2按会计科目分摊
+     */
+    Long ALLOCATION_TYPE=1907261486460440576L;
+
+    /**
+     * 科室及全院损益及对应的报表界面是否显示占比字段 0否 1是
+     */
+    Long SHOW_PERCENT=1851077044079824896L;
+
+    /**
+     * 科室损益同环比计算方式 1代码计算 2脚本计算
+     */
+    Long DEPT_YM_CALC_TYPE =1943243153054240768L;
+
+    /**
+     * 需要用脚本计算的科室损益及全院损益报表类型
+     */
+    Long SQL_CALC_REPORT_TYPE =1945667397503553536L;
 }

+ 133 - 0
src/main/java/com/kcim/common/constants/SplitConstant.java

@@ -0,0 +1,133 @@
+package com.kcim.common.constants;
+
+/**
+* @program: performace
+* @description: 分隔符常量
+* @author: Wang.YS
+* @create: 2023-01-08 21:55
+**/
+
+public interface SplitConstant {
+    /**
+     * 分割符-分号(半角)
+     */
+     String SEPARATOR_SEMICOLON = ";";
+
+    /**
+     * 分割符-右箭头
+     */
+     String RIGHT_ARROWS = "→";
+
+    /**
+     * 分割符-分号(全角)
+     */
+     String SEPARATOR_SEMICOLON_C = ";";
+
+    /**
+     * 分割符-冒号
+     */
+     String SEPARATOR_NUMBER = ":";
+
+    /**
+     * 分割符-冒号
+     */
+     String SEPARATOR_NUMBER_C = ":";
+
+    /**
+     *  分割符-下划线
+     */
+     String SEPARATOR_UNDERLINE = "_";
+    /**
+     * 分割符-竖线
+     */
+     String SEPARATOR_VERTICALLINE="\\|";
+
+    /**
+     * 分割符-逗号
+     */
+     String SEPARATOR_COMMA = ",";
+
+    /**
+     * 分割符-逗号
+     */
+     String SEPARATOR_COMMA_C = ",";
+
+
+    /**
+     * 分割符-反斜杠
+     */
+     String SEPARATOR_BACKSLASH = "/";
+
+    /**
+     * 分割符-反斜杠
+     */
+     String SEPARATOR_BACKSLASH_C = "、";
+
+    /**
+     * 分割符-横杠
+     */
+     String SEPARATOR_WHIPPTREE = "-";
+
+
+    /**
+     * 分割符-波浪号
+     */
+     String SEPARATOR_WAVE = "~";
+
+    /**
+     * 分割符-分号
+     */
+     String SEPARATOR_POINT = ".";
+
+    /**
+     * 分割符-分号
+     */
+     String SEPARATOR_POINT_C = "。";
+
+    /**
+     * 分割符-空格
+     */
+     String SEPARATOR_BlankSpace = " ";
+
+
+    /**
+     * 常量A
+     */
+     String SEPARATOR_A = "A";
+    /**
+     * 左括号
+     */
+     String LEFT_PARENTHESES = "(";
+    /**
+     * 左括号
+     */
+     String LEFT_PARENTHESES_C = "(";
+    /**
+     * 右括号
+     */
+     String RIGHT_PARENTHESES = ")";
+
+    /**
+     * 右括号
+     */
+     String RIGHT_PARENTHESES_C = ")";
+    /**
+     * 导管表默认时间
+     */
+     String ZERO_POINT = "0001-01-01 00:00:00";
+
+    /**
+     * 分隔符 - 乘号
+     */
+     String MULTIPLICATION_SIGN = "×";
+
+
+    /**
+     * 分隔符 - 加号
+     */
+
+     String MULTIPLICATION_PLUS = "\\+";
+
+     String PERCENT = "%";
+
+}

+ 5 - 0
src/main/java/com/kcim/dao/mapper/AllocationMapper.java

@@ -76,4 +76,9 @@ public interface AllocationMapper extends BaseMapper<Allocation> {
     List<Allocation> getAllSortLevel(@Param("hospId") Long hospId,
                                      @Param("year") int year,
                                      @Param("month") int month);
+
+    List<Allocation> getLevelSortAllocation(@Param("hospId") Long hospId,
+                                     @Param("year") int year,
+                                     @Param("month") int month,
+                                     @Param("shareLevelId") Long shareLevelId);
 }

+ 16 - 0
src/main/java/com/kcim/dao/mapper/CostAccountShareDetailMapper.java

@@ -0,0 +1,16 @@
+package com.kcim.dao.mapper;
+
+import com.kcim.dao.model.CostAccountShareDetail;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 成本分摊对应的会计科目
+ * 
+ * @author Wang.YS
+ * @date 2025-03-28 14:06:13
+ */
+@Mapper
+public interface CostAccountShareDetailMapper extends BaseMapper<CostAccountShareDetail> {
+	
+}

+ 16 - 0
src/main/java/com/kcim/dao/mapper/CostAccountShareParamMapper.java

@@ -0,0 +1,16 @@
+package com.kcim.dao.mapper;
+
+import com.kcim.dao.model.CostAccountShareParam;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 成本分摊对应的分摊参数
+ * 
+ * @author Wang.YS
+ * @date 2025-03-28 14:06:12
+ */
+@Mapper
+public interface CostAccountShareParamMapper extends BaseMapper<CostAccountShareParam> {
+	
+}

+ 16 - 0
src/main/java/com/kcim/dao/mapper/CostAccountShareTargetMapper.java

@@ -0,0 +1,16 @@
+package com.kcim.dao.mapper;
+
+import com.kcim.dao.model.CostAccountShareTarget;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 成本分摊对应的分摊目标责任中心
+ * 
+ * @author Wang.YS
+ * @date 2025-04-03 11:13:36
+ */
+@Mapper
+public interface CostAccountShareTargetMapper extends BaseMapper<CostAccountShareTarget> {
+	
+}

+ 15 - 0
src/main/java/com/kcim/dao/mapper/CostDepartmentProfitMapper.java

@@ -2,9 +2,12 @@ package com.kcim.dao.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.kcim.dao.model.CostDepartmentProfit;
+import com.kcim.vo.CostProfitVo;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.List;
+
 /**
  * 科室损益计算
  * 
@@ -18,4 +21,16 @@ public interface CostDepartmentProfitMapper extends BaseMapper<CostDepartmentPro
     Integer getMaxYear(@Param("hospId") Long hospId);
 
     Integer getMaxMonth(@Param("hospId") Long hospId, @Param("year") Integer year);
+
+
+    List<CostProfitVo> getAnnualSummaryByYearRange(@Param("startYear") Integer startYear,
+                                                @Param("endYear") Integer endYear,
+                                                @Param("hospId") Long hospId,
+                                                @Param("reportType") Integer reportType,
+                                                @Param("responsibilityCode") String responsibilityCode);
+
+    List<CostProfitVo> getAnnualSummaryByYear(@Param("year") Integer year,
+                                              @Param("hospId") Long hospId,
+                                              @Param("reportType") Integer reportType,
+                                              @Param("responsibilityCode") String responsibilityCode);
 }

+ 14 - 5
src/main/java/com/kcim/dao/model/Allocation.java

@@ -3,16 +3,14 @@ package com.kcim.dao.model;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
-
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
 
+import java.io.Serializable;
+import java.math.BigDecimal;
+
 /**
  * 成本分摊后表
  * 
@@ -94,6 +92,7 @@ public class Allocation implements Serializable {
 	/**
 	 * 分摊参数名称
 	 */
+
 	private String shareParamName;
 	/**
 	 * 分摊参数数值
@@ -112,6 +111,16 @@ public class Allocation implements Serializable {
 
 	private Long targetShareLevelId;
 
+	/**
+	 * 会计科目Code
+	 */
+	private String accountCode;
+
+	/**
+	 * 会计科目名称
+	 */
+	private String accountName;
+
 	/**
 	 * 
 	 */

+ 74 - 0
src/main/java/com/kcim/dao/model/CostAccountShareDetail.java

@@ -0,0 +1,74 @@
+package com.kcim.dao.model;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 成本分摊对应的会计科目
+ * 
+ * @author Wang.YS
+
+ * @date 2025-03-28 14:06:13
+ */
+@Data
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("cost_account_share_detail")
+public class CostAccountShareDetail implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 主键
+	 */
+	@TableId
+	private Long id;
+	/**
+	 * 医院id
+	 */
+	private Long hospId;
+	/**
+	 * 成本分摊参数对应表的ID
+	 */
+	private Long accountShareId;
+	/**
+	 * 会计科目代码
+	 */
+	private String accountingCode;
+	/**
+	 * 创建人
+	 */
+	private String createUser;
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+	/**
+	 * 更新人
+	 */
+	private String updateUser;
+	/**
+	 * 更新时间
+	 */
+	private Date updateTime;
+	/**
+	 * 删除人
+	 */
+	private String deleteUser;
+	/**
+	 * 删除时间
+	 */
+	private Date deleteTime;
+	/**
+	 * 删除标志  0正常 1作废
+	 */
+	private Integer delFlag;
+
+}

+ 85 - 0
src/main/java/com/kcim/dao/model/CostAccountShareParam.java

@@ -0,0 +1,85 @@
+package com.kcim.dao.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 成本分摊对应的分摊参数
+ * 
+ * @author Wang.YS
+
+ * @date 2025-03-28 14:06:12
+ */
+@Data
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("cost_account_share_param")
+public class CostAccountShareParam implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 主键
+	 */
+	@TableId
+	private Long id;
+	/**
+	 * 医院id
+	 */
+	private Long hospId;
+	/**
+	 * 成本分摊参数对应表的ID
+	 */
+	private Long accountShareId;
+	/**
+	 * 分摊参数代码
+	 */
+	private String shareParamCode;
+	/**
+	 * 分摊参数代码
+	 */
+	@TableField(exist = false)
+	private String shareParamName;
+	/**
+	 * 分摊参数占比
+	 */
+	private BigDecimal shareParamProportion;
+	/**
+	 * 创建人
+	 */
+	private String createUser;
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+	/**
+	 * 更新人
+	 */
+	private String updateUser;
+	/**
+	 * 更新时间
+	 */
+	private Date updateTime;
+	/**
+	 * 删除人
+	 */
+	private String deleteUser;
+	/**
+	 * 删除时间
+	 */
+	private Date deleteTime;
+	/**
+	 * 删除标志  0正常 1作废
+	 */
+	private Integer delFlag;
+
+}

+ 74 - 0
src/main/java/com/kcim/dao/model/CostAccountShareTarget.java

@@ -0,0 +1,74 @@
+package com.kcim.dao.model;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 成本分摊对应的分摊目标责任中心
+ * 
+ * @author Wang.YS
+
+ * @date 2025-04-03 11:13:36
+ */
+@Data
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("cost_account_share_target")
+public class CostAccountShareTarget implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 主键
+	 */
+	@TableId
+	private Long id;
+	/**
+	 * 医院id
+	 */
+	private Long hospId;
+	/**
+	 * 成本分摊参数对应表的ID
+	 */
+	private Long accountShareId;
+	/**
+	 * 目标责任中心代码
+	 */
+	private String targetResponsibilityCode;
+	/**
+	 * 创建人
+	 */
+	private String createUser;
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+	/**
+	 * 更新人
+	 */
+	private String updateUser;
+	/**
+	 * 更新时间
+	 */
+	private Date updateTime;
+	/**
+	 * 删除人
+	 */
+	private String deleteUser;
+	/**
+	 * 删除时间
+	 */
+	private Date deleteTime;
+	/**
+	 * 删除标志  0正常 1作废
+	 */
+	private Integer delFlag;
+
+}

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

@@ -115,5 +115,35 @@ public class CostDepartmentProfit implements Serializable {
 
 	private BigDecimal percent;
 
+	/**
+	 * 预算金额
+	 */
+	private BigDecimal budgetAmount;
+
+	/**
+	 * 上期金额
+	 */
+	private BigDecimal prevPeriodAmount;
+
+	/**
+	 * 同期金额
+	 */
+	private BigDecimal samePeriodAmount;
+
+	/**
+	 * 完成率
+	 */
+	private BigDecimal completionRate;
+
+	/**
+	 * 环比
+	 */
+	private BigDecimal momRate;
+
+	/**
+	 * 同比
+	 */
+	private BigDecimal yoyRate;
+
 
 }

+ 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;
 }

+ 22 - 0
src/main/java/com/kcim/dao/model/ReportForm.java

@@ -103,4 +103,26 @@ public class ReportForm implements Serializable {
      */
     private Integer hide;
 
+    /**
+     * 数据类型:0未知 1数值 2百分比
+     */
+    private Integer dataType;
+
+
+    /**
+     * 数据类型名称
+     */
+    @TableField(exist = false)
+    private String dataTypeName;
+
+    /**
+     * 小数位
+     */
+    private Integer decimalPlace;
+
+    /**
+     * 是否有千分号
+     */
+    private Integer permil;
+
 }

+ 18 - 0
src/main/java/com/kcim/dao/model/dto/ReportFormEditDTO.java

@@ -52,5 +52,23 @@ public class ReportFormEditDTO {
      */
     private Integer hide;
 
+    /**
+     * 数据类型:0未知 1数值 2百分比
+     */
+    private Integer dataType;
+
+    /**
+     * 数据类型名称
+     */
+    private String dataTypeName;
 
+    /**
+     * 小数位
+     */
+    private Integer decimalPlace;
+
+    /**
+     * 是否有千分号 0否 1是
+     */
+    private Integer permil;
 }

+ 18 - 0
src/main/java/com/kcim/dao/model/dto/ReportFormSaveDTO.java

@@ -48,5 +48,23 @@ public class ReportFormSaveDTO {
      */
     private Integer hide;
 
+    /**
+     * 数据类型:0未知 1数值 2百分比
+     */
+    private Integer dataType;
+
+    /**
+     * 数据类型名称
+     */
+    private String dataTypeName;
 
+    /**
+     * 小数位
+     */
+    private Integer decimalPlace;
+
+    /**
+     * 是否有千分号
+     */
+    private Integer permil;
 }

+ 78 - 0
src/main/java/com/kcim/dao/repository/CostAccountShareDetailRepository.java

@@ -0,0 +1,78 @@
+package com.kcim.dao.repository;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kcim.common.constants.NumberConstant;
+import com.kcim.common.util.UserContext;
+import com.kcim.dao.mapper.CostAccountShareDetailMapper;
+import com.kcim.dao.model.CostAccountShareDetail;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @program: CostAccount
+ * @description:
+ * @author: Wang.YS
+ * @create: 2024-04-03 14:49
+ **/
+@Repository
+public class CostAccountShareDetailRepository extends ServiceImpl<CostAccountShareDetailMapper, CostAccountShareDetail> {
+
+    /**
+     * 获取指定分摊设置对应的会计科目
+     * @param accountShareId
+     * @return
+     */
+    public List<CostAccountShareDetail> getCostAccountShareDetail(Long accountShareId) {
+        LambdaQueryWrapper<CostAccountShareDetail> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(CostAccountShareDetail::getHospId, UserContext.getHospId());
+        queryWrapper.eq(CostAccountShareDetail::getDelFlag, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)) {
+            queryWrapper.eq(CostAccountShareDetail::getAccountShareId, accountShareId);
+        }
+        return this.list(queryWrapper);
+    }
+
+    /**
+     * 作废指定分摊设置对应的会计科目
+     * @param accountShareId
+     * @return
+     */
+    public boolean delCostAccountShareDetail(Long accountShareId) {
+        LambdaUpdateWrapper<CostAccountShareDetail> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.set(CostAccountShareDetail::getDelFlag,NumberConstant.ONE);
+        updateWrapper.set(CostAccountShareDetail::getDeleteUser,UserContext.getCurrentUser().getId());
+        updateWrapper.set(CostAccountShareDetail::getDeleteTime,new Date());
+
+        updateWrapper.eq(CostAccountShareDetail::getHospId, UserContext.getHospId());
+        updateWrapper.eq(CostAccountShareDetail::getDelFlag, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)) {
+            updateWrapper.eq(CostAccountShareDetail::getAccountShareId, accountShareId);
+        }
+        return this.update(updateWrapper);
+    }
+
+    /**
+     * 批量删除指定分摊设置对应的会计科目
+     * @param accountShareIdList
+     * @return
+     */
+    public boolean delCostAccountShareDetailList(List<Long> accountShareIdList) {
+        LambdaUpdateWrapper<CostAccountShareDetail> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.set(CostAccountShareDetail::getDelFlag,NumberConstant.ONE);
+        updateWrapper.set(CostAccountShareDetail::getDeleteUser,UserContext.getCurrentUser().getId());
+        updateWrapper.set(CostAccountShareDetail::getDeleteTime,new Date());
+
+        updateWrapper.eq(CostAccountShareDetail::getHospId, UserContext.getHospId());
+        updateWrapper.eq(CostAccountShareDetail::getDelFlag, NumberConstant.ZERO);
+        if(!CollectionUtils.isEmpty(accountShareIdList)) {
+            updateWrapper.in(CostAccountShareDetail::getAccountShareId, accountShareIdList);
+        }
+        return this.update(updateWrapper);
+    }
+}

+ 77 - 0
src/main/java/com/kcim/dao/repository/CostAccountShareParamRepository.java

@@ -0,0 +1,77 @@
+package com.kcim.dao.repository;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kcim.common.constants.NumberConstant;
+import com.kcim.common.util.UserContext;
+import com.kcim.dao.mapper.CostAccountShareParamMapper;
+import com.kcim.dao.model.CostAccountShareParam;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @program: CostAccount
+ * @description:
+ * @author: Wang.YS
+ * @create: 2024-04-03 14:49
+ **/
+@Repository
+public class CostAccountShareParamRepository extends ServiceImpl<CostAccountShareParamMapper, CostAccountShareParam> {
+    /**
+     * 获取指定分摊设置对应的分摊参数
+     * @param accountShareId
+     * @return
+     */
+    public List<CostAccountShareParam> getCostAccountShareParam(Long accountShareId) {
+        LambdaQueryWrapper<CostAccountShareParam> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(CostAccountShareParam::getHospId, UserContext.getHospId());
+        queryWrapper.eq(CostAccountShareParam::getDelFlag, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)) {
+            queryWrapper.eq(CostAccountShareParam::getAccountShareId, accountShareId);
+        }
+        return this.list(queryWrapper);
+    }
+
+    /**
+     * 作废指定分摊设置对应的分摊参数
+     * @param accountShareId
+     * @return
+     */
+    public boolean delCostAccountParamDetail(Long accountShareId) {
+        LambdaUpdateWrapper<CostAccountShareParam> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.set(CostAccountShareParam::getDelFlag,NumberConstant.ONE);
+        updateWrapper.set(CostAccountShareParam::getDeleteUser,UserContext.getCurrentUser().getId());
+        updateWrapper.set(CostAccountShareParam::getDeleteTime,new Date());
+
+        updateWrapper.eq(CostAccountShareParam::getHospId, UserContext.getHospId());
+        updateWrapper.eq(CostAccountShareParam::getDelFlag, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)) {
+            updateWrapper.eq(CostAccountShareParam::getAccountShareId, accountShareId);
+        }
+        return this.update(updateWrapper);
+    }
+
+    /**
+     * 批量删除指定分摊设置对应的分摊参数
+     * @param accountShareIdList
+     * @return
+     */
+    public boolean delCostAccountShareParamList(List<Long> accountShareIdList) {
+        LambdaUpdateWrapper<CostAccountShareParam> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.set(CostAccountShareParam::getDelFlag,NumberConstant.ONE);
+        updateWrapper.set(CostAccountShareParam::getDeleteUser,UserContext.getCurrentUser().getId());
+        updateWrapper.set(CostAccountShareParam::getDeleteTime,new Date());
+
+        updateWrapper.eq(CostAccountShareParam::getHospId, UserContext.getHospId());
+        updateWrapper.eq(CostAccountShareParam::getDelFlag, NumberConstant.ZERO);
+        if(!CollectionUtils.isEmpty(accountShareIdList)) {
+            updateWrapper.in(CostAccountShareParam::getAccountShareId, accountShareIdList);
+        }
+        return this.update(updateWrapper);
+    }
+}

+ 77 - 0
src/main/java/com/kcim/dao/repository/CostAccountShareTargetRepository.java

@@ -0,0 +1,77 @@
+package com.kcim.dao.repository;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kcim.common.constants.NumberConstant;
+import com.kcim.common.util.UserContext;
+import com.kcim.dao.mapper.CostAccountShareTargetMapper;
+import com.kcim.dao.model.CostAccountShareTarget;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @program: CostAccount
+ * @description:
+ * @author: Wang.YS
+ * @create: 2024-04-03 14:49
+ **/
+@Repository
+public class CostAccountShareTargetRepository extends ServiceImpl<CostAccountShareTargetMapper, CostAccountShareTarget> {
+    /**
+     * 获取指定分摊设置对应的目标责任中心
+     * @param accountShareId
+     * @return
+     */
+    public List<CostAccountShareTarget> getCostAccountShareTarget(Long accountShareId) {
+        LambdaQueryWrapper<CostAccountShareTarget> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(CostAccountShareTarget::getHospId, UserContext.getHospId());
+        queryWrapper.eq(CostAccountShareTarget::getDelFlag, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)) {
+            queryWrapper.eq(CostAccountShareTarget::getAccountShareId, accountShareId);
+        }
+        return this.list(queryWrapper);
+    }
+
+    /**
+     * 作废指定分摊设置对应的目标责任中心
+     * @param accountShareId
+     * @return
+     */
+    public boolean delCostAccountShareTarget(Long accountShareId) {
+        LambdaUpdateWrapper<CostAccountShareTarget> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.set(CostAccountShareTarget::getDelFlag,NumberConstant.ONE);
+        updateWrapper.set(CostAccountShareTarget::getDeleteUser,UserContext.getCurrentUser().getId());
+        updateWrapper.set(CostAccountShareTarget::getDeleteTime,new Date());
+
+        updateWrapper.eq(CostAccountShareTarget::getHospId, UserContext.getHospId());
+        updateWrapper.eq(CostAccountShareTarget::getDelFlag, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)) {
+            updateWrapper.eq(CostAccountShareTarget::getAccountShareId, accountShareId);
+        }
+        return this.update(updateWrapper);
+    }
+
+    /**
+     * 批量删除指定分摊设置对应的目标责任中心
+     * @param accountShareIdList
+     * @return
+     */
+    public boolean delCostAccountShareTargetList(List<Long> accountShareIdList) {
+        LambdaUpdateWrapper<CostAccountShareTarget> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.set(CostAccountShareTarget::getDelFlag,NumberConstant.ONE);
+        updateWrapper.set(CostAccountShareTarget::getDeleteUser,UserContext.getCurrentUser().getId());
+        updateWrapper.set(CostAccountShareTarget::getDeleteTime,new Date());
+
+        updateWrapper.eq(CostAccountShareTarget::getHospId, UserContext.getHospId());
+        updateWrapper.eq(CostAccountShareTarget::getDelFlag, NumberConstant.ZERO);
+        if(!CollectionUtils.isEmpty(accountShareIdList)) {
+            updateWrapper.in(CostAccountShareTarget::getAccountShareId, accountShareIdList);
+        }
+        return this.update(updateWrapper);
+    }
+}

+ 7 - 0
src/main/java/com/kcim/service/CenterService.java

@@ -39,6 +39,13 @@ public interface CenterService {
      */
     String getParameterValue(Long parameterCode);
 
+    /**
+     * 获取指定Code的参数指定序号的数值,
+     * @param parameterCode
+     * @return
+     */
+    String getParameterValueByIndex(Long parameterCode,int index);
+
     List<KCClassVo>  getKCClass();
 
     List<SysDepartment>  getDepartmentByDepartmentIds(List<Long> departmentIds);

+ 15 - 0
src/main/java/com/kcim/service/CostAccountShareDetailService.java

@@ -0,0 +1,15 @@
+package com.kcim.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.kcim.dao.model.CostAccountShareDetail;
+
+/**
+ * 成本分摊对应的会计科目
+ *
+ * @author Wang.YS
+ * @date 2025-03-28 14:06:13
+ */
+public interface CostAccountShareDetailService extends IService<CostAccountShareDetail> {
+
+}
+

+ 15 - 0
src/main/java/com/kcim/service/CostAccountShareParamService.java

@@ -0,0 +1,15 @@
+package com.kcim.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.kcim.dao.model.CostAccountShareParam;
+
+/**
+ * 成本分摊对应的分摊参数
+ *
+ * @author Wang.YS
+ * @date 2025-03-28 14:06:12
+ */
+public interface CostAccountShareParamService extends IService<CostAccountShareParam> {
+
+}
+

+ 15 - 0
src/main/java/com/kcim/service/CostAccountShareService.java

@@ -10,6 +10,7 @@ import com.kcim.dao.model.dto.CostAccountShareSaveDto;
 import com.kcim.dao.model.dto.ShareParamEditDto;
 import com.kcim.vo.CostShareParamStatusVO;
 import com.kcim.vo.ShareParamProportionVO;
+import com.kcim.vo.ShareTargetMapVo;
 
 import java.util.List;
 
@@ -97,5 +98,19 @@ public interface CostAccountShareService extends IService<CostAccountShare> {
     void deleteByIds(List<Long> asList);
     List<CostAccountShare> getAll();
 
+    /**
+     * 同步分摊参数设置
+     * @param direction 1主表向明细表同步 2明细表向主表同步
+     * @param accountShareId
+     * @return
+     */
+    void synCostAccountShare(Integer direction, Long accountShareId);
+
+
+    /**
+     * 添加分摊设置对应的目标责任中心
+     * @param shareTargetMapVo
+     */
+    void applyAccountShareTarget(ShareTargetMapVo shareTargetMapVo);
 }
 

+ 15 - 0
src/main/java/com/kcim/service/CostAccountShareTargetService.java

@@ -0,0 +1,15 @@
+package com.kcim.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.kcim.dao.model.CostAccountShareTarget;
+
+/**
+ * 成本分摊对应的分摊目标责任中心
+ *
+ * @author Wang.YS
+ * @date 2025-04-03 11:13:36
+ */
+public interface CostAccountShareTargetService extends IService<CostAccountShareTarget> {
+
+}
+

+ 2 - 0
src/main/java/com/kcim/service/CostDepartmentProfitService.java

@@ -71,5 +71,7 @@ public interface CostDepartmentProfitService extends IService<CostDepartmentProf
     Object getRedirectData(String computeDate, Long reportId, Long hospId, String reportType, String responsibilityCode);
 
     Object getComputeProfitCollect(String computeDate, Long hospId, String reportType);
+
+    Object getBatchComputeProfitListByYear(String startYear, String endYear, String reportType, String responsibilityCode);
 }
 

+ 2 - 0
src/main/java/com/kcim/service/HospProfitAndLossService.java

@@ -60,5 +60,7 @@ public interface HospProfitAndLossService extends IService<HospProfitAndLoss> {
     Object getHospProfitResponsibilities(String responsibilityName);
 
     Object getHospProfitList(String computeDate, Long hospId, String reportType);
+
+    Object getBatchHospProfitList(String beginComputeDate, String endComputeDate, Long hospId, Integer reportType);
 }
 

+ 11 - 0
src/main/java/com/kcim/service/KpiComputeImportService.java

@@ -1,7 +1,10 @@
 package com.kcim.service;
 
+import com.kcim.vo.SheetImportResultVO;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.util.List;
+
 /**
  * @program: CostAccount
  * @description:
@@ -41,4 +44,12 @@ public interface KpiComputeImportService {
      * @param id 记录id
      */
     void recoverImport(Integer id);
+
+    /**
+     * 批量导入数据
+     * @param computeDate 核算年月
+     * @param file 导入文件
+     * @return 导入结果列表
+     */
+    List<SheetImportResultVO> importMultipleSheets(String computeDate, MultipartFile file);
 }

+ 503 - 28
src/main/java/com/kcim/service/impl/AllocationServiceImpl.java

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.kcim.common.constants.NumberConstant;
+import com.kcim.common.constants.ParameterConstant;
 import com.kcim.common.constants.SQLParameter;
 import com.kcim.common.enums.CustomSqlTypeEnum;
 import com.kcim.common.exception.CostException;
@@ -19,9 +20,13 @@ import com.kcim.common.util.UserContext;
 import com.kcim.dao.mapper.AllocationMapper;
 import com.kcim.dao.model.*;
 import com.kcim.dao.model.dto.StartDTO;
+import com.kcim.dao.repository.CostAccountShareDetailRepository;
+import com.kcim.dao.repository.CostAccountShareParamRepository;
+import com.kcim.dao.repository.CostAccountShareTargetRepository;
 import com.kcim.dao.repository.ResponsibilityRepository;
 import com.kcim.service.*;
 import com.kcim.vo.*;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.springframework.stereotype.Service;
@@ -36,6 +41,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 @Slf4j
+@AllArgsConstructor
 @Service("allocationService")
 public class AllocationServiceImpl extends ServiceImpl<AllocationMapper, Allocation> implements AllocationService {
     //    @Value("${file.serverPath}")
@@ -55,18 +61,22 @@ public class AllocationServiceImpl extends ServiceImpl<AllocationMapper, Allocat
 
     private final SqlService sqlService;
 
-    public AllocationServiceImpl(CostCostingGroupService costCostingGroupService, CostShareLevelService shareLevelService, ResponsibilityService responsibilityService, CostAccountShareService accountShareService, ShareParamValueService shareParamValueService, CostShareParamService shareParamService, AllocationQueryService allocationQueryService, ResponsibilityRepository responsibilityRepository, CenterService centerService, SqlService sqlService) {
-        this.costCostingGroupService = costCostingGroupService;
-        this.shareLevelService = shareLevelService;
-        this.responsibilityService = responsibilityService;
-        this.accountShareService = accountShareService;
-        this.shareParamValueService = shareParamValueService;
-        this.shareParamService = shareParamService;
-        this.allocationQueryService = allocationQueryService;
-        this.responsibilityRepository = responsibilityRepository;
-        this.centerService = centerService;
-        this.sqlService = sqlService;
-    }
+    private final CostAccountShareDetailRepository costAccountShareDetailRepository;
+    private final CostAccountShareParamRepository costAccountShareParamRepository;
+    private final CostAccountShareTargetRepository costAccountShareTargetRepository;
+
+//    public AllocationServiceImpl(CostCostingGroupService costCostingGroupService, CostShareLevelService shareLevelService, ResponsibilityService responsibilityService, CostAccountShareService accountShareService, ShareParamValueService shareParamValueService, CostShareParamService shareParamService, AllocationQueryService allocationQueryService, ResponsibilityRepository responsibilityRepository, CenterService centerService, SqlService sqlService) {
+//        this.costCostingGroupService = costCostingGroupService;
+//        this.shareLevelService = shareLevelService;
+//        this.responsibilityService = responsibilityService;
+//        this.accountShareService = accountShareService;
+//        this.shareParamValueService = shareParamValueService;
+//        this.shareParamService = shareParamService;
+//        this.allocationQueryService = allocationQueryService;
+//        this.responsibilityRepository = responsibilityRepository;
+//        this.centerService = centerService;
+//        this.sqlService = sqlService;
+//    }
 
     /**
      * 分摊成本数据
@@ -75,8 +85,481 @@ public class AllocationServiceImpl extends ServiceImpl<AllocationMapper, Allocat
      * @param hospId   医院id
      */
     @Override
-    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
     public void startAllocation(StartDTO startDTO, Long hospId) {
+        //获取分摊方式
+        String parameterValue = centerService.getParameterValue(ParameterConstant.ALLOCATION_TYPE);
+        if (NumberConstant.ONE_S.equals(parameterValue)) {
+            //开始按责任中心分摊成本
+            startCostAllocation(startDTO, hospId);
+        }
+        else if (NumberConstant.TWO_S.equals(parameterValue)) {
+            //按会计科目分摊
+            startAccountAllocation(startDTO, hospId);
+        }
+        //执行分摊后续处理脚本
+        execAllocationSQL(startDTO);
+    }
+
+    /**
+     * 按会计科目分摊
+     * @param startDTO
+     */
+    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
+    public void startAccountAllocation(StartDTO startDTO, Long hospId){
+        long timeMillis = System.currentTimeMillis();
+        // 得到这个月的所有导入的成本数据
+        List<CostCostingGroup> costingGroups = costCostingGroupService.getByYearAndDate(startDTO.getYear(), startDTO.getMonth(), hospId);
+        // 没有重新导入
+        if (costingGroups.isEmpty()) {
+            throw new CostException("本月分摊数据未导入");
+        }
+        // 导入数据按责任中心归类
+        Map<String, List<CostCostingGroup>> respDirectCostMap = costingGroups.stream().collect(Collectors.groupingBy(CostCostingGroup::getResponsibilityCode));
+        // 得到这个月导入的成本分摊参数值列表
+        List<ShareParamValue> respParamValueList = shareParamValueService.getListByYearAndMonth(startDTO.getYear(), startDTO.getMonth(), hospId);
+        if (respParamValueList.isEmpty()) {
+            throw new CostException("本月成本分摊参数值未导入");
+        }
+        // 得到这个医院所有的分摊层级列表排序
+        List<CostShareLevelVO> shareLevelVOs = shareLevelService.getAll(hospId);
+        if (CollUtil.isEmpty(shareLevelVOs)) {
+            throw new CostException("请先设置医院分摊层级");
+        }
+        //优化循环调用  责任中心  分摊层级对应
+        //成本分摊字典
+        List<CostShareParamVO> costShareParamList = shareParamService.getAll(hospId);
+        if (CollUtil.isEmpty(costShareParamList)) {
+            throw new CostException("请先设置分摊参数字典");
+        }
+        Map<String,String> ShareParamNameMap = costShareParamList.stream().collect(Collectors.toMap(CostShareParamVO::getShareParamCode, CostShareParamVO::getShareParamName, (a, b) -> b));
+
+        //责任中心
+        List<Responsibility> responsibilityList = responsibilityRepository.getList(hospId);
+        if (CollUtil.isEmpty(responsibilityList)) {
+            throw new CostException("请先设置责任中心");
+        }
+        //按shareId分组
+        Map<Long, List<Responsibility>> responsibilityShareIdMap = responsibilityList.stream().collect(Collectors.groupingBy(Responsibility::getShareId));
+        //按责任中心代码分组
+        Map<String, List<Responsibility>> responsibilityCodeMap = responsibilityList.stream().collect(Collectors.groupingBy(Responsibility::getResponsibilityCode));
+        //分摊参数对应
+        List<CostAccountShare> accountShareList = accountShareService.getAll();
+        if (CollUtil.isEmpty(accountShareList)) {
+            throw new CostException("请先设置责任中心分摊参数对应");
+        }
+        //责任中心的分摊参数对应设置
+        Map<String, List<CostAccountShare>> accountShareResponsibilityMap = accountShareList.stream().collect(Collectors.groupingBy(CostAccountShare::getResponsibilityCode));
+        //分摊设置对应的分摊参数明细
+        List<CostAccountShareParam> costAccountShareParamList = costAccountShareParamRepository.getCostAccountShareParam(null);
+        if (CollUtil.isEmpty(costAccountShareParamList)) {
+            throw new CostException("请先设置责任中心的分摊参数");
+        }
+        //设置分摊参数名称
+        costAccountShareParamList.forEach(shareParam->shareParam.setShareParamName(ShareParamNameMap.get(shareParam.getShareParamCode())));
+        //按分摊设置ID分组
+        Map<Long, List<CostAccountShareParam>> costAccountShareParamMap = costAccountShareParamList.stream().collect(Collectors.groupingBy(CostAccountShareParam::getAccountShareId));
+        //分摊设置对应的会计科目明细
+        List<CostAccountShareDetail> costAccountShareDetailList = costAccountShareDetailRepository.getCostAccountShareDetail(null);
+        Map<Long, List<CostAccountShareDetail>> costAccountShareDetailMap =new HashMap<>();
+        //全部都是合并计算时会有会计科目明细为空的情况
+        if(!CollectionUtils.isEmpty(costAccountShareDetailList)) {
+            costAccountShareDetailMap=costAccountShareDetailList.stream().collect(Collectors.groupingBy(CostAccountShareDetail::getAccountShareId));
+        }
+
+        //分摊设置对应的目标责任中心
+        List<CostAccountShareTarget> costAccountShareTargetList = costAccountShareTargetRepository.getCostAccountShareTarget(null);
+        Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap =new HashMap<>();
+        //全部没有指定分摊时会有目标责任中心为空的情况
+        if(!CollectionUtils.isEmpty(costAccountShareTargetList)) {
+            costAccountShareTargetMap=costAccountShareTargetList.stream().collect(Collectors.groupingBy(CostAccountShareTarget::getAccountShareId));
+        }
+        //分摊成本列表
+        List<Allocation> allocationCostList=new ArrayList<>();
+        //逐级分摊
+        for (CostShareLevelVO shareLevelVO : shareLevelVOs) {
+            List<Allocation> shareLevelAllocationList = allocationShareLevelCost(startDTO, shareLevelVO, responsibilityList, responsibilityShareIdMap, respDirectCostMap, respParamValueList,
+                    accountShareResponsibilityMap, costAccountShareParamMap, costAccountShareDetailMap, costAccountShareTargetMap,allocationCostList, timeMillis);
+            allocationCostList.addAll(shareLevelAllocationList);
+        }
+        //没有任何分摊数据
+        if(CollectionUtils.isEmpty(allocationCostList)){
+            log.error("没有任何分摊数据......");
+            return;
+        }
+        // 删除该年月已经分摊过的数据
+        removeData(startDTO, hospId);
+        //保存分摊结果信息
+        this.saveBatch(allocationCostList);
+        //保存分摊结果查询表
+        applyAllocationQuery(startDTO,respDirectCostMap,allocationCostList,responsibilityCodeMap);
+    }
+
+    /**
+     * 一个分摊层级的分摊计算
+     * @param startDTO 分摊年月所在对象
+     * @param shareLevelVO 分摊层级
+     * @param responsibilityList 责任中心字典
+     * @param responsibilityShareIdMap 各分摊层级的责任中心
+     * @param respDirectCostMap 直接成本
+     * @param respParamValueList 分摊参数数值
+     * @param accountShareResponsibilityMap 分摊参数对应
+     * @param costAccountShareParamMap 分摊设置对应的分摊参数
+     * @param costAccountShareDetailMap 分摊设置对应的会计科目
+     * @param allocationCostList 间接成本
+     * @param timeMillis 时间戳
+     * @return
+     */
+    public List<Allocation> allocationShareLevelCost(StartDTO startDTO, CostShareLevelVO shareLevelVO,
+                                                     List<Responsibility> responsibilityList,
+                                                     Map<Long, List<Responsibility>> responsibilityShareIdMap,
+                                                     Map<String, List<CostCostingGroup>> respDirectCostMap ,
+                                                     List<ShareParamValue> respParamValueList,
+                                                     Map<String,List<CostAccountShare>> accountShareResponsibilityMap,
+                                                     Map<Long, List<CostAccountShareParam>> costAccountShareParamMap,
+                                                     Map<Long, List<CostAccountShareDetail>> costAccountShareDetailMap,
+                                                     Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap,
+                                                     List<Allocation> allocationCostList,
+                                                     long timeMillis){
+        //分摊成本列表
+        List<Allocation> allocationList=new ArrayList<>();
+        // 分摊层级id
+        Long levelId = shareLevelVO.getId();
+        // 目标分摊层级,可能不存在
+        String targetLevel = shareLevelVO.getTargetLevel();
+        if (StrUtil.isBlank(targetLevel)) {
+            throw new CostException("未设置目标层级");
+        }
+        //目标分摊层级
+        List<String> targetLevelList = Arrays.stream(targetLevel.split(StrUtil.COMMA)).collect(Collectors.toList());
+        //目标层级是自己的不需要做分摊处理
+        if(NumberConstant.ONE.equals(targetLevelList.size())&&Integer.valueOf(targetLevelList.get(NumberConstant.ZERO)).equals(shareLevelVO.getLeverSort())){
+            return allocationList;
+        }
+        // 得到该分摊层级下责任中心列表,如果不存在,下一个
+        List<Responsibility> responsibilities = responsibilityShareIdMap.get(levelId);
+        if (CollectionUtils.isEmpty(responsibilities)) {
+            return allocationList;
+        }
+
+        //目标责任中心列表
+        List<Responsibility> targetResponsibilityList = responsibilityList.stream().filter(responsibility -> targetLevelList.contains(String.valueOf(responsibility.getShareLevel()))).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(targetResponsibilityList)) {
+            throw new CostException(String.format("[%s-%s]的分摊目标层级没有对应的责任中心",shareLevelVO.getLeverSort(),shareLevelVO.getShareName()));
+        }
+
+        // 按责任中心逐个开始分摊(并行)
+        allocationList = responsibilities.parallelStream().flatMap(responsibility ->  allocationResponsibilityCost(startDTO, shareLevelVO, responsibility, targetResponsibilityList, respDirectCostMap, respParamValueList,
+                    accountShareResponsibilityMap, costAccountShareParamMap, costAccountShareDetailMap,costAccountShareTargetMap, allocationCostList, timeMillis).stream()).collect(Collectors.toList());
+        return allocationList;
+    }
+
+
+    /**
+     * 分摊一个责任中心的成本
+     * @param startDTO 分摊年月所在对象
+     * @param shareLevelVO 分摊层级
+     * @param responsibility 责任中心
+     * @param respDirectCostMap 直接成本
+     * @param respParamValueList 分摊参数数值
+     * @param accountShareResponsibilityMap 分摊参数对应
+     * @param costAccountShareParamMap 分摊设置对应的分摊参数
+     * @param costAccountShareDetailMap 分摊设置对应的会计科目
+     * @param allocationCostList 间接成本
+     * @param timeMillis 时间戳
+     */
+    public List<Allocation> allocationResponsibilityCost(StartDTO startDTO,
+                                             CostShareLevelVO shareLevelVO,
+                                             Responsibility responsibility,
+                                             List<Responsibility> targetResponsibilityList,
+                                             Map<String, List<CostCostingGroup>> respDirectCostMap ,
+                                             List<ShareParamValue> respParamValueList,
+                                             Map<String,List<CostAccountShare>> accountShareResponsibilityMap,
+                                             Map<Long, List<CostAccountShareParam>> costAccountShareParamMap,
+                                             Map<Long, List<CostAccountShareDetail>> costAccountShareDetailMap,
+                                             Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap,
+                                             List<Allocation> allocationCostList,
+                                             long timeMillis){
+        //分摊结果列表
+        List<Allocation> allocationList=new ArrayList<>();
+        // 得到分摊参数对应记录,不存在,下一个
+        List<CostAccountShare> accountShares = accountShareResponsibilityMap.get(responsibility.getResponsibilityCode());
+        if (CollectionUtils.isEmpty(accountShares)) {
+            return allocationList;
+        }
+        // 计算方式 0是合并计算  1是分开计算
+        Integer calcType = shareLevelVO.getCalcType();
+        //合并计算时不应该有多个分摊设置
+        if(NumberConstant.ZERO.equals(calcType) &&NumberConstant.ONE!=accountShares.size()){
+            throw new CostException(String.format("[%s-%s]的分摊设置有问题,合并计算的责任中心只允许设置一个分摊配置",responsibility.getResponsibilityCode(),responsibility.getResponsibilityName()));
+        }
+        //间接成本
+        List<Allocation> respIndirectCostList = allocationCostList.stream().filter(allocationCost -> allocationCost.getTargetResponsibilityCode().equals(responsibility.getResponsibilityCode())).collect(Collectors.toList());
+        //直接成本
+        List<CostCostingGroup> respDirectCostList = respDirectCostMap.get(responsibility.getResponsibilityCode());
+        //按分摊设置逐个计算
+        for (CostAccountShare accountShare : accountShares) {
+            //分摊设置对应的直接成本
+            List<CostCostingGroup> paramDirectCostList=new ArrayList<>();
+            //分摊设置对应的间接成本
+            List<Allocation> paramIndirectCostList=new ArrayList<>();
+            //获取分摊设置对应的目标责任中心
+            List<Responsibility> shareTargetRespCodeList = getShareTargetResponsibility(accountShare, targetResponsibilityList, costAccountShareTargetMap);
+            //所有目标责任中心的责任中心代码列表
+            List<String> targetResponsibilityCodeList = shareTargetRespCodeList.stream().map(Responsibility::getResponsibilityCode).collect(Collectors.toList());
+            //合并计算
+            if(NumberConstant.ZERO.equals(calcType)){
+                //合并计算时分摊参数的成本=责任中心的成本
+                paramDirectCostList=respDirectCostList;
+                paramIndirectCostList=respIndirectCostList;
+            }else{
+                //分摊设置对应的会计科目
+                List<String> accountList = costAccountShareDetailMap.get(accountShare.getId()).stream().map(CostAccountShareDetail::getAccountingCode).collect(Collectors.toList());
+                if(!CollectionUtils.isEmpty(respDirectCostList)) {
+                    //获取分摊设置对应的直接成本
+                    paramDirectCostList = respDirectCostList.stream().filter(respDirectCost -> accountList.contains(respDirectCost.getAccountCode())).collect(Collectors.toList());
+                }
+                //只有包含分摊成本的才有间接成本
+                if(NumberConstant.ONE.equals(accountShare.getIsShareCost())){
+                    paramIndirectCostList=respIndirectCostList;
+                }
+            }
+            //分摊设置对应的分摊参数
+            List<CostAccountShareParam> costAccountShareParams = costAccountShareParamMap.get(accountShare.getId());
+            //按分摊参数逐个计算所有目标责任中心的分摊金额
+            for (CostAccountShareParam shareParam : costAccountShareParams) {
+                //获取所有目标责任中心的分摊参数数值信息
+                List<ShareParamValue> targetResponsibilityParamList = respParamValueList.stream().filter(respParamValue -> shareParam.getShareParamCode().equals(respParamValue.getShareParamCode())
+                        && targetResponsibilityCodeList.contains(respParamValue.getResponsibilityCode())).collect(Collectors.toList());
+                if(CollectionUtils.isEmpty(targetResponsibilityParamList)){
+                    throw new CostException(String.format("[%s-%s]的目标责任中心找不到对应的参数数值,无法继续分摊",responsibility.getResponsibilityName(),shareParam.getShareParamName()));
+                }
+                //按分摊参数计算每个会计科目分摊到的金额
+                List<Allocation> allocationParamCosts = allocationParamCostCalc(startDTO, shareLevelVO, responsibility, shareTargetRespCodeList, paramDirectCostList,
+                        paramIndirectCostList, accountShares.get(NumberConstant.ZERO), shareParam, targetResponsibilityParamList, timeMillis);
+                allocationList.addAll(allocationParamCosts);
+            }
+        }
+        return allocationList;
+    }
+
+    /**
+     * 获取分摊设置的目标责任中心
+     * @param accountShare
+     * @param levelTargetResponsibility
+     * @param costAccountShareTargetMap
+     * @return
+     */
+    public List<Responsibility> getShareTargetResponsibility(CostAccountShare accountShare,
+                                                             List<Responsibility> levelTargetResponsibility,
+                                                             Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap){
+        //获取分摊设置对应的目标责任中心
+        List<CostAccountShareTarget> shareTargetResponsibilityList=costAccountShareTargetMap.get(accountShare.getId());
+        if(CollectionUtils.isEmpty(shareTargetResponsibilityList)){
+            return levelTargetResponsibility;
+        }
+        //筛选出指定的目标责任中心
+        List<String> shareTargetRespCodeList = shareTargetResponsibilityList.stream().map(CostAccountShareTarget::getTargetResponsibilityCode).collect(Collectors.toList());
+        List<Responsibility> shareTargetRespList = levelTargetResponsibility.stream().filter(responsibility -> shareTargetRespCodeList.contains(responsibility.getResponsibilityCode())).collect(Collectors.toList());
+        if(CollectionUtils.isEmpty(shareTargetRespList)){
+            throw new CostException(String.format("[%s-%s]找不到对应的目标责任中心,无法继续分摊",accountShare.getResponsibilityName(),accountShare.getAlias()));
+        }
+        return shareTargetRespList;
+    }
+
+
+    /**
+     * 按分摊参数计算每个会计科目分摊到的金额
+     * @param startDTO 分摊年月所在对象
+     * @param shareLevelVO 分摊层级
+     * @param responsibility 责任中心
+     * @param targetResponsibilityList 目标责任中心列表
+     * @param respDirectCostList 直接成本
+     * @param respIndirectCostList 间接成本
+     * @param costAccountShare 分摊参数对应
+     * @param shareParam 分摊参数
+     * @param targetResponsibilityParamList 目标责任中心的分摊参数数值列表
+     * @param timeMillis 时间戳
+     * @return
+     */
+    public List<Allocation> allocationParamCostCalc(StartDTO startDTO,
+                                               CostShareLevelVO shareLevelVO,
+                                               Responsibility responsibility,
+                                               List<Responsibility> targetResponsibilityList,
+                                               List<CostCostingGroup> respDirectCostList,
+                                               List<Allocation> respIndirectCostList,
+                                               CostAccountShare costAccountShare,
+                                               CostAccountShareParam shareParam,
+                                               List<ShareParamValue> targetResponsibilityParamList,
+                                               long timeMillis){
+        //重新定义一个列表用于合并直接成本和间接成本
+        List<CostCostingGroup> respCostList=new ArrayList<>();
+        //添加直接成本
+        if(!CollectionUtils.isEmpty(respDirectCostList)){
+            respCostList.addAll(respDirectCostList);
+        }
+        //添加间接成本
+        if(!CollectionUtils.isEmpty(respIndirectCostList)){
+            //转成直接成本的对象
+            List<CostCostingGroup> respIndirectCosts = BeanUtil.convertList(respIndirectCostList, CostCostingGroup.class);
+            respCostList.addAll(respIndirectCosts);
+        }
+        //计算所有目标责任中心的分摊参数加总
+        BigDecimal totalParamValue= targetResponsibilityParamList.stream().map(ShareParamValue::getValueNum).reduce(BigDecimal.ZERO, BigDecimal::add);
+        if (NumberConstant.ZERO.equals(totalParamValue.compareTo(BigDecimal.ZERO))){
+            throw new CostException(String.format("[%s-%s]的目标责任中心参数数值加总为0,无法继续分摊",responsibility.getResponsibilityName(),shareParam.getShareParamName()));
+        }
+        //按责任中心分组,方便取到对应责任中心的分摊参数数值
+        Map<String, List<ShareParamValue>> targetRespParamGroup = targetResponsibilityParamList.stream().collect(Collectors.groupingBy(ShareParamValue::getResponsibilityCode));
+        //按会计科目分组,相同会计科目一起分摊
+        Map<String, List<CostCostingGroup>> respDirectCostGroup = respCostList.stream().collect(Collectors.groupingBy(CostCostingGroup::getAccountCode));
+        //分摊结果列表
+        List<Allocation> allocationList=new ArrayList<>();
+        // 按会计科目进行分摊计算
+        respDirectCostGroup.forEach((accountCode, costCostingGroups) -> {
+            //会计科目总金额
+            BigDecimal totalAmount = costCostingGroups.stream().map(CostCostingGroup::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+            //没有金额的项目不需要做分摊处理
+            if (NumberConstant.ZERO.equals(totalAmount.compareTo(BigDecimal.ZERO))){
+                return;
+            }
+            for (Responsibility targetResponsibility : targetResponsibilityList) {
+                List<ShareParamValue> targetRespParamValues = targetRespParamGroup.get(targetResponsibility.getResponsibilityCode());
+                //目标责任中心的分摊参数数值
+                BigDecimal targetRespParamValue = BigDecimal.ZERO;
+                if(!CollectionUtils.isEmpty(targetRespParamValues)) {
+                    targetRespParamValue = targetRespParamValues.stream().map(ShareParamValue::getValueNum).reduce(BigDecimal.ZERO, BigDecimal::add);
+                }
+                //计算分摊参数比例
+                BigDecimal shareParamRate =  targetRespParamValue.divide(totalParamValue, NumberConstant.FOUR, RoundingMode.HALF_UP);
+                //参数占比
+                BigDecimal paramRate = shareParam.getShareParamProportion().divide(new BigDecimal(NumberConstant.ONE_HUNDRED), NumberConstant.FOUR, RoundingMode.HALF_UP);
+                //计算分摊到的金额=目标责任中心的分摊参数数值*会计科目总金额/所有目标责任中心的分摊参数数值加总*参数比例
+                BigDecimal targetAmount = targetRespParamValue.multiply(totalAmount).multiply(paramRate).divide(totalParamValue, NumberConstant.FOUR, RoundingMode.HALF_UP);
+                //生成分摊结果的对象
+                Allocation targetAllocation = new Allocation();
+                targetAllocation.setDateMonth(startDTO.getMonth()).setDateYear(startDTO.getYear()).setLevelSort(shareLevelVO.getLeverSort())
+                        .setLevelName(shareLevelVO.getShareName()).setHospId(UserContext.getHospId()).setResponsibilityCode(responsibility.getResponsibilityCode())
+                        .setResponsibilityName(responsibility.getResponsibilityName()).setAccountShareId(costAccountShare.getId()).setAmount(targetAmount)
+                        .setCreateTime(timeMillis).setTargetResponsibilityCode(targetResponsibility.getResponsibilityCode()).setTargetResponsibilityName(targetResponsibility.getResponsibilityName())
+                        .setShareParamCode(shareParam.getShareParamCode()).setShareParamName(shareParam.getShareParamName()).setTotalAmount(totalAmount).setShareParamValueNum(targetRespParamValue)
+                        .setShareParamRate(shareParamRate).setShareLevelId(responsibility.getShareId()).setTargetShareLevelId(targetResponsibility.getShareId())
+                        .setAccountCode(accountCode).setAccountName(costCostingGroups.get(NumberConstant.ZERO).getAccountName());
+                allocationList.add(targetAllocation);
+            }
+        });
+        return allocationList;
+    }
+
+    /**
+     * 保存分摊结果查询表
+     * @param startDTO 分摊年月所在对象
+     * @param respDirectCostMap 责任中心的直接成本
+     * @param allocationCostList 所有责任中心的间接成本
+     * @param responsibilityCodeMap 责任中心字典
+     */
+    public void applyAllocationQuery(StartDTO startDTO, Map<String, List<CostCostingGroup>> respDirectCostMap ,List<Allocation> allocationCostList,Map<String, List<Responsibility>> responsibilityCodeMap){
+        List<AllocationQuery> allocationQueryList=new ArrayList<>();
+        //处理直接成本,直接成本可能是多次导入的没有按会计科目汇总的,需按责任中心+会计科目汇总保存
+        respDirectCostMap.forEach((respCode,respCostList)->{
+            List<AllocationQuery> directAllocationQueryList = createDirectAllocationQuery(startDTO, respCode, respCostList, responsibilityCodeMap);
+            allocationQueryList.addAll(directAllocationQueryList);
+        });
+        //处理直接成本,间接成本肯定是按会计科目汇总的,可直接生成对象
+        for (Allocation indirectCost : allocationCostList) {
+            AllocationQuery indirectAllocationQuery = createIndirectAllocationQuery(startDTO, indirectCost);
+            allocationQueryList.add(indirectAllocationQuery);
+        }
+        if(CollectionUtils.isEmpty(allocationQueryList)){
+            log.error("没有任何分摊结果数据......");
+            return;
+        }
+        //保存分摊结果查询对象信息
+        allocationQueryService.saveBatch(allocationQueryList);
+    }
+
+    /**
+     * 创建直接成本的分摊结果查询对象
+     * @param startDTO 分摊年月所在对象
+     * @param respCode 责任中心代码
+     * @param respCostList 责任中心的直接成本数据
+     * @param responsibilityCodeMap 责任中心字典
+     * @return
+     */
+    public List<AllocationQuery> createDirectAllocationQuery(StartDTO startDTO,String respCode,List<CostCostingGroup> respCostList, Map<String, List<Responsibility>> responsibilityCodeMap){
+        List<AllocationQuery> allocationQueryList=new ArrayList<>();
+        if(!responsibilityCodeMap.containsKey(respCode)){
+            log.error(String.format("[%s-%s]的责任中心数据已过期",respCode,respCostList.get(NumberConstant.ZERO).getResponsibilityName()));
+            return allocationQueryList;
+        }
+        //责任中心数据
+        Responsibility responsibility=responsibilityCodeMap.get(respCode).get(NumberConstant.ZERO);
+        //按会计科目代码分组
+        Map<String, List<CostCostingGroup>> respDirectCostGroup = respCostList.stream().collect(Collectors.groupingBy(CostCostingGroup::getAccountCode));
+        //按会计科目逐个生成分摊结果查询对象
+        respDirectCostGroup.forEach((respAccountCode,respAccountCostList)->{
+            CostCostingGroup firtsDirectCost = respAccountCostList.get(NumberConstant.ZERO);
+            //汇总金额
+            BigDecimal totalAccountCost = respAccountCostList.stream().map(CostCostingGroup::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+            //创建分摊结果查询对象
+            AllocationQuery allocationQuery = new AllocationQuery();
+            allocationQuery.setDateYear(startDTO.getYear()).setDateMonth(startDTO.getMonth())
+                    .setHospId(UserContext.getHospId()).setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
+                    .setCreateTime(System.currentTimeMillis())
+                    .setLevelSort(responsibility.getShareLevel()).setLevelName(responsibility.getShareName())
+                    .setTargetResponsibilityCode(responsibility.getResponsibilityCode())
+                    .setTargetResponsibilityName(responsibility.getResponsibilityName())
+                    .setShareLevelId(responsibility.getShareId())
+                    .setOriginId(firtsDirectCost.getId()).setOriginType(Long.valueOf(NumberConstant.ONE)).setAmount(totalAccountCost)
+                    .setAccountingCode(firtsDirectCost.getAccountCode()).setAccountingName(firtsDirectCost.getAccountName());
+            allocationQueryList.add(allocationQuery);
+        });
+        return allocationQueryList;
+    }
+
+    /**
+     * 创建间接成本的分摊结果查询对象
+     * @param startDTO 分摊年月所在对象
+     * @param indirectCost 间接成本对象
+     * @return
+     */
+    public AllocationQuery createIndirectAllocationQuery(StartDTO startDTO,Allocation indirectCost){
+        AllocationQuery allocationQuery = new AllocationQuery();
+        allocationQuery.setDateYear(startDTO.getYear()).setDateMonth(startDTO.getMonth())
+                .setHospId(UserContext.getHospId()).setResponsibilityCode(indirectCost.getResponsibilityCode())
+                .setResponsibilityName(indirectCost.getResponsibilityName())
+                .setOriginId(indirectCost.getId()).setOriginType(Long.valueOf(NumberConstant.TWO)).setAmount(indirectCost.getAmount())
+                .setAccountingCode(indirectCost.getAccountCode()).setAccountingName(indirectCost.getAccountName())
+                .setCreateTime(System.currentTimeMillis())
+                .setLevelSort(indirectCost.getLevelSort()).setLevelName(indirectCost.getLevelName())
+                .setTargetResponsibilityCode(indirectCost.getTargetResponsibilityCode())
+                .setTargetResponsibilityName(indirectCost.getTargetResponsibilityName())
+                .setShareLevelId(indirectCost.getShareLevelId());
+        return allocationQuery;
+    }
+
+    /**
+     * 执行分摊后续处理脚本
+     * @param startDTO
+     */
+    public void execAllocationSQL(StartDTO startDTO){
+        //执行分摊后续处理脚本
+        Map<String,String> sqlParameter = new HashMap<>();
+        if(startDTO.getMonth()<10){
+            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,startDTO.getYear()+"-0"+startDTO.getMonth());
+        }else{
+            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,startDTO.getYear()+"-"+startDTO.getMonth());
+        }
+        sqlService.autoExecuteSql(CustomSqlTypeEnum.COST_ALLOCATION_CALC.getCode(),sqlParameter);
+    }
+
+
+    /**
+     * 按责任中心分摊
+     * @param startDTO
+     * @param hospId
+     */
+    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
+    public  void startCostAllocation(StartDTO startDTO, Long hospId){
         long timeMillis = System.currentTimeMillis();
         // 得到这个月的所有导入的成本数据
         List<CostCostingGroup> costingGroups = costCostingGroupService.getByYearAndDate(startDTO.getYear(), startDTO.getMonth(), hospId);
@@ -207,14 +690,14 @@ public class AllocationServiceImpl extends ServiceImpl<AllocationMapper, Allocat
                                 String valueResponsibilityCode = paramValue.getResponsibilityCode();
                                 //优化循环调用
                                 String targetRespName = responsibilityNameMap.get(valueResponsibilityCode);
-    //                            String targetRespName = responsibilityService.getByCode(valueResponsibilityCode, hospId);
+                                //                            String targetRespName = responsibilityService.getByCode(valueResponsibilityCode, hospId);
                                 Long targetShareLevelId = responsibilityShareIdDictMap.get(valueResponsibilityCode);
-    //                            Long targetShareLevelId = responsibilityService.getLevelIdByCode(valueResponsibilityCode, hospId);
+                                //                            Long targetShareLevelId = responsibilityService.getLevelIdByCode(valueResponsibilityCode, hospId);
                                 if (Objects.isNull(targetShareLevelId)) {
                                     throw new CostException("目标责任中心分摊层级异常");
                                 }
                                 String shareParamName = ShareParamNameMap.get(paramValue.getShareParamCode());
-    //                            String shareParamName = shareParamService.getByCode(paramValue.getShareParamCode(), hospId);
+                                //                            String shareParamName = shareParamService.getByCode(paramValue.getShareParamCode(), hospId);
                                 targetAllocation.setDateMonth(startDTO.getMonth()).setDateYear(startDTO.getYear()).setLevelSort(shareLevelVO.getLeverSort())
                                         .setLevelName(shareLevelVO.getShareName()).setHospId(hospId).setResponsibilityCode(responsibility.getResponsibilityCode())
                                         .setResponsibilityName(responsibility.getResponsibilityName()).setAccountShareId(accountShareId).setAmount(targetAmount)
@@ -244,15 +727,6 @@ public class AllocationServiceImpl extends ServiceImpl<AllocationMapper, Allocat
         // 入cost_allocation_query 表 便于后续操作
         this.saveAllocationQuery(list, hospId, startDTO.getYear(), startDTO.getMonth(),shareLevelVOs,accountShareList);
 
-        Map<String,String> sqlParameter = new HashMap<>();
-        if(startDTO.getMonth()<10){
-            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,startDTO.getYear()+"-0"+startDTO.getMonth());
-
-        }else{
-            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,startDTO.getYear()+"-"+startDTO.getMonth());
-
-        }
-        sqlService.autoExecuteSql(CustomSqlTypeEnum.COST_ALLOCATION_CALC.getCode(),sqlParameter);
     }
 
     private void removeData(StartDTO startDTO, Long hospId) {
@@ -823,9 +1297,10 @@ public class AllocationServiceImpl extends ServiceImpl<AllocationMapper, Allocat
             throw new CostException(500, "参数异常");
         }
         // 处理第几次分摊计算值
-        List<Allocation> allocationList = baseMapper.selectList(new QueryWrapper<Allocation>().lambda()
-                .eq(Allocation::getHospId, hospId).eq(Allocation::getLevelSort, levelSort).eq(Allocation::getShareLevelId, shareLevelId).eq(Allocation::getDateYear, year)
-                .eq(Allocation::getDateMonth, month));
+//        List<Allocation> allocationList = baseMapper.selectList(new QueryWrapper<Allocation>().lambda()
+//                .eq(Allocation::getHospId, hospId).eq(Allocation::getLevelSort, levelSort).eq(Allocation::getShareLevelId, shareLevelId).eq(Allocation::getDateYear, year)
+//                .eq(Allocation::getDateMonth, month));
+        List<Allocation> allocationList =baseMapper.getLevelSortAllocation(hospId,year,month,shareLevelId);
         // 找会计科室的时候使用的
         List<Allocation> allocations = baseMapper.selectList(new QueryWrapper<Allocation>().lambda()
                 .eq(Allocation::getHospId, hospId).eq(Allocation::getDateYear, year)

+ 25 - 0
src/main/java/com/kcim/service/impl/CenterServiceImpl.java

@@ -1,7 +1,9 @@
 package com.kcim.service.impl;
 
 import com.alibaba.fastjson2.JSON;
+import com.kcim.common.constants.CommonConstant;
 import com.kcim.common.constants.Constant;
+import com.kcim.common.constants.NumberConstant;
 import com.kcim.common.exception.CostException;
 import com.kcim.dao.model.dto.SysDepartment;
 import com.kcim.endPoint.CenterEndPoint;
@@ -109,6 +111,29 @@ public class CenterServiceImpl implements CenterService {
         }
     }
 
+    /**
+     * 获取指定Code的参数指定序号的数值
+     * @param parameterCode
+     * @param index
+     * @return
+     */
+    @Override
+    public String getParameterValueByIndex(Long parameterCode, int index) {
+        try {
+            if(ObjectUtils.isEmpty(index)) {
+                index = NumberConstant.ZERO;
+            }
+            String parameterValue = getParameterValue(parameterCode);
+            String[] split = parameterValue.split(CommonConstant.SEPARATOR_VERTICAL);
+            if(split.length>index){
+                return split[index];
+            }
+            return Constant.EMPTY_STR;
+        }catch (Exception e){
+            throw new CostException(e.getMessage());
+        }
+    }
+
     @Override
     public List<KCClassVo> getKCClass() {
         Object kcClass = endPoint.getKCClass(null);

+ 11 - 0
src/main/java/com/kcim/service/impl/ComputeMedicalDepartmentProfitServiceImpl.java

@@ -103,6 +103,7 @@ public class ComputeMedicalDepartmentProfitServiceImpl implements ComputeMedical
         if(CollectionUtils.isEmpty(byReportType)){
             throw new CostException("未找到【"+reportType+"】对应的报表项目");
         }
+        Map<Long, List<ReportForm>> reportIdMap = byReportType.stream().collect(Collectors.groupingBy(ReportForm::getId));
         Map<Long,Integer> sortMap = new HashMap<>();
         Map<Long,Integer> hideMap = new HashMap<>();
         for (ReportForm reportForm : byReportType) {
@@ -167,6 +168,16 @@ public class ComputeMedicalDepartmentProfitServiceImpl implements ComputeMedical
             vo.setNum(profit.getReportNum());
             vo.setReportType(Integer.valueOf(reportType));
             vo.setSort(profit.getSort());
+            List<ReportForm> profitReportFormList = reportIdMap.get(profit.getReportId());
+            if(!CollectionUtils.isEmpty(profitReportFormList)) {
+                vo.setDataType(profitReportFormList.get(NumberConstant.ZERO).getDataType());
+                vo.setDecimalPlace(profitReportFormList.get(NumberConstant.ZERO).getDecimalPlace());
+                vo.setPermil(profitReportFormList.get(NumberConstant.ZERO).getPermil());
+            }else {
+                vo.setDataType(NumberConstant.ONE);
+                vo.setDecimalPlace(NumberConstant.TWO);
+                vo.setPermil(NumberConstant.ONE);
+            }
             reportFormProfitVos.add(vo);
         }
 

+ 14 - 0
src/main/java/com/kcim/service/impl/CostAccountShareDetailServiceImpl.java

@@ -0,0 +1,14 @@
+package com.kcim.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kcim.dao.mapper.CostAccountShareDetailMapper;
+import com.kcim.dao.model.CostAccountShareDetail;
+import com.kcim.service.CostAccountShareDetailService;
+import org.springframework.stereotype.Service;
+
+
+@Service("costAccountShareDetailService")
+public class CostAccountShareDetailServiceImpl extends ServiceImpl<CostAccountShareDetailMapper, CostAccountShareDetail> implements CostAccountShareDetailService {
+
+
+}

+ 14 - 0
src/main/java/com/kcim/service/impl/CostAccountShareParamServiceImpl.java

@@ -0,0 +1,14 @@
+package com.kcim.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kcim.dao.mapper.CostAccountShareParamMapper;
+import com.kcim.dao.model.CostAccountShareParam;
+import com.kcim.service.CostAccountShareParamService;
+import org.springframework.stereotype.Service;
+
+
+@Service("costAccountShareParamService")
+public class CostAccountShareParamServiceImpl extends ServiceImpl<CostAccountShareParamMapper, CostAccountShareParam> implements CostAccountShareParamService {
+
+
+}

+ 258 - 11
src/main/java/com/kcim/service/impl/CostAccountShareServiceImpl.java

@@ -13,16 +13,17 @@ import com.kcim.common.util.JacksonUtil;
 import com.kcim.common.util.PageUtils;
 import com.kcim.common.util.UserContext;
 import com.kcim.dao.mapper.CostAccountShareMapper;
-import com.kcim.dao.model.Accounting;
-import com.kcim.dao.model.CostAccountShare;
-import com.kcim.dao.model.CostShareLevel;
-import com.kcim.dao.model.Responsibility;
+import com.kcim.dao.model.*;
 import com.kcim.dao.model.dto.AccountShareCopyDto;
 import com.kcim.dao.model.dto.CostAccountShareEditDto;
 import com.kcim.dao.model.dto.CostAccountShareSaveDto;
 import com.kcim.dao.model.dto.ShareParamEditDto;
+import com.kcim.dao.repository.CostAccountShareDetailRepository;
+import com.kcim.dao.repository.CostAccountShareParamRepository;
+import com.kcim.dao.repository.CostAccountShareTargetRepository;
 import com.kcim.service.*;
 import com.kcim.vo.*;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
@@ -37,6 +38,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 @Slf4j
+@AllArgsConstructor
 @Service("costAccountShareService")
 public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMapper, CostAccountShare> implements CostAccountShareService {
 
@@ -48,13 +50,19 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
 
     private final CostShareParamService costShareParamService;
 
+    private final CostAccountShareDetailRepository costAccountShareDetailRepository;
+    private final CostAccountShareParamRepository costAccountShareParamRepository;
+    private final CostAccountShareTargetRepository costAccountShareTargetRepository;
 
-    public CostAccountShareServiceImpl(ResponsibilityService responsibilityService, AccountingService accountingService, CostShareLevelService costShareLevelService, CostShareParamService costShareParamService) {
-        this.responsibilityService = responsibilityService;
-        this.accountingService = accountingService;
-        this.costShareLevelService = costShareLevelService;
-        this.costShareParamService = costShareParamService;
-    }
+
+//    public CostAccountShareServiceImpl(CostAccountShareParamService costAccountShareParamService,CostAccountShareDetailService costAccountShareDetailService,ResponsibilityService responsibilityService, AccountingService accountingService, CostShareLevelService costShareLevelService, CostShareParamService costShareParamService) {
+//        this.responsibilityService = responsibilityService;
+//        this.accountingService = accountingService;
+//        this.costShareLevelService = costShareLevelService;
+//        this.costShareParamService = costShareParamService;
+//        this.costAccountShareDetailService = costAccountShareDetailService;
+//        this.costAccountShareParamService = costAccountShareParamService;
+//    }
 
     /**
      * 分页查询责任中心成本对照相关数据
@@ -157,6 +165,8 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
         costAccountShareRequest.setHospId(hospId);
         costAccountShareRequest.setCreateTime(System.currentTimeMillis());
         baseMapper.insert(costAccountShareRequest);
+        //提交分摊设置对应的会计科目
+        applyShareParamProportion(costAccountShareRequest.getId(),costAccountShareRequest.getAccountingCodes());
     }
 
     /**
@@ -278,6 +288,38 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
         BeanUtil.convertObj(accountShareEditDto, costAccountShare);
         costAccountShare.setId(costAccountShareEditDto.getId());
         baseMapper.updateById(costAccountShare);
+        //提交分摊设置对应的会计科目
+        applyShareParamProportion(costAccountShare.getId(),costAccountShare.getAccountingCodes());
+    }
+
+    /**
+     * 提交分摊设置对应的会计科目
+     * @param accountShareId
+     * @param accountingCodes
+     */
+    public void applyShareParamProportion(Long accountShareId , String accountingCodes ){
+        if(StringUtils.isEmpty(accountingCodes)){
+            //先作废上次的数据
+            costAccountShareDetailRepository.delCostAccountShareDetail(accountShareId);
+            return;
+        }
+        List<String> accountingCodeList = Arrays.stream(accountingCodes.split(StrUtil.COMMA)).collect(Collectors.toList());
+        applyAccountShareDetail(accountShareId,accountingCodeList);
+    }
+
+
+    /**
+     * 提交分摊设置对应的会计科目
+     * @param accountShareId
+     * @param accountingCodeList
+     */
+    public void applyAccountShareDetail(Long accountShareId , List<String> accountingCodeList){
+        //先作废上次的数据
+        costAccountShareDetailRepository.delCostAccountShareDetail(accountShareId);
+        //保存新的分摊参数
+        List<CostAccountShareDetail> costAccountShareDetailList = accountingCodeList.stream().map(accountingCode ->
+                createCostAccountShareDetail(accountShareId,accountingCode)).collect(Collectors.toList());
+        costAccountShareDetailRepository.saveBatch(costAccountShareDetailList);
     }
 
 
@@ -331,6 +373,8 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
             String paramList = JacksonUtil.obj2StrPretty(shareParamProportionVOList);
 
             costAccountShare.setParamList(paramList);
+            //提交分摊参数
+            applyShareParamProportion(shareParamEditDto);
         } else {
             costAccountShare.setParamList(null);
         }
@@ -338,6 +382,20 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
         baseMapper.updateById(costAccountShare);
     }
 
+    /**
+     * 提交分摊参数
+     * @param shareParamEditDto
+     */
+    public void applyShareParamProportion(ShareParamEditDto shareParamEditDto){
+        //先作废上次的数据
+        costAccountShareParamRepository.delCostAccountParamDetail(shareParamEditDto.getId());
+        //保存新的分摊参数
+        List<ShareParamProportionVO> newShareParamList = shareParamEditDto.getShareParamProportionVOList();
+        List<CostAccountShareParam> costAccountShareParamList = newShareParamList.stream().map(newShareParam ->
+                createCostAccountShareParam(shareParamEditDto.getId(),newShareParam.getShareParamCode(),newShareParam.getShareParamPopout())).collect(Collectors.toList());
+        costAccountShareParamRepository.saveBatch(costAccountShareParamList);
+    }
+
     /**
      * 获取责任中心成本表的分摊参数的集合
      *
@@ -422,7 +480,10 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
             accountShareList.add(accountShareRequest);
         });
         this.saveBatch(accountShareList);
-
+        //主表向明细表同步
+        synMainCostAccountShare(accountShareList);
+        //复制目标责任中心设置
+        copyCostAccountShareTarget(accountShareCopyDto,accountShareList);
     }
 
     /**
@@ -500,6 +561,8 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
     public void deleteByIds(List<Long> idList) {
         this.removeByIds(idList);
+        costAccountShareDetailRepository.delCostAccountShareDetailList(idList);
+        costAccountShareParamRepository.delCostAccountShareParamList(idList);
     }
 
     @Override
@@ -510,4 +573,188 @@ public class CostAccountShareServiceImpl extends ServiceImpl<CostAccountShareMap
         );
     }
 
+    /**
+     *
+     * @param direction 1主表向明细表同步 2明细表向主表同步
+     * @param accountShareId
+     */
+    @Override
+    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
+    public void synCostAccountShare(Integer direction, Long accountShareId) {
+        if(NumberConstant.ONE.equals(direction)){
+            synMainCostAccountShare(direction,accountShareId);
+        }
+    }
+
+    /**
+     * 主表向明细表同步
+     * @param direction
+     * @param accountShareId
+     */
+    public void synMainCostAccountShare(Integer direction, Long accountShareId) {
+        List<CostAccountShare> costAccountShareList = getCostAccountShareList(accountShareId);
+        if (CollectionUtils.isEmpty(costAccountShareList)) {
+            return;
+        }
+        synMainCostAccountShare(costAccountShareList);
+    }
+
+    /**
+     * 主表向明细表同步
+     * @param costAccountShareList
+     */
+    public void synMainCostAccountShare(List<CostAccountShare> costAccountShareList){
+        //生成分摊设置对应的会计科目对象
+        List<CostAccountShareDetail> fullCostAccountShareDetailList=new ArrayList<>();
+        //生成分摊设置对应的分摊参数对象
+        List<CostAccountShareParam> fullCostAccountShareParamList=new ArrayList<>();
+        //按主表的数据逐个转成会计科目对象和分摊参数对象
+        costAccountShareList.stream().forEach(costAccountShare->{
+            //处理会计科目
+            String accountingCodes = costAccountShare.getAccountingCodes();
+            if(!StringUtils.isEmpty(accountingCodes)){
+                List<String> accountingCodeList = Arrays.stream(accountingCodes.split(StrUtil.COMMA)).collect(Collectors.toList());
+                List<CostAccountShareDetail> costAccountShareDetailList = accountingCodeList.stream().map(accountingCode ->
+                        createCostAccountShareDetail(costAccountShare.getId(),accountingCode)).collect(Collectors.toList());
+                fullCostAccountShareDetailList.addAll(costAccountShareDetailList);
+            }
+
+            //处理分摊参数
+            String paramList = costAccountShare.getParamList();
+            if(!StringUtils.isEmpty(paramList)) {
+                List<ShareParamProportionVO> shareParamProportionVOS = JacksonUtil.str2ObjList(paramList, new TypeReference<List<ShareParamProportionVO>>() {
+                });
+                List<CostAccountShareParam> costAccountShareParamList = shareParamProportionVOS.stream().map(shareParamProportion ->
+                                createCostAccountShareParam(costAccountShare.getId(), shareParamProportion.getShareParamCode(), shareParamProportion.getShareParamPopout())).
+                        collect(Collectors.toList());
+                fullCostAccountShareParamList.addAll(costAccountShareParamList);
+            }
+        });
+        //作废原来的数据
+        List<Long> accountShareIdList = costAccountShareList.stream().map(CostAccountShare::getId).collect(Collectors.toList());
+        costAccountShareDetailRepository.delCostAccountShareDetailList(accountShareIdList);
+        costAccountShareParamRepository.delCostAccountShareParamList(accountShareIdList);
+        //保存会计科目对象
+        if (!CollectionUtils.isEmpty(fullCostAccountShareDetailList)) {
+            costAccountShareDetailRepository.saveBatch(fullCostAccountShareDetailList,NumberConstant.ONE_HUNDRED);
+        }
+        //保存分摊参数对象
+        if (!CollectionUtils.isEmpty(fullCostAccountShareParamList)) {
+            costAccountShareParamRepository.saveBatch(fullCostAccountShareParamList,NumberConstant.ONE_HUNDRED);
+        }
+    }
+
+    /**
+     * 创建分摊设置对应的会计科目对象
+     * @param accountShareId
+     * @param accountingCode
+     * @return
+     */
+    public CostAccountShareDetail createCostAccountShareDetail(Long accountShareId,String accountingCode){
+        CostAccountShareDetail costAccountShareDetail = new CostAccountShareDetail();
+        costAccountShareDetail.setHospId(UserContext.getHospId());
+        costAccountShareDetail.setAccountShareId(accountShareId);
+        costAccountShareDetail.setAccountingCode(accountingCode);
+        costAccountShareDetail.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId()));
+        costAccountShareDetail.setCreateTime(new Date());
+        return costAccountShareDetail;
+    }
+
+    /**
+     * 创建分摊设置对应的分摊参数对象
+     * @param accountShareId
+     * @param shareParamCode
+     * @param shareParamProportion
+     * @return
+     */
+    public CostAccountShareParam createCostAccountShareParam(Long accountShareId,String shareParamCode,BigDecimal shareParamProportion) {
+        CostAccountShareParam costAccountShareParam = new CostAccountShareParam();
+        costAccountShareParam.setHospId(UserContext.getHospId());
+        costAccountShareParam.setAccountShareId(accountShareId);
+        costAccountShareParam.setShareParamCode(shareParamCode);
+        costAccountShareParam.setShareParamProportion(shareParamProportion);
+        costAccountShareParam.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId()));
+        costAccountShareParam.setCreateTime(new Date());
+        return costAccountShareParam;
+    }
+
+    /**
+     * 获取所有的分摊参数配置
+     * @return
+     */
+    public  List<CostAccountShare> getCostAccountShareList(Long accountShareId){
+        LambdaQueryWrapper<CostAccountShare> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(CostAccountShare::getHospId, UserContext.getHospId());
+        queryWrapper.eq(CostAccountShare::getDeleteTime, NumberConstant.ZERO);
+        if(!ObjectUtils.isEmpty(accountShareId)){
+            queryWrapper.eq(CostAccountShare::getId, accountShareId);
+        }
+        return baseMapper.selectList(queryWrapper);
+    }
+
+    /**
+     * 复制目标责任中心设置
+     * @param accountShareCopyDto
+     * @param accountShareList
+     */
+    public void copyCostAccountShareTarget(AccountShareCopyDto accountShareCopyDto,List<CostAccountShare> accountShareList){
+        //没有指定的复制目标
+        if(CollectionUtils.isEmpty(accountShareList)){
+            return;
+        }
+        List<CostAccountShareTarget> costAccountShareTargetList = costAccountShareTargetRepository.getCostAccountShareTarget(accountShareCopyDto.getId());
+        //没有设置指定的分摊目标责任中心
+        if(CollectionUtils.isEmpty(costAccountShareTargetList)){
+            return;
+        }
+        //逐个分摊设置复制目标责任中心
+        List<CostAccountShareTarget> copyCostAccountShareTargetList=new ArrayList<>();
+        for (CostAccountShare costAccountShare : accountShareList) {
+            List<CostAccountShareTarget> copyTargetList = costAccountShareTargetList.stream().map(target -> createCostAccountShareTarget(costAccountShare.getId(), target.getTargetResponsibilityCode())).collect(Collectors.toList());
+            copyCostAccountShareTargetList.addAll(copyTargetList);
+        }
+        //保存复制的数据
+        if(CollectionUtils.isEmpty(copyCostAccountShareTargetList)) {
+            costAccountShareTargetRepository.saveBatch(copyCostAccountShareTargetList);
+        }
+    }
+
+    @Override
+    public void applyAccountShareTarget(ShareTargetMapVo shareTargetMapVo) {
+        applyAccountShareTarget(shareTargetMapVo.getAccountShareId(),shareTargetMapVo.getTargetRespCodeList());
+    }
+    /**
+     * 提交分摊设置对应的目标责任中心
+     * @param accountShareId
+     * @param respCodeList
+     */
+    public void applyAccountShareTarget(Long accountShareId , List<String> respCodeList){
+        //先作废上次的数据
+        costAccountShareTargetRepository.delCostAccountShareTarget(accountShareId);
+        //没有新的目标责任中心时不用处理
+        if(CollectionUtils.isEmpty(respCodeList)){
+            return;
+        }
+        //保存新的目标责任中心
+        List<CostAccountShareTarget> costAccountShareTargetList = respCodeList.stream().map(respCode ->
+                createCostAccountShareTarget(accountShareId, respCode)).collect(Collectors.toList());
+        costAccountShareTargetRepository.saveBatch(costAccountShareTargetList);
+    }
+
+    /**
+     * 生成分摊设置对应的目标责任中心对象
+     * @param accountShareId
+     * @param targetResponsibilityCode
+     * @return
+     */
+    public CostAccountShareTarget createCostAccountShareTarget(Long accountShareId,String targetResponsibilityCode) {
+        CostAccountShareTarget costAccountShareTarget = new CostAccountShareTarget();
+        costAccountShareTarget.setHospId(UserContext.getHospId());
+        costAccountShareTarget.setAccountShareId(accountShareId);
+        costAccountShareTarget.setTargetResponsibilityCode(targetResponsibilityCode);
+        costAccountShareTarget.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId()));
+        costAccountShareTarget.setCreateTime(new Date());
+        return costAccountShareTarget;
+    }
+
 }

+ 15 - 0
src/main/java/com/kcim/service/impl/CostAccountShareTargetService.java

@@ -0,0 +1,15 @@
+package com.kcim.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.kcim.dao.model.CostAccountShareTarget;
+
+/**
+ * 成本分摊对应的分摊目标责任中心
+ *
+ * @author Wang.YS
+ * @date 2025-04-03 11:13:36
+ */
+public interface CostAccountShareTargetService extends IService<CostAccountShareTarget> {
+
+}
+

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

@@ -15,7 +15,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.ImmutableMap;
 import com.kcim.common.constants.NumberConstant;
+import com.kcim.common.constants.ParameterConstant;
 import com.kcim.common.constants.SQLParameter;
+import com.kcim.common.constants.SplitConstant;
 import com.kcim.common.enums.CustomSqlTypeEnum;
 import com.kcim.common.enums.DateStyleEnum;
 import com.kcim.common.enums.ErrorCodeEnum;
@@ -111,7 +113,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
 
     private final CostAccountShareService costAccountShareService;
 
-    private final UserResponsibilityRightService userResponsibilityRightService ;
+    private final UserResponsibilityRightService userResponsibilityRightService;
 
 
     private final SqlService sqlService;
@@ -123,7 +125,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
 
     private final String PERCENT_FIELD = "percent";
 
-    public CostDepartmentProfitServiceImpl(ReportFormService reportFormService, IncomeCollectionService incomeCollectionService, CostShareLevelService costShareLevelService, ResponsibilityService responsibilityService, ReportRelationService reportRelationService, AllocationService allocationService, AllocationQueryService allocationQueryService, FileRecordService fileRecordService, MinioConfig minioConfig, MinioFileUtil minioFileUtil, CenterService centerService, CostDepartmentProfitRepository costDepartmentProfitRepository, ComputeLastProfitDateRepository computeLastProfitDateRepository, AccountingService accountingService, CostAccountShareService costAccountShareService, SqlService sqlService,UserResponsibilityRightService userResponsibilityRightService) {
+    public CostDepartmentProfitServiceImpl(ReportFormService reportFormService, IncomeCollectionService incomeCollectionService, CostShareLevelService costShareLevelService, ResponsibilityService responsibilityService, ReportRelationService reportRelationService, AllocationService allocationService, AllocationQueryService allocationQueryService, FileRecordService fileRecordService, MinioConfig minioConfig, MinioFileUtil minioFileUtil, CenterService centerService, CostDepartmentProfitRepository costDepartmentProfitRepository, ComputeLastProfitDateRepository computeLastProfitDateRepository, AccountingService accountingService, CostAccountShareService costAccountShareService, SqlService sqlService, UserResponsibilityRightService userResponsibilityRightService) {
         this.reportFormService = reportFormService;
         this.incomeCollectionService = incomeCollectionService;
         this.costShareLevelService = costShareLevelService;
@@ -140,7 +142,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         this.accountingService = accountingService;
         this.costAccountShareService = costAccountShareService;
         this.sqlService = sqlService;
-        this.userResponsibilityRightService=userResponsibilityRightService;
+        this.userResponsibilityRightService = userResponsibilityRightService;
     }
 
     /**
@@ -1112,8 +1114,19 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
     }
 
     @Override
-    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
     public void computeProfit(String computeDate, Long hospId, String reportType) {
+        //需要用代码计算时
+        if(IsNeedCalc(reportType)){
+            //计算科室损益
+            computeProfitAction(computeDate, hospId, reportType);
+            //计算同环比
+            handleSpecificMonthsCalculation(hospId, computeDate, reportType);
+        }
+        execDeptProfitSql(computeDate, reportType);
+    }
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+    public void computeProfitAction(String computeDate, Long hospId, String reportType) {
 //        DateTime parse = DateUtil.parse(computeDate);
 //        int year = DateUtil.year(parse);
 //        int month = DateUtil.month(parse) + 1;
@@ -1201,13 +1214,237 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         this.saveBatch(costDepartmentProfits);
         //记录最后一次 损益计算日期
         computeLastProfitDateRepository.saveLastComputeDate(hospId, computeDate);
+    }
+
+    /**
+     * 处理指定月份及关联月份的计算(当前月、下个月、明年同月)
+     *
+     * @param hospId      院区ID
+     * @param computeDate 日期字符串,格式:YYYY-MM
+     * @param shareType   报表类型
+     */
+    public void handleSpecificMonthsCalculation(Long hospId, String computeDate, String shareType) {
+        // 解析输入的年月
+        Integer currentYear = ComputeDateUtils.getComputeYear(computeDate);
+        Integer currentMonth = ComputeDateUtils.getComputeMonth(computeDate);
+        // 计算需要处理的期间信息
+        List<PeriodInfoVO> periods = calculateAllRelatedPeriods(currentYear, currentMonth);
+
+        // 获取所有相关的期间数据
+        Map<String, List<CostDepartmentProfit>> 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<CostDepartmentProfit> currentRecords = allDataMap.get(currentKey);
+                List<CostDepartmentProfit> prevRecords = getPeriodData(allDataMap, period.getPrevYear(), period.getPrevMonth());
+                List<CostDepartmentProfit> 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<CostDepartmentProfit>> loadAllPeriodData(Long hospId, String 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<CostDepartmentProfit>> result = new HashMap<>();
+        Set<String> processedKeys = new HashSet<>(); // 已处理的键集合
+
+        for (String key : allPeriodKeys) {
+            if (!processedKeys.contains(key)) {
+                int[] ym = parsePeriodKey(key);
+                List<CostDepartmentProfit> 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<CostDepartmentProfit> getPeriodRecords(Long hospId, String shareType, int year, int month) {
+        return this.list(new QueryWrapper<CostDepartmentProfit>().lambda()
+                .eq(CostDepartmentProfit::getHospId, hospId)
+                .eq(CostDepartmentProfit::getShareType, shareType)
+                .eq(CostDepartmentProfit::getYear, year)
+                .eq(CostDepartmentProfit::getMonth, month)
+                .eq(CostDepartmentProfit::getDeleteTime, 0));
+    }
+
+    /**
+     * 根据年月获取对应的数据(从内存中查找)
+     */
+    private List<CostDepartmentProfit> getPeriodData(Map<String, List<CostDepartmentProfit>> dataMap, int year, int month) {
+        String key = buildPeriodKey(year, month);
+        return dataMap.getOrDefault(key, Collections.emptyList());
+    }
+
+    /**
+     * 批量计算同环比及同期金额(使用预加载数据)
+     */
+    public void calculatePeriodComparisonWithPreloadedData(
+            List<CostDepartmentProfit> currentRecords,
+            List<CostDepartmentProfit> prevRecords,
+            List<CostDepartmentProfit> lastYearRecords) {
+
+        if (CollectionUtils.isEmpty(currentRecords)) {
+            return;
+        }
+
+        // 创建责任码+报表编号到记录的映射
+        Map<String, CostDepartmentProfit> prevMap = new HashMap<>();
+        for (CostDepartmentProfit record : prevRecords) {
+            String key = buildRecordKey(record);
+            prevMap.put(key, record);
+        }
+
+        Map<String, CostDepartmentProfit> lastYearMap = new HashMap<>();
+        for (CostDepartmentProfit record : lastYearRecords) {
+            String key = buildRecordKey(record);
+            lastYearMap.put(key, record);
+        }
+
+        // 批量更新数据
+        for (CostDepartmentProfit record : currentRecords) {
+            String key = buildRecordKey(record);
+            CostDepartmentProfit prevRecord = prevMap.get(key);
+            CostDepartmentProfit 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(CostDepartmentProfit record) {
+        return record.getResponsibilityCode() + "_" + 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;
+    }
 
-        Map<String,String> sqlParameter = new HashMap<>();
-        sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,computeDate);
-        sqlParameter.put(SQLParameter.REPORT_TYPE_CODE,reportType);
-        sqlService.autoExecuteSql(CustomSqlTypeEnum.DEPT_PROFIT_CALC.getCode(),sqlParameter);
+
+
+    /**
+     * 执行科室损益后续脚本
+     *
+     * @param computeDate
+     * @param reportType
+     */
+    public void execDeptProfitSql(String computeDate, String reportType) {
+        Map<String, String> sqlParameter = new HashMap<>();
+        sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE, computeDate);
+        sqlParameter.put(SQLParameter.REPORT_TYPE_CODE, reportType);
+        sqlService.autoExecuteSql(CustomSqlTypeEnum.DEPT_PROFIT_CALC.getCode(), sqlParameter);
     }
 
+
     private DictDataVo getDictDataVo(String reportType) {
         DictDataVo dict = centerService.getDict(PROFIT_REPORT_TYPE);
         if (Objects.isNull(dict)) {
@@ -1246,8 +1483,14 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         if (CollUtil.isEmpty(reportFormList)) {
             throw new CostException(500, "损益表未找到");
         }
-        List<CommonTitleReportVo> commonTitleReportVos = reportFormList.stream().map(reportForm -> new CommonTitleReportVo(reportForm.getId(), reportForm.getParentId(), reportForm.getReportName(), reportForm.getSort())).collect(Collectors.toList());
-
+        List<CommonTitleReportVo> commonTitleReportVos = reportFormList.stream().map(reportForm -> {
+            CommonTitleReportVo commonTitleReportVo = new CommonTitleReportVo(reportForm.getId(), reportForm.getParentId(), reportForm.getReportName(), reportForm.getSort());
+            commonTitleReportVo.setDataType(reportForm.getDataType());
+            commonTitleReportVo.setDecimalPlace(reportForm.getDecimalPlace());
+            commonTitleReportVo.setPermil(reportForm.getPermil());
+            return commonTitleReportVo;
+        }).collect(Collectors.toList());
+//        List<CommonTitleReportVo> commonTitleReportVos = BeanUtil.convertList(reportFormList,CommonTitleReportVo.class);
         Map<Long, List<CommonTitleReportVo>> collect = commonTitleReportVos.stream().collect(Collectors.groupingBy(CommonTitleReportVo::getParentReportId));
         List<CommonTitleReportVo> titleReportVoList = collect.get(NumberConstant.ZERO_L);
         for (CommonTitleReportVo reportVo : titleReportVoList) {
@@ -1278,9 +1521,9 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         Map<Long, List<CostDepartmentProfit>> collectGroup = departmentProfits.stream().collect(Collectors.groupingBy(CostDepartmentProfit::getReportId));
         for (ReportForm reportForm : reportFormList) {
             Integer hide = reportForm.getHide();
-            if(hide.equals(NumberConstant.ONE)){
+            if (hide.equals(NumberConstant.ONE)) {
                 List<CostDepartmentProfit> costDepartmentProfits = collectGroup.get(reportForm.getId());
-                if(!CollectionUtils.isEmpty(costDepartmentProfits)) {
+                if (!CollectionUtils.isEmpty(costDepartmentProfits)) {
                     filterProfit.addAll(costDepartmentProfits);
                 }
             }
@@ -1305,7 +1548,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             throw new CostException("未找到对应分摊层级责任中心");
         }
         //获取授权的责任中心
-        responsibilityList=userResponsibilityRightService.getAuthorizedResponsibility(responsibilityList);
+        responsibilityList = userResponsibilityRightService.getAuthorizedResponsibility(responsibilityList);
         //获取当前院区所有的责任中心
         List<Responsibility> responsibilityAllList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda()
                 .eq(Responsibility::getHospId, hospId));
@@ -1619,7 +1862,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             throw new CostException("未找到对应分摊层级责任中心");
         }
         //获取授权的责任中心
-        responsibilityList=userResponsibilityRightService.getAuthorizedResponsibility(responsibilityList);
+        responsibilityList = userResponsibilityRightService.getAuthorizedResponsibility(responsibilityList);
         List<Responsibility> responsibilityAllList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda()
                 .eq(Responsibility::getHospId, hospId));
         Map<Long, Responsibility> map = responsibilityAllList.stream().collect(Collectors.toMap(Responsibility::getId, responsibility -> responsibility, (a, b) -> b));
@@ -1703,10 +1946,10 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         Map<Long, List<CostDepartmentProfit>> collect1 = departmentProfits.stream().collect(Collectors.groupingBy(CostDepartmentProfit::getReportId));
         for (ReportForm reportForm : reportFormList) {
             Integer hide = reportForm.getHide();
-            if(hide.equals(NumberConstant.ONE)){
+            if (hide.equals(NumberConstant.ONE)) {
 //                filterProfit.addAll(collect1.get(reportForm.getId()));
                 List<CostDepartmentProfit> costDepartmentProfits = collect1.get(reportForm.getId());
-                if(!CollectionUtils.isEmpty(costDepartmentProfits)) {
+                if (!CollectionUtils.isEmpty(costDepartmentProfits)) {
                     filterProfit.addAll(costDepartmentProfits);
                 }
             }
@@ -1715,11 +1958,22 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
 
         Map<Long, Integer> sortMap = reportFormList.stream().collect(Collectors.toMap(ReportForm::getId, ReportForm::getSort, (a, b) -> b));
         Map<Long, String> descriptionMap = reportFormList.stream().collect(Collectors.toMap(ReportForm::getId, reportForm -> !StringUtils.isEmpty(reportForm.getDescription()) ? reportForm.getDescription() : "", (a, b) -> b));
+        Map<Long, List<ReportForm>> reportIdMap = reportFormList.stream().collect(Collectors.groupingBy(ReportForm::getId));
 
         List<CostProfitVo> costProfitVos = BeanUtil.convertList(filterProfit, CostProfitVo.class);
         costProfitVos.forEach(profitVo -> {
             profitVo.setDescription(descriptionMap.get(profitVo.getReportId()));
             profitVo.setSort(sortMap.get(profitVo.getReportId()));
+            List<ReportForm> profitReportFormList = reportIdMap.get(profitVo.getReportId());
+            if (!CollectionUtils.isEmpty(profitReportFormList)) {
+                profitVo.setDataType(profitReportFormList.get(NumberConstant.ZERO).getDataType());
+                profitVo.setDecimalPlace(profitReportFormList.get(NumberConstant.ZERO).getDecimalPlace());
+                profitVo.setPermil(profitReportFormList.get(NumberConstant.ZERO).getPermil());
+            } else {
+                profitVo.setDataType(NumberConstant.ONE);
+                profitVo.setDecimalPlace(NumberConstant.TWO);
+                profitVo.setPermil(NumberConstant.ONE);
+            }
         });
         Map<Long, List<CostProfitVo>> collect = costProfitVos.stream().collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
         List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
@@ -1773,15 +2027,16 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             if (CollUtil.isEmpty(reportFormList)) {
                 throw new CostException(500, "损益表未找到");
             }
+            Map<Long, List<ReportForm>> reportIdMap = reportFormList.stream().collect(Collectors.groupingBy(ReportForm::getId));
             List<CostDepartmentProfit> filterProfit = new ArrayList<>();
 
             Map<Long, List<CostDepartmentProfit>> collect1 = departmentProfits.stream().collect(Collectors.groupingBy(CostDepartmentProfit::getReportId));
             for (ReportForm reportForm : reportFormList) {
                 Integer hide = reportForm.getHide();
-                if(hide.equals(NumberConstant.ONE)){
+                if (hide.equals(NumberConstant.ONE)) {
 //                    filterProfit.addAll(collect1.get(reportForm.getId()));
                     List<CostDepartmentProfit> costDepartmentProfits = collect1.get(reportForm.getId());
-                    if(!CollectionUtils.isEmpty(costDepartmentProfits)) {
+                    if (!CollectionUtils.isEmpty(costDepartmentProfits)) {
                         filterProfit.addAll(costDepartmentProfits);
                     }
                 }
@@ -1794,6 +2049,16 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             costProfitVos.forEach(profitVo -> {
                 profitVo.setDescription(descriptionMap.get(profitVo.getReportId()));
                 profitVo.setSort(sortMap.get(profitVo.getReportId()));
+                List<ReportForm> profitReportFormList = reportIdMap.get(profitVo.getReportId());
+                if (!CollectionUtils.isEmpty(profitReportFormList)) {
+                    profitVo.setDataType(profitReportFormList.get(NumberConstant.ZERO).getDataType());
+                    profitVo.setDecimalPlace(profitReportFormList.get(NumberConstant.ZERO).getDecimalPlace());
+                    profitVo.setPermil(profitReportFormList.get(NumberConstant.ZERO).getPermil());
+                } else {
+                    profitVo.setDataType(NumberConstant.ONE);
+                    profitVo.setDecimalPlace(NumberConstant.TWO);
+                    profitVo.setPermil(NumberConstant.ONE);
+                }
             });
             Map<Long, List<CostProfitVo>> collect = costProfitVos.stream().collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
             List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
@@ -1810,6 +2075,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             BatchCostProfitResponse response = new BatchCostProfitResponse();
             response.setComputeDate(beginComputeDate);
             response.setProfitVoList(costProfitParentVos);
+            response.setAllowDrillDown(true);
             responses.add(response);
             return responses;
         } else {
@@ -1819,6 +2085,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
                 throw new CostException("开始日期不能大于结束日期");
             }
             List<String> intervalMonthList = DateUtils.getIntervalMonthList(date, endDate);
+            List<CostProfitVo> allCostProfitVo=new ArrayList<>();
             if (!CollectionUtils.isEmpty(intervalMonthList)) {
                 for (String computeDates : intervalMonthList) {
                     Integer year = ComputeDateUtils.getComputeYear(computeDates);
@@ -1835,15 +2102,16 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
                     if (CollUtil.isEmpty(reportFormList)) {
                         throw new CostException(500, "损益表未找到");
                     }
+                    Map<Long, List<ReportForm>> reportIdMap = reportFormList.stream().collect(Collectors.groupingBy(ReportForm::getId));
                     List<CostDepartmentProfit> filterProfit = new ArrayList<>();
 
                     Map<Long, List<CostDepartmentProfit>> collect1 = departmentProfits.stream().collect(Collectors.groupingBy(CostDepartmentProfit::getReportId));
                     for (ReportForm reportForm : reportFormList) {
                         Integer hide = reportForm.getHide();
-                        if(hide.equals(NumberConstant.ONE)){
+                        if (hide.equals(NumberConstant.ONE)) {
 //                            filterProfit.addAll(collect1.get(reportForm.getId()));
                             List<CostDepartmentProfit> costDepartmentProfits = collect1.get(reportForm.getId());
-                            if(!CollectionUtils.isEmpty(costDepartmentProfits)) {
+                            if (!CollectionUtils.isEmpty(costDepartmentProfits)) {
                                 filterProfit.addAll(costDepartmentProfits);
                             }
                         }
@@ -1855,7 +2123,19 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
                     costProfitVos.forEach(profitVo -> {
                         profitVo.setDescription(descriptionMap.get(profitVo.getReportId()));
                         profitVo.setSort(sortMap.get(profitVo.getReportId()));
+                        List<ReportForm> profitReportFormList = reportIdMap.get(profitVo.getReportId());
+                        if (!CollectionUtils.isEmpty(profitReportFormList)) {
+                            profitVo.setDataType(profitReportFormList.get(NumberConstant.ZERO).getDataType());
+                            profitVo.setDecimalPlace(profitReportFormList.get(NumberConstant.ZERO).getDecimalPlace());
+                            profitVo.setPermil(profitReportFormList.get(NumberConstant.ZERO).getPermil());
+                        } else {
+                            profitVo.setDataType(NumberConstant.ONE);
+                            profitVo.setDecimalPlace(NumberConstant.TWO);
+                            profitVo.setPermil(NumberConstant.ONE);
+                        }
                     });
+                    //记录所有月份的科室损益数据
+                    allCostProfitVo.addAll(costProfitVos);
                     Map<Long, List<CostProfitVo>> collect = costProfitVos.stream().collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
                     List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
                     collect.remove(NumberConstant.ZERO_L);
@@ -1871,8 +2151,19 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
                     BatchCostProfitResponse response = new BatchCostProfitResponse();
                     response.setComputeDate(computeDates);
                     response.setProfitVoList(costProfitParentVos);
+                    response.setAllowDrillDown(true);
                     responses.add(response);
                 }
+                //获取月度汇总数据
+                List<CostProfitVo> costProfitVos = generateSummaryForMultipleMonths(allCostProfitVo);
+                if(!CollectionUtils.isEmpty(costProfitVos)){
+                    BatchCostProfitResponse response = new BatchCostProfitResponse();
+                    response.setComputeDate(String.format("%s至%s", beginComputeDate, endComputeDate));
+                    response.setProfitVoList(costProfitVos);
+                    response.setAllowDrillDown(false);
+                    response.setMonthlyAccumulation(true);
+                    responses.add(NumberConstant.ZERO,response);
+                }
                 return responses;
 
             } else {
@@ -1881,6 +2172,69 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         }
     }
 
+    /**
+     * 获取树状结构的科室损益数据
+     * @param costProfitVos
+     * @return
+     */
+    public List<CostProfitVo> getCostProfitVoTree(List<CostProfitVo> costProfitVos){
+        // 参数校验
+        if (CollectionUtils.isEmpty(costProfitVos)) {
+            return Collections.emptyList();
+        }
+        Map<Long, List<CostProfitVo>> collect = costProfitVos.stream().collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
+        List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
+        collect.remove(NumberConstant.ZERO_L);
+        for (CostProfitVo costProfitVo : costProfitParentVos) {
+            List<CostProfitVo> costProfitVo1 = collect.get(costProfitVo.getReportId());
+            if (!CollectionUtils.isEmpty(costProfitVo1)) {
+                costProfitVo.setChildren(setChildren(costProfitVo1, collect));
+            }
+        }
+        if (!CollectionUtils.isEmpty(costProfitParentVos)) {
+            costProfitParentVos.sort(Comparator.comparing(CostProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
+        }
+        return  costProfitParentVos;
+    }
+
+    /**
+     * 生成多月份汇总的科室损益数据
+     *
+     * @param allCostProfitVo 原始数据列表
+     * @return 汇总后的科室损益数据
+     */
+    private List<CostProfitVo> generateSummaryForMultipleMonths(List<CostProfitVo> allCostProfitVo){
+        // 参数校验
+        if (CollectionUtils.isEmpty(allCostProfitVo)) {
+            return Collections.emptyList();
+        }
+        List<CostProfitVo> profitVoList=new ArrayList<>() ;
+        Map<Long, List<CostProfitVo>> reportGroup = allCostProfitVo.stream().collect(Collectors.groupingBy(CostProfitVo::getReportId));
+        reportGroup.forEach((reportId, profitVos) -> {
+            CostProfitVo costProfitVo = BeanUtil.convertObj(profitVos.get(NumberConstant.ZERO), CostProfitVo.class);
+            BigDecimal totalAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal totalBudgetAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getBudgetAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal totalPrevPeriodAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getPrevPeriodAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal totalSamePeriodAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getSamePeriodAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            costProfitVo.setAmount(totalAmount);
+            costProfitVo.setBudgetAmount(totalBudgetAmount);
+            costProfitVo.setPrevPeriodAmount(totalPrevPeriodAmount);
+            costProfitVo.setSamePeriodAmount(totalSamePeriodAmount);
+            costProfitVo.setCompletionRate(calculateRate(costProfitVo.getAmount(), costProfitVo.getBudgetAmount()));
+            costProfitVo.setMomRate(calculateRate(costProfitVo.getAmount().subtract(costProfitVo.getPrevPeriodAmount()), costProfitVo.getPrevPeriodAmount()));
+            costProfitVo.setYoyRate(calculateRate(costProfitVo.getAmount().subtract(costProfitVo.getSamePeriodAmount()), costProfitVo.getSamePeriodAmount()));
+            profitVoList.add(costProfitVo);
+        });
+        //转成树状结构
+        List<CostProfitVo> costProfitVoTree = getCostProfitVoTree(profitVoList);
+        return costProfitVoTree;
+    }
+
+
     @Override
     public Object getRedirectData(String computeDate, Long reportId, Long hospId, String reportType, String responsibilityCode) {
         //查询报表项目
@@ -2167,7 +2521,7 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             throw new CostException("未找到对应分摊层级责任中心");
         }
         //获取授权的责任中心
-        responsibilityList=userResponsibilityRightService.getAuthorizedResponsibility(responsibilityList);
+        responsibilityList = userResponsibilityRightService.getAuthorizedResponsibility(responsibilityList);
         List<Responsibility> responsibilityAllList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda()
                 .eq(Responsibility::getHospId, hospId));
         Map<Long, Responsibility> map = responsibilityAllList.stream().collect(Collectors.toMap(Responsibility::getId, responsibility -> responsibility, (a, b) -> b));
@@ -2201,7 +2555,14 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             if (!CollectionUtils.isEmpty(responsibilities)) {
                 commonResponsibilityReportVo.setChild(getChildTitle(responsibilities, responsibilityGroup));
             } else {
-                commonResponsibilityReportVo.setChild(setDefalutChildList(responsibility));
+                //要显示占比时自动生成默认金额占比层
+                if (IsShowPercent()) {
+                    commonResponsibilityReportVo.setChild(setDefalutChildList(responsibility));
+                } else {
+                    //不显示占比时,直接责任中心作为金额层
+                    commonResponsibilityReportVo.setResponsibilityCode(commonResponsibilityReportVo.getResponsibilityCode() + AMOUNT_FIELD);
+                }
+
             }
             list.add(commonResponsibilityReportVo);
         }
@@ -2531,6 +2892,30 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
 
     }
 
+    /**
+     * 是否显示占比
+     *
+     * @return
+     */
+    public boolean IsShowPercent() {
+        String parameterValue = centerService.getParameterValue(ParameterConstant.SHOW_PERCENT);
+        return NumberConstant.ONE_S.equals(parameterValue);
+    }
+
+    /**
+     * 是否需要代码计算
+     * @return
+     */
+    public boolean IsNeedCalc(String reportType) {
+        String parameterValue = centerService.getParameterValue(ParameterConstant.SQL_CALC_REPORT_TYPE);
+        if(StringUtils.isEmpty(parameterValue)){
+            return true;
+        }
+        //不需计算的报表类型列表
+        List<String> nonCalcReportTypeList = Arrays.stream(parameterValue.split(SplitConstant.SEPARATOR_VERTICALLINE)).collect(Collectors.toList());
+        return !nonCalcReportTypeList.contains(reportType);
+    }
+
     private List<CommonResponsibilityReportVo> getChildTitle(List<Responsibility> responsibilities, Map<Long, List<Responsibility>> responsibilityGroup) {
         List<CommonResponsibilityReportVo> list = new ArrayList<>();
         for (Responsibility responsibility : responsibilities) {
@@ -2542,8 +2927,15 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
             if (!CollectionUtils.isEmpty(responsibilityChild)) {
                 commonResponsibilityReportVo.setChild(getChildTitle(responsibilityChild, responsibilityGroup));
             } else {
-                //添加金额占比
-                commonResponsibilityReportVo.setChild(setDefalutChildList(responsibility));
+//                //添加金额占比
+//                commonResponsibilityReportVo.setChild(setDefalutChildList(responsibility));
+                //要显示占比时自动生成默认金额占比层
+                if (IsShowPercent()) {
+                    commonResponsibilityReportVo.setChild(setDefalutChildList(responsibility));
+                } else {
+                    //不显示占比时,直接责任中心作为金额层
+                    commonResponsibilityReportVo.setResponsibilityCode(commonResponsibilityReportVo.getResponsibilityCode() + AMOUNT_FIELD);
+                }
             }
             list.add(commonResponsibilityReportVo);
         }
@@ -3136,4 +3528,137 @@ public class CostDepartmentProfitServiceImpl extends ServiceImpl<CostDepartmentP
         }
         T.sort(Comparator.comparing(CostResponsibilityProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
     }
+
+    @Override
+    public Object getBatchComputeProfitListByYear(String startYear, String endYear, String reportType, String responsibilityCode) {
+        Integer integerReportType = Integer.valueOf(reportType);
+        Long hospId = UserContext.getHospId();
+        List<BatchCostProfitResponse> responses = new ArrayList<>();
+        // 解析起止年份
+        int startY = Integer.parseInt(startYear);
+        int endY = Integer.parseInt(endYear);
+
+        if (startY > endY) {
+            throw new CostException(500, "起始年份不能大于结束年份");
+        }
+        // 获取报表配置
+        List<ReportForm> reportFormList = reportFormService.list(new QueryWrapper<ReportForm>().lambda()
+                .eq(ReportForm::getHospId, hospId)
+                .eq(ReportForm::getDeleteTime, NumberConstant.ZERO)
+                .eq(ReportForm::getReportType, integerReportType));
+        if (CollUtil.isEmpty(reportFormList)) {
+            throw new CostException(500, "损益表未找到");
+        }
+        Map<Long, ReportForm> reportMap = reportFormList.stream()
+                .collect(Collectors.toMap(ReportForm::getId, report -> report));
+        // 取非隐藏的报表项目ID
+        List<Long> activeReportIdList = reportFormList.stream()
+                .filter(report -> NumberConstant.ONE.equals(report.getHide()))
+                .map(ReportForm::getId)
+                .collect(Collectors.toList());
+
+        // 获取当前年份和上一年度的汇总数据(一次性查询)
+        List<CostProfitVo> summaryList = this.baseMapper.getAnnualSummaryByYearRange(startY, endY, hospId, integerReportType, responsibilityCode);
+        List<CostProfitVo> preYearSummaryList = this.baseMapper.getAnnualSummaryByYearRange(startY - 1, endY - 1, hospId, integerReportType, responsibilityCode);
+
+        // 按年份分组处理数据
+        Map<Integer, List<CostProfitVo>> currentYearMap = new HashMap<>();
+        Map<Integer, List<CostProfitVo>> preYearMap = new HashMap<>();
+
+        // 将结果按年份分组
+        if (!CollectionUtils.isEmpty(summaryList)) {
+            currentYearMap = summaryList.stream()
+                    .collect(Collectors.groupingBy(CostProfitVo::getYear));
+        }
+
+        if (!CollectionUtils.isEmpty(preYearSummaryList)) {
+            preYearMap = preYearSummaryList.stream()
+                    .collect(Collectors.groupingBy(CostProfitVo::getYear));
+        }
+
+        // 遍历每个年份进行数据处理
+        for (int year = startY; year <= endY; year++) {
+            int previousYear = year - 1;
+
+            List<CostProfitVo> currentYearList = currentYearMap.get(year);
+            List<CostProfitVo> previousYearList = preYearMap.get(previousYear);
+
+            if (CollectionUtils.isEmpty(currentYearList)) {
+                continue;
+            }
+
+            // 只处理非隐藏的项目
+            List<CostProfitVo> activeCostProfitVo = currentYearList.stream()
+                    .filter(vo -> activeReportIdList.contains(vo.getReportId()))
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(activeCostProfitVo)) {
+                continue;
+            }
+
+            // 创建映射以便快速查找上一年的数据
+            Map<Long, CostProfitVo> preYearCostProfitMap = new HashMap<>();
+            if (!CollectionUtils.isEmpty(previousYearList)) {
+                preYearCostProfitMap = previousYearList.stream()
+                        .collect(Collectors.toMap(CostProfitVo::getReportId, costProfitVo -> costProfitVo));
+            }
+
+            // 计算完成率、环比、同比
+            for (CostProfitVo vo : currentYearList) {
+                // 设置同期数据
+                if (preYearCostProfitMap.containsKey(vo.getReportId())) {
+                    vo.setSamePeriodAmount(preYearCostProfitMap.get(vo.getReportId()).getAmount());
+                } else {
+                    vo.setSamePeriodAmount(BigDecimal.ZERO);
+                }
+
+                vo.setCompletionRate(calculateRate(vo.getAmount(), vo.getBudgetAmount()));
+                vo.setMomRate(calculateRate(vo.getAmount().subtract(vo.getPrevPeriodAmount()), vo.getPrevPeriodAmount()));
+                vo.setYoyRate(calculateRate(vo.getAmount().subtract(vo.getSamePeriodAmount()), vo.getSamePeriodAmount()));
+
+                // 设置报表相关属性
+                ReportForm reportForm = reportMap.get(vo.getReportId());
+                if (reportForm != null) {
+                    vo.setDescription(reportForm.getDescription());
+                    vo.setSort(reportForm.getSort());
+                    vo.setPermil(reportForm.getPermil());
+                    vo.setDataType(reportForm.getDataType());
+                    vo.setDecimalPlace(reportForm.getDecimalPlace());
+                }
+            }
+
+            // 构建树状结构
+            Map<Long, List<CostProfitVo>> collect = currentYearList.stream()
+                    .collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
+            List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
+            collect.remove(NumberConstant.ZERO_L);
+
+            if (!CollectionUtils.isEmpty(costProfitParentVos)) {
+                costProfitParentVos.forEach(parentVo -> {
+                    parentVo.setChildren(collect.get(parentVo.getReportId()));
+                });
+                costProfitParentVos.sort(Comparator.comparing(CostProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
+            }
+
+            BatchCostProfitResponse response = new BatchCostProfitResponse();
+            response.setComputeDate(String.valueOf(year));
+            response.setProfitVoList(costProfitParentVos);
+            response.setAllowDrillDown(false);
+            responses.add(response);
+        }
+
+        return responses;
+    }
+
+
+    /**
+     * 安全地计算百分比:避免除零错误
+     */
+    private BigDecimal calculateRate(BigDecimal current, BigDecimal base) {
+        if (base == null || base.compareTo(BigDecimal.ZERO) == 0) {
+            return BigDecimal.ZERO;
+        }
+        BigDecimal rate = current.divide(base, 4, RoundingMode.HALF_UP);
+        return rate.setScale(4, RoundingMode.HALF_UP);
+    }
+
 }

+ 591 - 18
src/main/java/com/kcim/service/impl/HospProfitAndLossServiceImpl.java

@@ -11,27 +11,20 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 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.SQLParameter;
+import com.kcim.common.constants.*;
 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 com.kcim.web.reponse.BatchCostProfitResponse;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.fileupload.FileItem;
 import org.apache.commons.fileupload.FileItemFactory;
@@ -42,6 +35,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.commons.CommonsMultipartFile;
 
@@ -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;
     }
 
 
@@ -194,8 +198,18 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
      * @param reportType 报表类型
      */
     @Override
+    public void calcHospProfit(String date, Long hospId, Integer reportType){
+        //需要用代码计算全院损益时
+        if(IsNeedCalc(reportType)){
+            //计算全院损益
+            calcHospProfitAction(date,hospId,reportType);
+            //计算同环比
+            handleSpecificMonthsCalculation(hospId, date, reportType);
+        }
+        execHospProfitSql(date,reportType);
+    }
     @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
-    public void calcHospProfit(String date, Long hospId, Integer reportType) {
+    public void calcHospProfitAction(String date, Long hospId, Integer reportType) {
         List<Responsibility> leafResp = responsibilityService.getLeafResp(hospId);
         DateTime parse = DateUtil.parse(date);
         int year = DateUtil.year(parse);
@@ -378,13 +392,28 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
         // 处理医院其他收支
         List<CostOtherPaymentsData> otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId);
         if (!otherPaymentsDatas.isEmpty()) {
-            otherPaymentsDatas.forEach(ele -> {
+            Optional<ReportForm> firstLossReport = reportForms.stream().filter(f -> NumberConstant.ONE.equals(f.getIsLoss())).findFirst();
+            if(!firstLossReport.isPresent()){
+                throw new CostException("全院损益报表配置有误,未设置损益项目");
+            }
+            Optional<HospProfitAndLoss> firstHospProfitAndLoss = list.stream().filter(f -> firstLossReport.get().getNum().equals(f.getReportNum())).findFirst();
+            if(!firstHospProfitAndLoss.isPresent()){
+                throw new CostException("全院损益报表配置有误,未找到配置的损益项目");
+            }
+            for (CostOtherPaymentsData ele : otherPaymentsDatas) {
                 HospProfitAndLoss loss = new HospProfitAndLoss();
                 loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum((int) (-ele.getId()))
                         .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId)
-                        .setResponsibilityName("全院").setResponsibilityCode("-1").setPercent(null);
+                        .setResponsibilityName("全院").setResponsibilityCode("-1").setPercent(null).setOriginType(NumberConstant.ONE);
+                if( NumberConstant.ONE.equals(ele.getPaymentsType()) ) {
+                    //全院损益项目加上收入
+                    firstHospProfitAndLoss.get().setAmount(firstHospProfitAndLoss.get().getAmount().add(ele.getTotalAmount()));
+                }else{
+                    //全院损益项目减去支出
+                    firstHospProfitAndLoss.get().setAmount(firstHospProfitAndLoss.get().getAmount().subtract(ele.getTotalAmount()));
+                }
                 list.add(loss);
-            });
+            }
         }
         long l = System.currentTimeMillis();
         list.forEach(i -> {
@@ -392,11 +421,247 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
             i.setReportType(reportType);
         });
         this.saveBatch(list);
-        Map<String,String> sqlParameter = new HashMap<>();
 
+    }
+
+    /**
+     * 是否需要代码计算
+     * @return
+     */
+    public boolean IsNeedCalc(Integer reportType) {
+        String parameterValue = centerService.getParameterValue(ParameterConstant.SQL_CALC_REPORT_TYPE);
+        if(StringUtils.isEmpty(parameterValue)){
+            return true;
+        }
+        //不需计算的报表类型列表
+        List<String> nonCalcReportTypeList = Arrays.stream(parameterValue.split(SplitConstant.SEPARATOR_VERTICALLINE)).collect(Collectors.toList());
+        return !nonCalcReportTypeList.contains(String.valueOf(reportType));
+    }
+
+    /**
+     * 处理指定月份及关联月份的计算(当前月、下个月、明年同月)
+     *
+     * @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
+     * @param reportType
+     */
+    public void execHospProfitSql(String date,Integer reportType){
+        Map<String,String> sqlParameter = new HashMap<>();
         sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE, DateUtils.StringToString(date, DateStyleEnum.YYYY_MM));
-        sqlParameter.put(SQLParameter.REPORT_TYPE_CODE,reportType.toString());
+        sqlParameter.put(SQLParameter.REPORT_TYPE_CODE,String.valueOf(reportType));
         sqlService.autoExecuteSql(CustomSqlTypeEnum.HOSP_PROFIT_CALC.getCode(),sqlParameter);
+
     }
 
     // 计算公式中钱
@@ -1295,6 +1560,314 @@ public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossM
 
     }
 
+    @Override
+    public Object getBatchHospProfitList(String beginComputeDate, String endComputeDate, Long hospId, Integer reportType) {
+        List<HospProfitAndLoss> hospProfitAndLossByDateRange = getHospProfitAndLossByDateRange(beginComputeDate, endComputeDate,reportType);
+        if (CollectionUtils.isEmpty(hospProfitAndLossByDateRange)) {
+            throw new CostException(500, "未进行全院损益计算");
+        }
+        //获取全院损益表配置信息
+        List<ReportForm> reportFormList=getReportFormList(hospId,reportType);
+        if (CollectionUtils.isEmpty(reportFormList)) {
+            throw new CostException(500, "未找到全院损益表配置");
+        }
+        //按年-月进行分组
+        Map<String, List<HospProfitAndLoss>> groupedByYearMonth = hospProfitAndLossByDateRange.stream()
+                .collect(Collectors.groupingBy(
+                        item -> item.getDateYear() + "-" + (item.getDateMonth() < 10 ? "0" + item.getDateMonth() : item.getDateMonth())
+                ));
+        List<BatchCostProfitResponse> responses = new ArrayList<>();
+        List<CostProfitVo> allCostProfitVo=new ArrayList<>();
+        //按年-月处理科室损益数据
+        for (Map.Entry<String, List<HospProfitAndLoss>> entry : groupedByYearMonth.entrySet()){
+            List<HospProfitAndLoss> hospProfitAndLossList = entry.getValue();
+            //转换成成本损益数据
+            List<CostProfitVo> costProfitVos = convertToCostProfitVoList(hospProfitAndLossList, reportFormList);
+            //记录所有月份的损益数据
+            allCostProfitVo.addAll(costProfitVos);
+            //生成出参样式
+            BatchCostProfitResponse response = new BatchCostProfitResponse();
+            //第13月为审计月份
+            if(hospProfitAndLossList.get(0).getDateMonth()==13){
+                response.setComputeDate(String.format("%s-%s", hospProfitAndLossList.get(0).getDateYear(), "12A"));
+                response.setAuditMonth(true);
+                response.setMonthlyAccumulation(false);
+            }else {
+                response.setComputeDate(entry.getKey());
+                response.setAuditMonth(false);
+                response.setMonthlyAccumulation(false);
+            }
+            response.setProfitVoList(costProfitVos);
+            response.setAllowDrillDown(false);
+            responses.add(response);
+        }
+        responses.sort(Comparator.comparing(BatchCostProfitResponse::getComputeDate));
+        //起止年月不同时代表有多月份需要处理月累计
+        if(!beginComputeDate.equals(endComputeDate)){
+            //获取月度汇总数据
+            List<CostProfitVo> costProfitVos = generateSummaryForMultipleMonths(allCostProfitVo);
+            if(!CollectionUtils.isEmpty(costProfitVos)){
+                BatchCostProfitResponse response = new BatchCostProfitResponse();
+                response.setComputeDate(String.format("%s至%s", beginComputeDate, endComputeDate));
+                response.setProfitVoList(costProfitVos);
+                response.setAllowDrillDown(false);
+                response.setMonthlyAccumulation(true);
+                response.setAuditMonth(false);
+                responses.add(NumberConstant.ZERO,response);
+            }
+        }
+
+        return responses;
+    }
+
+    /**
+     * 生成多月份汇总的科室损益数据
+     *
+     * @param allCostProfitVo 原始数据列表
+     * @return 汇总后的科室损益数据
+     */
+    private List<CostProfitVo> generateSummaryForMultipleMonths(List<CostProfitVo> allCostProfitVo){
+        // 参数校验
+        if (CollectionUtils.isEmpty(allCostProfitVo)) {
+            return Collections.emptyList();
+        }
+        //排除掉13月的数据
+        List<CostProfitVo> activeCostProfitVo = allCostProfitVo.stream().filter(profitVo -> profitVo.getMonth() != 13).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(activeCostProfitVo)) {
+            return Collections.emptyList();
+        }
+        List<CostProfitVo> profitVoList=new ArrayList<>() ;
+        Map<Long, List<CostProfitVo>> reportGroup = allCostProfitVo.stream().collect(Collectors.groupingBy(CostProfitVo::getReportId));
+        reportGroup.forEach((reportId, profitVos) -> {
+            CostProfitVo costProfitVo = BeanUtil.convertObj(profitVos.get(NumberConstant.ZERO), CostProfitVo.class);
+            BigDecimal totalAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal totalBudgetAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getBudgetAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal totalPrevPeriodAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getPrevPeriodAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal totalSamePeriodAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getSamePeriodAmount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            costProfitVo.setAmount(totalAmount);
+            costProfitVo.setBudgetAmount(totalBudgetAmount);
+            costProfitVo.setPrevPeriodAmount(totalPrevPeriodAmount);
+            costProfitVo.setSamePeriodAmount(totalSamePeriodAmount);
+            costProfitVo.setCompletionRate(calculateProfitRate(costProfitVo.getAmount(), costProfitVo.getBudgetAmount()));
+            costProfitVo.setMomRate(calculateProfitRate(costProfitVo.getAmount().subtract(costProfitVo.getPrevPeriodAmount()), costProfitVo.getPrevPeriodAmount()));
+            costProfitVo.setYoyRate(calculateProfitRate(costProfitVo.getAmount().subtract(costProfitVo.getSamePeriodAmount()), costProfitVo.getSamePeriodAmount()));
+            profitVoList.add(costProfitVo);
+        });
+        //转成树状结构
+        List<CostProfitVo> costProfitVoTree = converToCostProfitVoTree(profitVoList);
+        return costProfitVoTree;
+    }
+
+    /**
+     * 转换为多层级样式全院损益数据
+     * @param hospProfitAndLossList
+     * @param reportFormList
+     * @return
+     */
+    public  List<CostProfitVo> convertToCostProfitVoList(List<HospProfitAndLoss> hospProfitAndLossList,List<ReportForm> reportFormList ){
+        //全院其他收支项目
+        List<HospProfitAndLoss> otherHospProfitAndLoss = hospProfitAndLossList.stream().filter(i -> NumberConstant.ONE.equals(i.getOriginType())).collect(Collectors.toList());
+        //按报表项目编号map
+        Map<Integer, HospProfitAndLoss> hospProfitAndLossMap = hospProfitAndLossList.stream().collect(Collectors.toMap(HospProfitAndLoss::getReportNum, b -> b));
+        //报表项目转换成多层级损益对象
+        List<CostProfitVo> costProfitVos =reportFormList.stream().map(reportForm -> reportFormToProfitVo(reportForm)).collect(Collectors.toList());
+        //组装全院损益及报表项目信息
+        for (CostProfitVo profitVo : costProfitVos) {
+            //处理年月数据
+            profitVo.setYear(hospProfitAndLossList.get(0).getDateYear());
+            profitVo.setMonth(hospProfitAndLossList.get(0).getDateMonth());
+            //添加损益数据
+            HospProfitAndLoss hospProfitAndLoss = hospProfitAndLossMap.get(profitVo.getReportNum());
+            //组装全院损益及报表项目金额信息
+            combineReportAndProfitAmount(profitVo,hospProfitAndLoss);
+        }
+        //有全院其他收支时添加全院其他收支
+        if(!CollectionUtils.isEmpty(otherHospProfitAndLoss)){
+            //找到配置的损益项目
+            Optional<ReportForm> firstLossReport = reportFormList.stream().filter(i -> NumberConstant.ONE.equals(i.getIsLoss())).findFirst();
+            if(!firstLossReport.isPresent()){
+                throw new CostException(500, "全院损益报表配置有误,未设置损益项目");
+            }
+            //生成一个虚拟的全院其他收支
+            CostProfitVo otherProfit = getOtherProfit(reportFormList);
+            //处理年月数据
+            otherProfit.setYear(otherHospProfitAndLoss.get(NumberConstant.ZERO).getDateYear());
+            otherProfit.setMonth(otherHospProfitAndLoss.get(NumberConstant.ZERO).getDateMonth());
+            costProfitVos.add(otherProfit);
+            ReportForm reportForm = firstLossReport.get();
+            for (int i = 0; i < otherHospProfitAndLoss.size(); i++) {
+                HospProfitAndLoss hospProfitAndLoss = otherHospProfitAndLoss.get(i);
+                CostProfitVo costProfitVo =reportFormToProfitVo(reportForm);
+                //组装全院损益及报表项目金额信息
+                combineReportAndProfitAmount(costProfitVo,hospProfitAndLoss);
+                //处理年月数据
+                costProfitVo.setYear(hospProfitAndLoss.getDateYear());
+                costProfitVo.setMonth(hospProfitAndLoss.getDateMonth());
+                //全院其他收支项目放在虚拟的全院其他收支下
+                costProfitVo.setSort(i);
+                costProfitVo.setReportNum(hospProfitAndLoss.getReportNum());
+                costProfitVo.setReportId(hospProfitAndLoss.getReportNum().longValue());
+                costProfitVo.setReportParentId(otherProfit.getReportId());
+                costProfitVos.add(costProfitVo);
+            }
+        }
+        //转成树状结构的损益数据
+        List<CostProfitVo> costProfitVoTree = converToCostProfitVoTree(costProfitVos);
+        return costProfitVoTree;
+    }
+
+    /**
+     * 生成一个虚拟的全院其他收支
+     * @param reportFormList
+     * @return
+     */
+    public CostProfitVo getOtherProfit(List<ReportForm> reportFormList) {
+        //全院其他收支
+        CostProfitVo hospProfitVO = reportFormToProfitVo(reportFormList.get(NumberConstant.ZERO));
+        hospProfitVO.setId(-1L);
+        hospProfitVO.setReportId(-1L);
+        hospProfitVO.setReportNum(-1);
+        hospProfitVO.setReportParentId(0L);
+        hospProfitVO.setReportName("全院其他收支");
+        hospProfitVO.setSort(reportFormList.size());
+        return hospProfitVO;
+    }
+
+    /**
+     * 报表项目转换成多层级损益对象
+     * @param reportForm
+     * @return
+     */
+    public CostProfitVo reportFormToProfitVo(ReportForm reportForm){
+        CostProfitVo profitVo = new CostProfitVo();
+        profitVo.setReportId(reportForm.getId());
+        profitVo.setReportName(reportForm.getReportName());
+        profitVo.setReportParentId(reportForm.getParentId());
+        profitVo.setReportNum(reportForm.getNum());
+        profitVo.setCalcType(reportForm.getCalcType());
+        profitVo.setCalcFormula(reportForm.getCalcFormula());
+        profitVo.setCostType(reportForm.getCostType());
+        profitVo.setShareType(reportForm.getReportType());
+        profitVo.setSort(reportForm.getSort());
+        profitVo.setDescription(reportForm.getDescription());
+        profitVo.setDataType(reportForm.getDataType());
+        profitVo.setDecimalPlace(reportForm.getDecimalPlace());
+        profitVo.setPermil(reportForm.getPermil());
+        return profitVo;
+    }
+
+    /**
+     * 组装全院损益及报表项目的金额信息
+     * @param profitVo
+     * @param hospProfitAndLoss
+     */
+    public void combineReportAndProfitAmount(CostProfitVo profitVo, HospProfitAndLoss hospProfitAndLoss) {
+        if (ObjectUtils.isEmpty(hospProfitAndLoss)) {
+            profitVo.setAmount(BigDecimal.ZERO);
+            profitVo.setSamePeriodAmount(BigDecimal.ZERO);
+            profitVo.setPrevPeriodAmount(BigDecimal.ZERO);
+            profitVo.setBudgetAmount(BigDecimal.ZERO);
+            profitVo.setYoyRate(BigDecimal.ZERO);
+            profitVo.setMomRate(BigDecimal.ZERO);
+            profitVo.setCompletionRate(BigDecimal.ZERO);
+        } else {
+            profitVo.setAmount(hospProfitAndLoss.getAmount());
+            profitVo.setSamePeriodAmount(hospProfitAndLoss.getSamePeriodAmount());
+            profitVo.setPrevPeriodAmount(hospProfitAndLoss.getPrevPeriodAmount());
+            profitVo.setBudgetAmount(hospProfitAndLoss.getBudgetAmount());
+            profitVo.setYoyRate(hospProfitAndLoss.getYoyRate());
+            profitVo.setMomRate(hospProfitAndLoss.getMomRate());
+            profitVo.setCompletionRate(hospProfitAndLoss.getCompletionRate());
+        }
+    }
+
+    /**
+     * 转成树状结构的损益数据
+     * @param costProfitVos
+     * @return
+     */
+    public List<CostProfitVo> converToCostProfitVoTree(List<CostProfitVo> costProfitVos){
+        // 参数校验
+        if (CollectionUtils.isEmpty(costProfitVos)) {
+            return Collections.emptyList();
+        }
+        Map<Long, List<CostProfitVo>> collect = costProfitVos.stream().collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
+        List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
+        collect.remove(NumberConstant.ZERO_L);
+        for (CostProfitVo costProfitVo : costProfitParentVos) {
+            List<CostProfitVo> costProfitVo1 = collect.get(costProfitVo.getReportId());
+            if (!CollectionUtils.isEmpty(costProfitVo1)) {
+                costProfitVo.setChildren(setChildrenProfitVo(costProfitVo1, collect));
+            }
+        }
+        if (!CollectionUtils.isEmpty(costProfitParentVos)) {
+            costProfitParentVos.sort(Comparator.comparing(CostProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
+        }
+        return  costProfitParentVos;
+    }
+
+    private List<CostProfitVo> setChildrenProfitVo(List<CostProfitVo> child, Map<Long, List<CostProfitVo>> collect) {
+        for (CostProfitVo costProfitVo : child) {
+            if (!CollectionUtils.isEmpty(collect.get(costProfitVo.getReportId()))) {
+                costProfitVo.setChildren(setChildrenProfitVo(collect.get(costProfitVo.getReportId()), collect));
+            }
+        }
+        if (!CollectionUtils.isEmpty(child)) {
+            child.sort(Comparator.comparing(CostProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
+        }
+        return child;
+    }
+
+    /**
+     * 获取全院损益表配置信息
+     * @param hospId
+     * @param reportType
+     * @return
+     */
+    public List<ReportForm> getReportFormList(Long hospId,Integer reportType ) {
+        List<ReportForm> reportFormList = reportFormService.list(new QueryWrapper<ReportForm>().lambda()
+                .eq(ReportForm::getHospId, hospId)
+                .eq(ReportForm::getReportType, reportType)
+                .eq(ReportForm::getDeleteTime , NumberConstant.ZERO)
+                .eq(ReportForm::getHide, NumberConstant.ONE));
+        return  reportFormList;
+    }
+
+
+    /**
+     * 获取指定年月区间的全院损益数据
+     * @param beginComputeDate
+     * @param endComputeDate
+     * @return
+     */
+    public List<HospProfitAndLoss> getHospProfitAndLossByDateRange(String beginComputeDate, String endComputeDate, Integer reportType) {
+        // 解析开始和结束时间
+        int startYear = Integer.parseInt(beginComputeDate.split("-")[0]);
+        int startMonth = Integer.parseInt(beginComputeDate.split("-")[1]);
+        int endYear = Integer.parseInt(endComputeDate.split("-")[0]);
+        int endMonth = Integer.parseInt(endComputeDate.split("-")[1]);
+
+        if( startYear * 100 + startMonth> endYear * 100 + endMonth){
+            throw new CostException("开始时间不能大于结束时间");
+        }
+        //如果截止月份是12月要自动添加第13月的数据
+        if(endMonth==12)
+        {
+            endMonth=13;
+        }
+        List<HospProfitAndLoss> list = this.list(new QueryWrapper<HospProfitAndLoss>().lambda()
+                .eq(HospProfitAndLoss::getDeleteTime, NumberConstant.ZERO)
+                .eq(HospProfitAndLoss::getHospId, UserContext.getHospId())
+                .eq(HospProfitAndLoss::getReportType, reportType)
+                .apply("(date_year * 100 + date_month) >= {0}", startYear * 100 + startMonth)
+                .apply("(date_year * 100 + date_month) <= {0}", endYear * 100 + endMonth));
+        return list;
+    }
+
     private static @NotNull HospProfitVO getHospProfitVO(List<HospProfitAndLoss> hospAllList, int i,int reportType) {
         HospProfitAndLoss hospProfitAndLoss = hospAllList.get(i);
         HospProfitVO hospProfitVO1 = new HospProfitVO();

+ 228 - 9
src/main/java/com/kcim/service/impl/KpiComputeImportServiceImpl.java

@@ -18,15 +18,14 @@ import com.kcim.service.CenterService;
 import com.kcim.service.KpiComputeImportService;
 import com.kcim.vo.CommonTitleVo;
 import com.kcim.vo.DictDataVo;
+import com.kcim.vo.SheetImportResultVO;
 import com.kcim.vo.UserInfoVO;
+import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FileUtils;
 import org.apache.ibatis.jdbc.SqlRunner;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.CellType;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -39,6 +38,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
@@ -168,7 +169,7 @@ public class KpiComputeImportServiceImpl implements KpiComputeImportService {
             assert fileName != null;
             File file1 = new File(fileName);
             FileUtils.copyInputStreamToFile(file.getInputStream(), file1);
-            String excelSql = readExcelSql(file1, tableName, id);
+            String excelSql = readExcelSql(file1, tableName, id, computeDate);
             SqlRunner sqlRunner = new SqlRunner(getConnection());
             sqlRunner.insert(excelSql);
 
@@ -240,6 +241,220 @@ public class KpiComputeImportServiceImpl implements KpiComputeImportService {
         }
     }
 
+
+    @Override
+    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
+    public List<SheetImportResultVO> importMultipleSheets(String computeDate, MultipartFile file) {
+        List<CommonTitleVo> tableList = (List<CommonTitleVo>) getTableList();
+        String fileName = file.getOriginalFilename();
+        String uploadFileUrl = uploadFile(file, fileName);
+        List<SheetImportResultVO> sheetResults = new ArrayList<>();
+        try {
+            // 使用系统临时目录显式创建临时文件
+            File tempDir = new File(System.getProperty("java.io.tmpdir"));
+            File tempFile = new File(tempDir, "excel-" + UUID.randomUUID() + ".tmp");
+
+            // 确保文件存在并正确关闭流
+            try (InputStream inputStream = file.getInputStream()) {
+                Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            }
+
+            try (Workbook workbook = WorkbookFactory.create(tempFile)) {
+                for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
+                    Sheet sheet = workbook.getSheetAt(i);
+                    String sheetName = sheet.getSheetName();
+
+                    SheetImportResultVO resultItem = new SheetImportResultVO();
+                    resultItem.setSheetName(sheetName);
+                    resultItem.setSheetIndex(i);
+
+                    Optional<CommonTitleVo> mapping = tableList.stream()
+                            .filter(t -> t.getName().equals(sheetName))
+                            .findFirst();
+
+                    if (!mapping.isPresent()) {
+                        log.warn("未找到sheet页 '{%s}' 对应的数据表配置,跳过导入", sheetName);
+                        resultItem.setSuccess(false);
+                        resultItem.setErrorMessage("未找到对应的数据库表配置");
+                        sheetResults.add(resultItem);
+                        continue;
+                    }
+
+                    String tableName = mapping.get().getCode();
+                    resultItem.setTableName(tableName);
+
+                    try {
+                        KpiComputeImport kpiComputeImport = new KpiComputeImport();
+                        kpiComputeImport.setComputeDate(computeDate);
+                        kpiComputeImport.setImportStatus(NumberConstant.ZERO);
+                        kpiComputeImport.setCreateTime(new Date());
+                        kpiComputeImport.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId()));
+                        kpiComputeImport.setUrl(uploadFileUrl);
+                        kpiComputeImport.setTableName(tableName);
+                        kpiComputeImport.setFileName(String.format("%s_%s", fileName, sheetName));
+                        kpiComputeImport.setHospId(UserContext.getCurrentLoginHospId());
+                        kpiComputeImport.setDelFlag(NumberConstant.ZERO);
+
+                        repository.save(kpiComputeImport);
+
+                        insertExcelDataForSheet(kpiComputeImport.getId(), tableName, file, fileName, computeDate, sheet);
+
+                        resultItem.setSuccess(true);
+                        resultItem.setImportId(kpiComputeImport.getId());
+                        resultItem.setImportTime(kpiComputeImport.getCreateTime());
+
+                    } catch (Exception e) {
+                        log.error(String.format("sheet页 '%s' 导入失败: %s", sheetName, e.getMessage()));
+                        resultItem.setSuccess(false);
+                        resultItem.setErrorMessage("导入失败: " + e.getMessage());
+                    }
+                    sheetResults.add(resultItem);
+                }
+            } finally {
+                // 删除临时文件
+                if (tempFile.exists()) {
+                    tempFile.delete();
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("导入发生异常: " + e.getMessage(), e);
+        }
+        return sheetResults;
+    }
+
+
+    @SneakyThrows
+    private void insertExcelDataForSheet(Integer id, String tableName, MultipartFile file, String fileName, String computeDate, Sheet sheet) {
+        // 将MultipartFile转换为InputStream
+        InputStream inputStream = file.getInputStream();
+
+        // 这里假设有一个方法可以从给定的InputStream和sheet对象中读取SQL语句
+        List<String> excelSql = readExcelSqlFromSheet(inputStream, sheet, tableName, id, computeDate);
+
+        // 执行SQL插入操作
+        SqlRunner sqlRunner = new SqlRunner(getConnection());
+        for (String sql : excelSql) {
+            sqlRunner.insert(sql);
+        }
+
+        // 更新新增人和时间
+        String updateSql = "UPDATE `" + tableName + "` SET " +
+                "`create_user` = '" + UserContext.getCurrentUser().getId() + "', " +
+                "`create_time` = '" + DateUtils.formatDate2String(new Date()) + "' " +
+                "WHERE `import_id` = '" + id + "';";
+        sqlRunner.run(updateSql);
+    }
+
+    private static final int BATCH_SIZE = 500; // 每批次插入的记录数
+
+    private List<String> readExcelSqlFromSheet(InputStream inputStream, Sheet sheet, String tableName, Integer id, String computeDate) throws IOException {
+        List<String> sqlList = new ArrayList<>();
+        boolean firstRow = true;
+        Row headerRow = null;
+        DataFormatter dataFormatter = new DataFormatter();
+        int rowCount = 0;
+        boolean hasComputeDateField = false;
+
+        // 第一次遍历获取字段信息并判断是否有 compute_date 字段
+        for (Row row : sheet) {
+            if (rowCount == 0) {
+                headerRow = row;
+                // 检查字段中是否包含 compute_date
+                for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                    Cell headerCell = headerRow.getCell(i);
+                    if (headerCell != null && "compute_date".equals(headerCell.getStringCellValue())) {
+                        hasComputeDateField = true;
+                        break;
+                    }
+                }
+                rowCount++;
+                continue;
+            }
+            break; // 只需要读取第一行判断字段
+        }
+
+        // 第二次遍历处理数据行
+        StringBuilder currentBatchValues = new StringBuilder();
+        int batchCount = 0;
+        // 新增变量用于记录实际数据行数
+        int actualRowCount = 0;
+
+        for (Row row : sheet) {
+            if (actualRowCount < 2) {
+                actualRowCount++;
+                continue; // 跳过前两行(标题行 + 说明行)
+            }
+
+            StringBuilder valueSb = new StringBuilder();
+            valueSb.append("(");
+
+            for (int i = 0; i < row.getLastCellNum(); i++) {
+                if (i > 0) {
+                    valueSb.append(", ");
+                }
+                Cell cell = row.getCell(i);
+                if (cell == null) {
+                    valueSb.append("NULL");
+                    continue;
+                }
+                cell.setCellType(CellType.STRING);
+                valueSb.append("'").append(cell.getStringCellValue().replace("'", "''")).append("'");
+            }
+
+            // 添加 import_id 和 hosp_id
+            valueSb.append(", ").append(id)
+                    .append(", ").append(UserContext.getCurrentLoginHospId());
+
+            // 如果字段中没有 compute_date,才手动添加
+            if (!hasComputeDateField) {
+                valueSb.append(", '").append(computeDate).append("'");
+            }
+
+            valueSb.append(")");
+
+            if (batchCount == 0) {
+                // 构建字段名部分
+                currentBatchValues.setLength(0); // 清空之前的缓存
+                currentBatchValues.append("INSERT INTO `").append(tableName).append("` (");
+
+                for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                    if (i > 0) {
+                        currentBatchValues.append(", ");
+                    }
+                    Cell headerCell = headerRow.getCell(i);
+                    currentBatchValues.append("`").append(headerCell.getStringCellValue()).append("`");
+                }
+
+                currentBatchValues.append(", `import_id`, `hosp_id`");
+                if (!hasComputeDateField) {
+                    currentBatchValues.append(", `compute_date`");
+                }
+                currentBatchValues.append(") VALUES ");
+            } else {
+                currentBatchValues.append(", ");
+            }
+
+            currentBatchValues.append(valueSb.toString());
+            batchCount++;
+
+            // 如果达到批次大小,保存当前批次并重置
+            if (batchCount >= BATCH_SIZE) {
+                sqlList.add(currentBatchValues.toString());
+                batchCount = 0;
+            }
+
+            actualRowCount++; // 使用独立变量追踪实际行数
+        }
+
+        // 添加最后一批未满的数据
+        if (batchCount > 0) {
+            sqlList.add(currentBatchValues.toString());
+        }
+
+        return sqlList;
+    }
+
+
     /**
      * 复原导入数据
      *
@@ -273,7 +488,7 @@ public class KpiComputeImportServiceImpl implements KpiComputeImportService {
         }
     }
 
-    public static String readExcelSql(File file, String tableName, Integer id) throws IOException {
+    public static String readExcelSql(File file, String tableName, Integer id, String computeDate) throws IOException {
         // 获取excel工作簿对象
         String sql = null;
         String fileName = file.getName();
@@ -315,7 +530,7 @@ public class KpiComputeImportServiceImpl implements KpiComputeImportService {
                     i++;
                 }
                 //组装记录表和hospId
-                setCommonData(id, dataList);
+                setCommonData(id, dataList, computeDate);
                 sql = insertFromMap(dataList, tableName);
 
             }
@@ -356,17 +571,21 @@ public class KpiComputeImportServiceImpl implements KpiComputeImportService {
                     }
                     i++;
                 }
-                setCommonData(id, dataList);
+                setCommonData(id, dataList, computeDate);
                 sql = insertFromMap(dataList, tableName);
             }
         }
         return sql;
     }
 
-    private static void setCommonData(Integer id, List<Map<String, Object>> dataList) {
+    private static void setCommonData(Integer id, List<Map<String, Object>> dataList, String computeDate) {
         if (!CollectionUtils.isEmpty(dataList)) {
             dataList.forEach(stringObjectMap -> {
                 stringObjectMap.put("hosp_id", UserContext.getCurrentLoginHospId());
+                //Excel字段里没有传核算年月的,用传入的核算年月
+                if (!stringObjectMap.containsKey("compute_date")) {
+                    stringObjectMap.put("compute_date", computeDate);
+                }
                 stringObjectMap.put("import_id", id);
             });
         }

+ 27 - 13
src/main/java/com/kcim/service/impl/ProjectCostServiceImpl.java

@@ -257,16 +257,23 @@ public class ProjectCostServiceImpl implements ProjectCostService {
      */
     @Override
     public void computeItemCost(String computeDate){
-        //根据参数判断是否走新的计算流程
-        if(NumberConstant.ONE_S.equals(centerService.getParameterValue(ParameterConstant.USER_HOSP_ITEM_CALC))){
-            //项目成本分摊计算
-            calcItemCostAllocation(computeDate);
-        }else{
-            computeItemCostACtion(computeDate);
+        String parameterValue = centerService.getParameterValueByIndex(ParameterConstant.ITEM_CALC_TYPE,NumberConstant.ONE);
+        //走系统计算逻辑
+        if(StringUtils.isEmpty(parameterValue)|| NumberConstant.ONE_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)){
+            //根据参数判断是否走新的计算流程
+            if(NumberConstant.ONE_S.equals(centerService.getParameterValue(ParameterConstant.USER_HOSP_ITEM_CALC))){
+                //项目成本分摊计算
+                calcItemCostAllocation(computeDate);
+            }else{
+                computeItemCostACtion(computeDate);
+            }
+        }
+        //走自定义SQL计算逻辑
+        if(NumberConstant.TWO_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            log.info("项目成本分摊计算完成开始执行后续处理脚本");
+            //执行后续处理脚本
+            execItemCostSQL(computeDate);
         }
-        log.info("项目成本分摊计算完成开始执行后续处理脚本");
-        //执行后续处理脚本
-        execItemCostSQL(computeDate);
     }
 
     /**
@@ -1246,10 +1253,17 @@ public class ProjectCostServiceImpl implements ProjectCostService {
      */
     @Override
     public void computeStandItemCost(String computeDate){
-        //标准项目成本分摊计算
-        computeStandItemCostAction(computeDate);
-        //执行标准法项目成本分摊计算后续处理脚本
-        execStandItemCostSQL(computeDate);
+        String parameterValue = centerService.getParameterValueByIndex(ParameterConstant.ITEM_CALC_TYPE,NumberConstant.THREE);
+        //走系统计算逻辑
+        if(StringUtils.isEmpty(parameterValue)|| NumberConstant.ONE_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            //标准项目成本分摊计算
+            computeStandItemCostAction(computeDate);
+        }
+        //走自定义SQL计算逻辑
+        if(NumberConstant.TWO_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            //执行标准法项目成本分摊计算后续处理脚本
+            execStandItemCostSQL(computeDate);
+        }
     }
 
     /**

+ 7 - 0
src/main/java/com/kcim/service/impl/ReportFormServiceImpl.java

@@ -69,6 +69,13 @@ public class ReportFormServiceImpl extends ServiceImpl<ReportFormMapper, ReportF
             } else if (i.getCalcType() == 5) {
                 reportFormVO.setShowAddRelation(3);
             }
+            if (i.getDataType() == 1) {
+                reportFormVO.setDataTypeName("数值");
+            } else if (i.getDataType() == 2) {
+                reportFormVO.setDataTypeName("百分比");
+            } else {
+                reportFormVO.setDataTypeName("未知");
+            }
             return reportFormVO;
         }).collect(Collectors.toList());
 

+ 27 - 13
src/main/java/com/kcim/service/impl/ShareParamServiceImpl.java

@@ -150,15 +150,22 @@ public class ShareParamServiceImpl implements ShareParamService {
      */
     @Override
     public void computeShareParamCost(String computeDate){
-        //根据参数判断是否走新的计算流程
-        if(NumberConstant.ONE_S.equals(centerService.getParameterValue(ParameterConstant.USER_HOSP_ITEM_CALC))){
-            //完全法项目分摊参数计算
-            calcShareParamCost(computeDate);
-        }else{
-            computeShareParamCostAction(computeDate);
-        }
-        //执行完全法项目分摊参数计算后续处理脚本
-        execShareParamCostSQL(computeDate);
+        String parameterValue = centerService.getParameterValueByIndex(ParameterConstant.ITEM_CALC_TYPE,NumberConstant.ZERO);
+        //走系统计算逻辑
+        if(StringUtils.isEmpty(parameterValue)|| NumberConstant.ONE_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            //根据参数判断是否走新的计算流程
+            if (NumberConstant.ONE_S.equals(centerService.getParameterValue(ParameterConstant.USER_HOSP_ITEM_CALC))) {
+                //完全法项目分摊参数计算
+                calcShareParamCost(computeDate);
+            } else {
+                computeShareParamCostAction(computeDate);
+            }
+        }
+        //走自定义SQL计算逻辑
+        if(NumberConstant.TWO_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            //执行完全法项目分摊参数计算后续处理脚本
+            execShareParamCostSQL(computeDate);
+        }
     }
 
     /**
@@ -820,10 +827,17 @@ public class ShareParamServiceImpl implements ShareParamService {
      */
     @Override
     public void computeStandShareParamCost(String computeDate){
-        //标准法项目分摊参数计算
-        computeStandShareParamCostAction(computeDate);
-        //执行标准法项目分摊参数计算后续处理脚本
-        execStandShareParamCostSQL(computeDate);
+        String parameterValue = centerService.getParameterValueByIndex(ParameterConstant.ITEM_CALC_TYPE,NumberConstant.TWO);
+        //走系统计算逻辑
+        if(StringUtils.isEmpty(parameterValue)|| NumberConstant.ONE_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            //标准法项目分摊参数计算
+            computeStandShareParamCostAction(computeDate);
+        }
+        //走自定义SQL计算逻辑
+        if(NumberConstant.TWO_S.equals(parameterValue)||NumberConstant.THREE_S.equals(parameterValue)) {
+            //执行标准法项目分摊参数计算后续处理脚本
+            execStandShareParamCostSQL(computeDate);
+        }
     }
 
     /**

+ 14 - 0
src/main/java/com/kcim/vo/CommonTitleReportVo.java

@@ -20,6 +20,20 @@ public class CommonTitleReportVo {
     private String reportName;
 
     private Integer sort;
+    /**
+     * 数据类型:0未知 1数值 2百分比
+     */
+    private Integer dataType;
+
+    /**
+     * 小数位
+     */
+    private Integer decimalPlace;
+
+    /**
+     * 是否有千分号
+     */
+    private Integer permil;
 
     private List<CommonTitleReportVo> childTitle;
 

+ 45 - 0
src/main/java/com/kcim/vo/CostProfitVo.java

@@ -104,4 +104,49 @@ public class CostProfitVo implements Serializable {
 
 	private List<ReportVo> data;
 
+	/**
+	 * 数据类型:0未知 1数值 2百分比
+	 */
+	private Integer dataType;
+
+	/**
+	 * 小数位
+	 */
+	private Integer decimalPlace;
+
+	/**
+	 * 是否有千分号
+	 */
+	private Integer permil;
+
+	/**
+	 * 预算金额
+	 */
+	private BigDecimal budgetAmount;
+
+	/**
+	 * 上期金额
+	 */
+	private BigDecimal prevPeriodAmount;
+
+	/**
+	 * 同期金额
+	 */
+	private BigDecimal samePeriodAmount;
+
+	/**
+	 * 完成率
+	 */
+	private BigDecimal completionRate;
+
+	/**
+	 * 环比
+	 */
+	private BigDecimal momRate;
+
+	/**
+	 * 同比
+	 */
+	private BigDecimal yoyRate;
+
 }

+ 15 - 0
src/main/java/com/kcim/vo/HospProfitVO.java

@@ -91,4 +91,19 @@ public class HospProfitVO {
     private String description;
 
     private List<HospProfitVO> child;
+
+    /**
+     * 数据类型:0未知 1数值 2百分比
+     */
+    private Integer dataType;
+
+    /**
+     * 小数位
+     */
+    private Integer decimalPlace;
+
+    /**
+     * 是否有千分号
+     */
+    private Integer permil;
 }

+ 39 - 0
src/main/java/com/kcim/vo/PeriodInfoVO.java

@@ -0,0 +1,39 @@
+package com.kcim.vo;
+
+import lombok.Data;
+
+/**
+ *
+ */
+@Data
+public class PeriodInfoVO {
+    /**
+     * 当前记录的年份
+     */
+    private Integer year;
+
+    /**
+     * 当前记录的月份
+     */
+    private Integer month;
+
+    /**
+     * 上期记录的年份
+     */
+    private Integer prevYear;
+
+    /**
+     * 上期记录的月份
+     */
+    private Integer prevMonth;
+
+    /**
+     * 同期记录的年份(去年)
+     */
+    private Integer lastYear;
+
+    /**
+     * 同期记录的月份(去年同期)
+     */
+    private Integer lastMonth;
+}

+ 20 - 0
src/main/java/com/kcim/vo/ReportFormVO.java

@@ -55,6 +55,26 @@ public class ReportFormVO {
 
     private Integer hide;
 
+    /**
+     * 数据类型:0未知 1数值 2百分比
+     */
+    private Integer dataType;
+
+    /**
+     * 数据类型名称
+     */
+    private String dataTypeName;
+
+    /**
+     * 小数位
+     */
+    private Integer decimalPlace;
+
+    /**
+     * 是否有千分号
+     */
+    private Integer permil;
+
     /**
      * 报表与诊次床日关联的状态
      */

+ 23 - 0
src/main/java/com/kcim/vo/ShareTargetMapVo.java

@@ -0,0 +1,23 @@
+package com.kcim.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 分摊目标责任中心绑定对象
+ * @program:
+ * @description:
+ * @author: Wang.YS
+ * @create: 2023-11-01 21:25
+ **/
+@Data
+public class ShareTargetMapVo {
+
+    @ApiModelProperty(name = "accountShareId",value = "分摊参数对应ID")
+    private Long accountShareId;
+
+    @ApiModelProperty(name = "targetRespCodeList",value = "目标责任中心代码")
+    private List<String> targetRespCodeList;
+}

+ 19 - 0
src/main/java/com/kcim/vo/SheetImportResultVO.java

@@ -0,0 +1,19 @@
+package com.kcim.vo;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 每个sheet页导入结果VO
+ */
+@Data
+public class SheetImportResultVO {
+    private String sheetName;
+    private Integer sheetIndex;
+    private String tableName;
+    private Integer importId;
+    private Date importTime;
+    private Boolean success;
+    private String errorMessage;
+}

+ 25 - 1
src/main/java/com/kcim/web/CostAccountShareController.java

@@ -10,9 +10,10 @@ import com.kcim.dao.model.dto.AccountShareCopyDto;
 import com.kcim.dao.model.dto.CostAccountShareEditDto;
 import com.kcim.dao.model.dto.CostAccountShareSaveDto;
 import com.kcim.dao.model.dto.ShareParamEditDto;
+import com.kcim.service.CostAccountShareService;
 import com.kcim.vo.CostShareParamStatusVO;
 import com.kcim.vo.ShareParamProportionVO;
-import com.kcim.service.CostAccountShareService;
+import com.kcim.vo.ShareTargetMapVo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -152,4 +153,27 @@ public class CostAccountShareController {
         List<Responsibility> responsibilityList= costAccountShareService.getResponsibilityCalType(accountShareId,hospId);
         return Result.ok(responsibilityList);
     }
+
+    /**
+     * 同步分摊参数设置
+     * @param direction 1主表向明细表同步 2明细表向主表同步
+     * @return
+     */
+    @PostMapping("/synCostAccountShare")
+    @ApiOperation("同步分摊参数设置")
+    public Result synCostAccountShare(@RequestParam(defaultValue = "1") Integer direction,
+                                      @RequestParam(required = false) Long accountShareId){
+        costAccountShareService.synCostAccountShare(direction,accountShareId);
+        return Result.ok();
+    }
+
+    /**
+     * 进行分摊参数设置
+     */
+    @PostMapping("/applyAccountShareTarget")
+    @ApiOperation("添加分摊参数设置对应的目标责任中心")
+    public Result applyAccountShareTarget(@RequestBody ShareTargetMapVo shareTargetMapVo){
+        costAccountShareService.applyAccountShareTarget(shareTargetMapVo);
+        return Result.ok();
+    }
 }

+ 11 - 0
src/main/java/com/kcim/web/CostDepartmentProfitController.java

@@ -179,6 +179,17 @@ public class CostDepartmentProfitController {
         return  Result.ok(costDepartmentProfitService.getBatchComputeProfitList(beginComputeDate,endComputeDate,hospId,reportType,responsibilityCode));
     }
 
+    @GetMapping("/getBatchComputeProfitListByYear")
+    @ApiOperation("多年份损益报表计算列表")
+    public Result getBatchComputeProfitListByYear(@RequestParam String startYear,
+                                            @RequestParam String endYear,
+                                            @RequestParam String reportType,
+                                            @RequestParam String responsibilityCode){
+        return  Result.ok(costDepartmentProfitService.getBatchComputeProfitListByYear(startYear,endYear,reportType,responsibilityCode));
+    }
+
+
+
     @GetMapping("/getRedirectData")
     @ApiOperation("下钻数据列表")
     public Result getRedirectData(@RequestParam @ApiParam(name = "date", value = "yyyy-MM") String computeDate,

+ 9 - 0
src/main/java/com/kcim/web/HospProfitAndLossController.java

@@ -55,6 +55,15 @@ public class HospProfitAndLossController extends AbstractController {
         return  Result.ok(hospProfitAndLossService.getHospProfitList(computeDate,hospId,reportType));
     }
 
+    @GetMapping("/getBatchHospProfitList")
+    @ApiOperation("多月份全院损益列表")
+    public Result getBatchComputeProfitList(@RequestParam @ApiParam(name = "date", value = "yyyy-MM") String beginComputeDate,
+                                            @RequestParam String endComputeDate,
+                                            @RequestParam Integer reportType){
+        Long hospId = UserContext.getHospId();
+        return  Result.ok(hospProfitAndLossService.getBatchHospProfitList(beginComputeDate,endComputeDate,hospId,reportType));
+    }
+
 
     @ApiOperation("全院损益计算输出")
     @PostMapping("/report")

+ 11 - 0
src/main/java/com/kcim/web/PublicImportController.java

@@ -2,6 +2,7 @@ package com.kcim.web;
 
 import com.kcim.common.util.Result;
 import com.kcim.service.KpiComputeImportService;
+import com.kcim.vo.SheetImportResultVO;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
@@ -9,6 +10,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.util.List;
+
 /**
  * @program: CostAccount
  * @description:
@@ -61,4 +64,12 @@ public class PublicImportController {
         service.recoverImport(id);
         return Result.ok();
     }
+
+    @ApiOperation("批量导入数据")
+    @PostMapping("importMultipleSheets")
+    public Result importMultipleSheets(@RequestPart("computeDate")String computeDate,
+                             @RequestPart("file") MultipartFile file){
+        List<SheetImportResultVO> sheetImportResultVOS = service.importMultipleSheets(computeDate, file);
+        return Result.ok(sheetImportResultVOS);
+    }
 }

+ 20 - 0
src/main/java/com/kcim/web/reponse/BatchCostProfitResponse.java

@@ -14,6 +14,26 @@ import java.util.List;
 @Data
 public class BatchCostProfitResponse {
 
+    /**
+     * 核算年月
+     */
     private String computeDate;
+    /**
+     * 损益数据列表
+     */
     private List<CostProfitVo> profitVoList;
+    /**
+     * 是否允许下钻
+     */
+    private boolean allowDrillDown=false;
+
+    /**
+     * 是否月累计
+     */
+    private boolean isMonthlyAccumulation=false;
+
+    /**
+     * 是否审计月
+     */
+    private boolean isAuditMonth=false;
 }

+ 5 - 5
src/main/resources/application-dev.yml

@@ -24,8 +24,8 @@ spring:
       discovery:
         service: ${spring.application.name}
         server-addr: 120.27.235.181:8848
-        #        namespace: 38b4fbe9-4a20-48c3-a8bf-ebf069fb26e8
-        #        group: KCIM
+#        namespace: 38b4fbe9-4a20-48c3-a8bf-ebf069fb26e8
+#        group: KCIM
         namespace: 060cc0fe-193f-4a94-bbca-6d48a4f95ac2
         group: dev
     sentinel:
@@ -98,10 +98,10 @@ sa-token:
   is-read-cookie: false
   is-read-body: false
   ##token 有效期 默认30天,到期强制登录
-  timeout: 2592000
+  timeout: -1
   ## 30分钟无操作就失效 不适用
-  activity-timeout: 1800
-  # 配置 Sa-Token 单独使用的 Redis 连接
+  activity-timeout: -1
+  #配置 Sa-Token 单独使用的 Redis 连接
   alone-redis:
     # Redis数据库索引(默认为0) 先固定2,后续有钱开另外的单独的db
     database: 1

+ 37 - 0
src/main/resources/mapper/AllocationMapper.xml

@@ -83,5 +83,42 @@
         group by share_level_id
     </select>
 
+    <select id="getLevelSortAllocation" resultType="com.kcim.dao.model.Allocation">
+        SELECT
+            date_year,
+            date_month,
+            level_sort,
+            level_name,
+            responsibility_code,
+            responsibility_name,
+            account_share_id,
+            account_code,
+            account_name,
+            is_base_cost,
+            SUM( amount ) AS amount,
+            target_responsibility_code,
+            target_responsibility_name,
+            share_param_code,
+            share_param_name,
+            SUM( total_amount ) AS total_amount,
+            share_param_value_num,
+            share_param_rate,
+            share_level_id,
+            target_share_level_id
+        FROM
+            cost_allocation
+        WHERE
+            date_year = #{year}
+          AND date_month = #{month}
+          AND hosp_id = #{hospId}
+          AND delete_time = 0
+          AND share_level_id = #{shareLevelId}
+        GROUP BY
+            responsibility_code,
+            target_responsibility_code,
+            account_share_id,
+            share_param_code
+    </select>
+
 
 </mapper>

+ 57 - 1
src/main/resources/mapper/CostDepartmentProfitMapper.xml

@@ -39,6 +39,62 @@
           and hosp_id = #{hospId}
           and `year` = #{year}
     </select>
+    <select id="getAnnualSummaryByYear" parameterType="map" resultType="com.kcim.vo.CostProfitVo">
+        SELECT
+            `year`,
+            `month`,
+            report_parent_id as reportParentId,
+            report_id as reportId,
+            report_num as reportNum,
+            report_name as reportName,
+            calc_formula as calcFormula,
+            calc_type as calcType,
+            responsibility_code as responsibilityCode,
+            responsibility_name as responsibilityName,
+            cost_type as costType,
+            income_type as incomeType,
+            share_type as shareType,
+            percent,
+            SUM(budget_amount) AS budget_amount,
+            SUM(amount) AS amount,
+            SUM(prev_period_amount) AS prev_period_amount,
+            SUM(same_period_amount) AS same_period_amount
+        FROM cost_department_profit
+        WHERE year = #{year}
+          AND hosp_id = #{hospId}
+          AND share_type = #{reportType}
+          AND responsibility_code = #{responsibilityCode}
+          AND delete_time = 0
+        GROUP BY report_id
+        ORDER BY report_num
+    </select>
 
-
+    <select id="getAnnualSummaryByYearRange" parameterType="map" resultType="com.kcim.vo.CostProfitVo">
+        SELECT
+            `year`,
+            report_parent_id as reportParentId,
+            report_id as reportId,
+            report_num as reportNum,
+            report_name as reportName,
+            calc_formula as calcFormula,
+            calc_type as calcType,
+            responsibility_code as responsibilityCode,
+            responsibility_name as responsibilityName,
+            cost_type as costType,
+            income_type as incomeType,
+            share_type as shareType,
+            percent,
+            SUM(budget_amount) AS budget_amount,
+            SUM(amount) AS amount,
+            SUM(prev_period_amount) AS prev_period_amount,
+            SUM(same_period_amount) AS same_period_amount
+        FROM cost_department_profit
+        WHERE year BETWEEN #{startYear} AND #{endYear}
+          AND hosp_id = #{hospId}
+          AND share_type = #{reportType}
+          AND responsibility_code = #{responsibilityCode}
+          AND delete_time = 0
+        GROUP BY `year`, report_id
+        ORDER BY `year`, report_num
+    </select>
 </mapper>

+ 4 - 1
src/main/resources/mapper/CostReportFormMapper.xml

@@ -66,7 +66,10 @@
                create_time,
                delete_time,
                description,
-               hide
+               hide,
+               data_type,
+               decimal_place,
+               permil
         from cost_report_form
         where hosp_id = #{hospId}
           and report_type = #{reportType}