zqy 1 日 前
コミット
404e116d53

+ 691 - 0
src/com/kingdee/eas/custom/compensation/service/CalSubmitSchemeFunctionServiceEx.java

@@ -0,0 +1,691 @@
+package com.kingdee.eas.custom.compensation.service;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.eas.util.app.DbUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+import com.kingdee.shr.compensation.service.CalSubmitSchemeFunctionService;
+
+import com.kingdee.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+
+/**
+ * 薪酬方案计算函数服务扩展类。
+ * <p>
+ * 提供以下薪酬计算相关的扩展函数:
+ * <ul>
+ *   <li>当月入职/离职/转正人数统计</li>
+ *   <li>月末净增人数(入职-离职)</li>
+ *   <li>获取适用薪酬方案名称</li>
+ *   <li>获取薪酬档案中的固定薪资项目值</li>
+ * </ul>
+ */
+public class CalSubmitSchemeFunctionServiceEx extends CalSubmitSchemeFunctionService {
+    private static final Logger logger = LoggerFactory.getLogger(CalSubmitSchemeFunctionServiceEx.class);
+
+
+    /**
+     * 人才留存奖金
+     * @param ctx
+     * @param paramMap
+     * @param tempMap
+     * @param mainTableName
+     * @param mainTableRowFilter
+     * @return
+     */
+    public double getTalentRetentionBonus(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter,double isFixed) {
+        try {
+            //todo
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+            String Effect = getEffect(ctx, tempMap, mainTableName, mainTableRowFilter, id);
+
+            StringBuilder sql = new StringBuilder();
+            sql.append(" /*dialect*/ ");
+            // 主查询:按核算表和生效月份分组统计入职人数
+            sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
+            // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次入职记录
+            sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
+            sql.append(" from CT_REC_ApplicantBeisen ab ");
+            sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
+            sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
+            // 取每个人员最近一次入职记录(ROW_NUMBER + rn=1)
+            sql.append(" left join ( ");
+            sql.append("         SELECT FPERSONID, FBIZDATE ");
+            sql.append(" FROM ( ");
+            sql.append("         SELECT ");
+            sql.append("         e.FPERSONID, ");
+            sql.append("         e.FBIZDATE, ");
+            sql.append("         ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
+            sql.append("         FROM T_HR_EmpEnrollBizBillEntry e ");
+            sql.append("         LEFT JOIN T_HR_EmpEnrollBizBill b ON b.fid = e.FBillID ");
+            sql.append("         where b.FBillState=3 ");
+            sql.append("  ) t ");
+            sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
+            // 入职日期在生效日期当月范围内:[当月第一天, 下月第一天)
+            sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)");              // 当月第一天 00:00:00.000
+            sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) ");           // 下个月第一天 00:00:00.000
+            sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
+            IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
+            //fixPayMap = new HashMap<>();
+            while (rs.next()) {
+                //获取人数
+                String fpersonid = rs.getString("fpersonid");
+                String FEffectDate = rs.getString("FEffectDate");
+                int pNum = rs.getInt("pNum");
+                //fixPayMap.put(fpersonid + "_" + FEffectDate, pNum);
+            }
+        }catch (Exception e){
+            logger.error(e.getMessage(),e);
+        }
+        return 0D;
+    }
+
+
+    /**
+     * 当月净增人数 = 当月入职人数 - 当月离职人数(通过招聘渠道入职但当月即离职的人员统计)。
+     * @param ctx                上下文
+     * @param paramMap           参数(含 FPERSONID、FID)
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @return 当月净增人数
+     */
+    public double getEndMonthRetainedUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
+        try {
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+            String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
+            // 获取当月入职人数
+            double ruzhiNum = getMonthRuzhiUsers(ctx, paramMap, tempMap, mainTableName, mainTableRowFilter);
+            // 获取当月离职人数
+            double lizhiNum = getMonthLiZhiUsers(ctx, paramMap, tempMap, mainTableName, mainTableRowFilter);
+            // 净增 = 入职 - 离职
+            return ((double) ruzhiNum - lizhiNum);
+        } catch (Exception ex) {
+            logger.error(ex.getMessage(), ex);
+            //ex.printStackTrace();
+        }
+        return (double) 0.0F;
+    }
+    /**
+     * 当月入职人数(通过招聘渠道入职且在生效日期当月入职的人数)。
+     * @param ctx                上下文
+     * @param paramMap           参数(含 FPERSONID、FID)
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @return 当前人员在生效日期对应的当月入职人数
+     */
+    public double getMonthRuzhiUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
+        try {
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+            String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
+            // 入职人数缓存 key
+            String ruZhiMapKey = "getMonthRuzhiUsers_Map";
+            // 查询生效日期当月入职的人数
+            getRuZhiPersonNum(ctx, tempMap, mainTableName, mainTableRowFilter, ruZhiMapKey);
+
+
+            Map<String, Integer> fixPayMap = (Map) tempMap.get(ruZhiMapKey);
+
+            String key = personId + "_" + Effect;
+            if (fixPayMap.containsKey(key)) {
+                int num = fixPayMap.containsKey(key) ? fixPayMap.get(key) : 0;
+                return (double) (num);
+            } else {
+                return (double) 0.0F;
+            }
+
+        } catch (Exception ex) {
+            logger.error(ex.getMessage(), ex);
+            //ex.printStackTrace();
+        }
+        return (double) 0.0F;
+    }
+    /**
+     * 当月转正人数(通过招聘渠道入职且在生效日期当月转正的人数)。
+     * @param ctx                上下文
+     * @param paramMap           参数(含 FPERSONID、FID)
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @return 当前人员在生效日期对应的当月转正人数
+     */
+    public double getMonthZhuanZhengUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
+        try {
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+            String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
+            // 转正人数缓存 key
+            String zhuanZhengMapKey = "getMonthZhuanZhengUsers_Map";
+            // 查询生效日期当月转正的人数
+            getZhuanZhengPersonNum(ctx, tempMap, mainTableName, mainTableRowFilter, zhuanZhengMapKey);
+            Map<String, Integer> fixPayMap = (Map) tempMap.get(zhuanZhengMapKey);
+            String key = personId + "_" + Effect;
+            if (fixPayMap.containsKey(key)) {
+                int num = fixPayMap.containsKey(key) ? fixPayMap.get(key) : 0;
+                return (double) (num);
+            } else {
+                return (double) 0.0F;
+            }
+
+        } catch (Exception ex) {
+            logger.error(ex.getMessage(), ex);
+            //ex.printStackTrace();
+        }
+        return (double) 0.0F;
+    }
+
+    /**
+     * 当月离职人数(通过招聘渠道入职且在生效日期当月离职的人数)。
+     * @param ctx                上下文
+     * @param paramMap           参数(含 FPERSONID、FID)
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @return 当前人员在生效日期对应的当月离职人数
+     */
+    public double getMonthLiZhiUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
+        try {
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+            String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
+            // 离职人数缓存 key
+            String liZhiMapKey = "getMonthLiZhiUsers_Map";
+            // 查询生效日期当月离职的人数
+            getLiZhiPersonNum(ctx, tempMap, mainTableName, mainTableRowFilter, liZhiMapKey);
+            Map<String, Integer> fixPayMap = (Map) tempMap.get(liZhiMapKey);
+            String key = personId + "_" + Effect;
+            if (fixPayMap.containsKey(key)) {
+                int num = fixPayMap.containsKey(key) ? fixPayMap.get(key) : 0;
+                return (double) (num);
+            } else {
+                return (double) 0.0F;
+            }
+
+        } catch (Exception ex) {
+            logger.error(ex.getMessage(), ex);
+            //ex.printStackTrace();
+        }
+        return (double) 0.0F;
+    }
+
+
+    /**
+     * 查询生效日期当月离职的人数,按"人员ID_生效年月"为 key 缓存到 tempMap。
+     * <p>
+     * 通过 CT_REC_ApplicantBeisen(北森应聘表)关联 T_HR_RESIGNBIZBILL(离职单据),
+     * 统计招聘渠道入职的员工在生效日期当月离职的人数。
+     * @param ctx                上下文
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @param lzZhiMapKey        缓存 key
+     * @throws BOSException 数据库异常
+     * @throws SQLException SQL异常
+     */
+    private static void getLiZhiPersonNum(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String lzZhiMapKey) throws BOSException, SQLException {
+        Map<String, Integer> lzFixPayMap = (Map) tempMap.get(lzZhiMapKey);
+        if(lzFixPayMap==null){
+            StringBuilder sql = new StringBuilder();
+            sql.append(" /*dialect*/ ");
+            // 主查询:按核算表和生效月份分组统计离职人数
+            sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
+            // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次离职记录
+            sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
+            sql.append(" from CT_REC_ApplicantBeisen ab ");
+            sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
+            sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
+            // 取每个人员最近一次离职记录(ROW_NUMBER + rn=1)
+            sql.append(" left join ( ");
+            sql.append("         SELECT FPERSONID, FBIZDATE ");
+            sql.append(" FROM ( ");
+            sql.append("         SELECT ");
+            sql.append("         e.FPERSONID, ");
+            sql.append("         e.FBIZDATE, ");
+            sql.append("         ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
+            sql.append("         FROM T_HR_RESIGNBIZBILLENTRY e ");
+            sql.append("         LEFT JOIN T_HR_RESIGNBIZBILL b ON b.fid = e.FBillID ");
+            sql.append("         where b.FBillState=3 ");
+            sql.append("  ) t ");
+            sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
+            // 离职日期在生效日期当月范围内:[当月第一天, 下月第一天)
+            sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)");              // 当月第一天 00:00:00.000
+            sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) ");           // 下个月第一天 00:00:00.000
+            sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
+            IRowSet rs = DbUtil.executeQuery(ctx,sql.toString());
+            lzFixPayMap = new HashMap<>();
+            while (rs.next()) {
+                //获取人数
+                String fpersonid = rs.getString("fpersonid");
+                String FEffectDate = rs.getString("FEffectDate");
+                int pNum = rs.getInt("pNum");
+                lzFixPayMap.put(fpersonid+"_"+FEffectDate,pNum);
+            }
+            tempMap.put(lzZhiMapKey, lzFixPayMap);
+        }
+    }
+    /**
+     * 查询生效日期当月入职的人数,按"人员ID_生效年月"为 key 缓存到 tempMap。
+     * <p>
+     * 通过 CT_REC_ApplicantBeisen(北森应聘表)关联 T_HR_EmpEnrollBizBill(入职单据),
+     * 统计招聘渠道入职的员工在生效日期当月入职的人数。
+     * @param ctx                上下文
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @param ruZhiMapKey        缓存 key
+     * @throws BOSException 数据库异常
+     * @throws SQLException SQL异常
+     */
+    private static void getRuZhiPersonNum(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String ruZhiMapKey) throws BOSException, SQLException {
+        Map<String, Integer> fixPayMap = (Map) tempMap.get(ruZhiMapKey);
+        if(fixPayMap==null){
+            StringBuilder sql = new StringBuilder();
+            sql.append(" /*dialect*/ ");
+            // 主查询:按核算表和生效月份分组统计入职人数
+            sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
+            // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次入职记录
+            sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
+            sql.append(" from CT_REC_ApplicantBeisen ab ");
+            sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
+            sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
+            // 取每个人员最近一次入职记录(ROW_NUMBER + rn=1)
+            sql.append(" left join ( ");
+            sql.append("         SELECT FPERSONID, FBIZDATE ");
+            sql.append(" FROM ( ");
+            sql.append("         SELECT ");
+            sql.append("         e.FPERSONID, ");
+            sql.append("         e.FBIZDATE, ");
+            sql.append("         ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
+            sql.append("         FROM T_HR_EmpEnrollBizBillEntry e ");
+            sql.append("         LEFT JOIN T_HR_EmpEnrollBizBill b ON b.fid = e.FBillID ");
+            sql.append("         where b.FBillState=3 ");
+            sql.append("  ) t ");
+            sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
+            // 入职日期在生效日期当月范围内:[当月第一天, 下月第一天)
+            sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)");              // 当月第一天 00:00:00.000
+            sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) ");           // 下个月第一天 00:00:00.000
+            sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
+            IRowSet rs = DbUtil.executeQuery(ctx,sql.toString());
+            fixPayMap = new HashMap<>();
+            while (rs.next()) {
+                //获取人数
+                String fpersonid = rs.getString("fpersonid");
+                String FEffectDate = rs.getString("FEffectDate");
+                int pNum = rs.getInt("pNum");
+                fixPayMap.put(fpersonid+"_"+FEffectDate,pNum);
+            }
+            tempMap.put(ruZhiMapKey, fixPayMap);
+        }
+    }
+
+    /**
+     * 查询生效日期当月转正的人数,按"人员ID_生效年月"为 key 缓存到 tempMap。
+     * <p>
+     * 通过 CT_REC_ApplicantBeisen(北森应聘表)关联 T_HR_EMPHIREBIZBILL(转正单据),
+     * 统计招聘渠道入职的员工在生效日期当月转正的人数。
+     * @param ctx                上下文
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @param ruZhiMapKey        缓存 key
+     * @throws BOSException 数据库异常
+     * @throws SQLException SQL异常
+     */
+    private static void getZhuanZhengPersonNum(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String ruZhiMapKey) throws BOSException, SQLException {
+        Map<String, Integer> fixPayMap = (Map) tempMap.get(ruZhiMapKey);
+        if(fixPayMap==null){
+            StringBuilder sql = new StringBuilder();
+            sql.append(" /*dialect*/ ");
+            // 主查询:按核算表和生效月份分组统计转正人数
+            sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
+            // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次转正记录
+            sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
+            sql.append(" from CT_REC_ApplicantBeisen ab ");
+            sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
+            sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
+            // 取每个人员最近一次转正记录(ROW_NUMBER + rn=1)
+            sql.append(" left join ( ");
+            sql.append("         SELECT FPERSONID, FBIZDATE ");
+            sql.append(" FROM ( ");
+            sql.append("         SELECT ");
+            sql.append("         e.FPERSONID, ");
+            sql.append("         e.FBIZDATE,e.FEnrollDate, ");
+            sql.append("         ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
+            sql.append("         FROM T_HR_EMPHIREBIZBILLENTRY e ");
+            sql.append("         LEFT JOIN T_HR_EMPHIREBIZBILL b ON b.fid = e.FBillID ");
+            sql.append("         where b.FBillState=3 ");
+            sql.append("  ) t ");
+            sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
+            // 转正日期在生效日期当月范围内:[当月第一天, 下月第一天)
+            sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)");              // 当月第一天 00:00:00.000
+            sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) ");           // 下个月第一天 00:00:00.000
+            sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
+            IRowSet rs = DbUtil.executeQuery(ctx,sql.toString());
+            fixPayMap = new HashMap<>();
+            while (rs.next()) {
+                //获取人数
+                String fpersonid = rs.getString("fpersonid");
+                String FEffectDate = rs.getString("FEffectDate");
+                int pNum = rs.getInt("pNum");
+                fixPayMap.put(fpersonid+"_"+FEffectDate,pNum);
+            }
+            tempMap.put(ruZhiMapKey, fixPayMap);
+        }
+    }
+
+
+    /**
+     * 获取生效日期的月份(yyyy-MM 格式)。
+     * <p>
+     * 优先从 tempMap 中以 "getEffect_" + id 为 key 查找缓存;
+     * 缓存未命中时,一次性批量查询 mainTableRowFilter 范围内所有行的 FEffectDate,
+     * 逐行以 "getEffect_" + fid 为 key 写入 tempMap,后续同一批次其他行直接命中缓存。
+     * @param ctx                上下文
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件(用于批量加载)
+     * @param id                 当前数据行 FID
+     * @return 生效日期对应的 yyyy-MM 格式月份字符串,若未查到则为空字符串
+     * @throws BOSException 数据库异常
+     * @throws SQLException SQL异常
+     */
+    private static String getEffect(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String id) throws BOSException, SQLException {
+        String cacheKey = "getEffect_" + id;
+        if (tempMap.containsKey(cacheKey)) {
+            return (String) tempMap.get(cacheKey);
+        }
+        IRowSet rowSet = DbUtil.executeQuery(ctx, " SELECT mt.fid,mt.fpersonid,mt.FEffectDate from " + mainTableName + " mt where mt.fid in("+mainTableRowFilter+")");
+        String Effect = "";
+        while (rowSet.next()) {
+            String fid = rowSet.getString("fid");
+            LocalDate effectDate = rowSet.getDate("FEffectDate").toLocalDate();
+            Effect = effectDate.format(DateTimeFormatter.ofPattern("yyyy-MM"));
+            String Key = "getEffect_" + fid;
+            tempMap.put(Key, Effect);
+        }
+
+        return (String) tempMap.getOrDefault(cacheKey, "");
+    }
+
+
+
+
+
+
+
+
+
+    /**
+     * 获取适用薪酬方案名称(fname_l2)。
+     * <p>
+     * 通过计算表关联 T_HR_SBatchSubmitShemeBill 和 T_HR_SCalSubmitScheme,
+     * 查询当前计算范围对应的薪酬方案名称,结果以"name"为 key 缓存。
+     * @param ctx                上下文
+     * @param paramMap           参数(含 FPERSONID、FID)
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @return 方案名称,未找到则返回空字符串
+     */
+    public String getApplicablePlan(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
+        try {
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+            //根据计算范围初始化薪酬项目信息
+            String fixPayMapKey = "getApplicablePlan_SchemeName";
+
+            if (tempMap.get(fixPayMapKey) == null) {
+                StringBuilder sql = new StringBuilder();
+                sql.append(" select sss.fname_l2 from "+mainTableName+" sbe ");
+                sql.append(" left join T_HR_SBatchSubmitShemeBill ssb on ssb.fid=sbe.FBillID ");
+                sql.append(" left join T_HR_SCalSubmitScheme sss on sss.fid=ssb.FSubmitSchemeID ");
+                sql.append(" where sbe.fid in(" + mainTableRowFilter + ")");
+                IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
+                if (rs.next()) {
+                    Map<String, String> objectMap = new HashMap<>();
+                    objectMap.put("name", rs.getString("fname_l2"));
+                    tempMap.put(fixPayMapKey, objectMap);
+                }
+            }
+            //获取薪酬项目值
+            Map<String, String> fixPayMap = (Map) tempMap.get(fixPayMapKey);
+            String key = "name";
+            if (fixPayMap.containsKey(key)) {
+                String schemeName = fixPayMap.get(key);
+
+                return schemeName;
+            } else {
+                return "";
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            //e.printStackTrace();
+            return "";
+        }
+    }
+
+    /**
+     * 获取薪酬档案中指定薪资项目的金额。
+     * <p>
+     * 根据人员 ID 和薪酬项目名称,从 T_HR_SFIXADJUSTSALARY 表中查询固定薪酬数据。
+     * 结果以 "personId_cmpItem" 为 key 缓存到 tempMap,后续同一计算范围直接复用。
+     * @param ctx                上下文
+     * @param paramMap           参数(含 FPERSONID、FID)
+     * @param tempMap            临时缓存 Map
+     * @param mainTableName      计算表对象名
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @param cmpItem            薪酬项目名称(对应 T_HR_SCMPITEM.FNAME_L2)
+     * @param isFixed            是否固定薪酬(0=按FEffectDate匹配, 非0=按FEffectDate匹配;预留扩展)
+     * @return 薪资项目金额,若人员或项目不存在则返回 0.0
+     */
+    public double getItemSalary(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter,
+                                String cmpItem, double isFixed) {
+        try {
+            //计算的员工id
+            String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
+            //计算的数据行
+            String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
+
+            //根据计算范围初始化薪酬项目信息
+            String fixPayMapKey = "getItemSalary_fixPayMap_" + isFixed;
+            if (!StringUtils.isEmpty(cmpItem) && personId != null) {
+                if (tempMap.get(fixPayMapKey) == null) {
+                    tempMap.put(fixPayMapKey, this.initFixPayForMoneyMap(ctx, mainTableName, isFixed, mainTableRowFilter));
+                }
+                //获取薪酬项目值
+                Map<String, BigDecimal> fixPayMap = (Map) tempMap.get(fixPayMapKey);
+                String key = personId + "_" + cmpItem;
+                if (fixPayMap.containsKey(key)) {
+                    BigDecimal money = fixPayMap.containsKey(key) ? (BigDecimal) fixPayMap.get(key) : new BigDecimal(0);
+                    double pay = money.doubleValue();
+                    return pay;
+                } else {
+                    return (double) 0.0F;
+                }
+            } else {
+                return (double) 0.0F;
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            //e.printStackTrace();
+            return (double) 0.0F;
+        }
+    }
+
+    /**
+     * 初始化固定薪酬数据 Map,以 "personId_cmpItem" 为 key,金额为 value。
+     * <p>
+     * 查询 T_HR_SFIXADJUSTSALARY(固定调整薪酬表),按生效日期匹配核算表中的薪酬项目。
+     * 查询结果按 type 优先级排序:type=1(相同组织)优先于 type=0(无组织限制),
+     * 同一 key 仅保留首次出现的记录(优先级最高的)。
+     * @param ctx                上下文
+     * @param mainTableName      计算表对象名
+     * @param isFixed            是否固定薪酬(0=按FEffectDate匹配, 非0=按FEffectDate匹配;预留扩展)
+     * @param mainTableRowFilter 计算表数据行过滤条件
+     * @return key 为 "personId_cmpItem",value 为对应金额的 Map
+     * @throws Exception 数据库异常
+     */
+    public Map<String, BigDecimal> initFixPayForMoneyMap(Context ctx, String mainTableName, double isFixed, String mainTableRowFilter) throws Exception {
+
+        StringBuilder sb = new StringBuilder();
+        String effectDayField = "";
+        String key = null;
+        String personId = "";
+        String cmpItem = "";
+        Map<String, BigDecimal> fixPayMap = new HashMap();
+        Set<String> set = new HashSet();
+        if (isFixed == (double) 0.0F) {
+            effectDayField = "calTable.FEffectDate";
+        } else {
+            effectDayField = "calTable.FEffectDate";
+        }
+
+        sb.append(" SELECT DISTINCT CALTABLE.FPERSONID as personId, FIX.FMONEY MONEY, FIX.FCURRENCYID, ");
+        sb.append(" CMPITEM.FNAME_L2 CMPITEM,FIX.FEFFECTDAY, ");
+        // type: 0=组织为空(通用), 1=组织匹配(优先), 2=组织不匹配(排除)
+        sb.append(" ( case when (fix.fpayhrorgunitid is null or fix.fpayhrorgunitid in ('')) then 0  when ( calTable.FHrOrgUnitID= fix.fpayhrorgunitid) then 1 else 2 end ) as type").append("\r\n");
+        sb.append(" FROM " + mainTableName + " CALTABLE ");
+        sb.append(" INNER JOIN T_HR_SFIXADJUSTSALARY FIX ON CALTABLE.FPERSONID=FIX.FPERSONID AND (FIX.FCurrencyID = CALTABLE.FCurrencyID OR FIX.FCurrencyID IS NULL)");
+        sb.append(" LEFT JOIN T_HR_SCMPITEM CMPITEM ON CMPITEM.FID=FIX.FCMPITEMID ");
+        sb.append(" WHERE FIX.feffectday <= ").append(effectDayField).append(" AND FIX.fleffectday >= ").append(effectDayField);
+        sb.append(" AND CALTABLE.fid in(").append(mainTableRowFilter).append(")");
+
+        IRowSet rs = DbUtil.executeQuery(ctx, sb.toString());
+        List<Map<String, Object>> dataList = this.getDataFromExecuteQuery(rs);
+        Iterator<Map<String, Object>> it = dataList.iterator();
+        while (it.hasNext()) {
+            Map<String, Object> data = (Map) it.next();
+            personId = (String) data.get("personId");
+            cmpItem = (String) data.get("CMPITEM");
+            key = personId + "_" + cmpItem;
+            if (!set.contains(key)) {
+                set.add(key);
+                BigDecimal money = (BigDecimal) data.get("MONEY");
+                fixPayMap.put(key, money);
+            }
+        }
+        return fixPayMap;
+    }
+
+    /**
+     * 将 IRowSet 查询结果按优先级排序后转换为 List&lt;Map&gt;。
+     * <p>
+     * 排序规则:type=1(相同组织)的记录优先,type=0(无组织限制)的次之,type=2(组织不匹配)的排除。
+     * 同一人员同一薪酬项目的 key 只保留首次出现(即优先级最高)的记录。
+     * @param executeQuery 查询结果集
+     * @return 按优先级排序后的数据列表
+     * @throws SQLException SQL异常
+     */
+    public List<Map<String, Object>> getDataFromExecuteQuery(IRowSet executeQuery) throws SQLException {
+        Map<String, Object> map = null;
+        List<Map<String, Object>> dataList = null;
+        Map<String, List<Map<String, Object>>> mapList = null;
+        Map<String, List<Map<String, Object>>> mapListOne = null;
+        Map<String, List<Map<String, Object>>> mapListZero = null;
+        List<Map<String, Object>> list = null;
+        List<Map<String, Object>> oneList = null;
+        List<Map<String, Object>> zeroList = null;
+        if (null != executeQuery) {
+            String personId = null;
+            dataList = new ArrayList();
+            mapList = new HashMap();
+            mapListOne = new HashMap();
+            mapListZero = new HashMap();
+
+            while (executeQuery.next()) {
+                map = this.executeQueryToMap(executeQuery);
+                personId = executeQuery.getString("personId");
+                int type = executeQuery.getInt("type");
+                if (mapList.containsKey(personId)) {
+                    list = (List) mapList.get(personId);
+                    list.add(map);
+                } else {
+                    list = new ArrayList();
+                    list.add(map);
+                    mapList.put(personId, list);
+                }
+
+                // type=1: 组织匹配,优先使用
+                if (type == 1) {
+                    if (mapListOne.containsKey(personId)) {
+                        oneList = (List) mapListOne.get(personId);
+                        oneList.add(map);
+                    } else {
+                        oneList = new ArrayList();
+                        oneList.add(map);
+                        mapListOne.put(personId, oneList);
+                    }
+                // type=0: 无组织限制,次优
+                } else if (type == 0) {
+                    if (mapListZero.containsKey(personId)) {
+                        zeroList = (List) mapListZero.get(personId);
+                        zeroList.add(map);
+                    } else {
+                        zeroList = new ArrayList();
+                        zeroList.add(map);
+                        mapListZero.put(personId, zeroList);
+                    }
+                }
+                // type=2(组织不匹配)排除,不加入结果
+            }
+
+            List<Map<String, Object>> var21 = null;
+
+            // 合并结果:先加入 type=1(组织匹配),再加入 type=0(无组织限制)
+            // 后续去重逻辑会保留首次出现的记录,从而实现优先级
+            for (String str : mapList.keySet()) {
+                List<Map<String, Object>> var22 = (List) mapList.get(str);
+                if (mapListOne.containsKey(str)) {
+                    dataList.addAll((Collection) mapListOne.get(str));
+                }
+
+                if (mapListZero.containsKey(str)) {
+                    dataList.addAll((Collection) mapListZero.get(str));
+                }
+            }
+        }
+
+        return (List<Map<String, Object>>) (null != dataList && !dataList.isEmpty() ? dataList : new ArrayList(0));
+    }
+
+    /**
+     * 将 IRowSet 当前行转换为 Map,提取固定薪酬相关字段。
+     * @param executeQuery 查询结果集(已定位到某一行)
+     * @return 包含 personId、MONEY、FCURRENCYID、CMPITEM、FEFFECTDAY 的 Map
+     * @throws SQLException SQL异常
+     */
+    public Map<String, Object> executeQueryToMap(IRowSet executeQuery) throws SQLException {
+        Map<String, Object> map = new HashMap();
+        map.put("personId", executeQuery.getString("personId"));
+        map.put("MONEY", executeQuery.getBigDecimal("MONEY") == null ? new BigDecimal(0) : executeQuery.getBigDecimal("MONEY"));
+        map.put("FCURRENCYID", executeQuery.getString("FCURRENCYID"));
+        map.put("CMPITEM", executeQuery.getString("CMPITEM"));
+        map.put("FEFFECTDAY", executeQuery.getDate("FEFFECTDAY"));
+        return map;
+    }
+}