CalSubmitSchemeFunctionServiceEx.java 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. package com.kingdee.eas.custom.compensation.service;
  2. import com.kingdee.bos.BOSException;
  3. import com.kingdee.bos.Context;
  4. import com.kingdee.eas.util.app.DbUtil;
  5. import com.kingdee.jdbc.rowset.IRowSet;
  6. import com.kingdee.shr.compensation.service.CalSubmitSchemeFunctionService;
  7. import com.kingdee.util.StringUtils;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import java.math.BigDecimal;
  11. import java.sql.Date;
  12. import java.sql.SQLException;
  13. import java.time.LocalDate;
  14. import java.time.format.DateTimeFormatter;
  15. import java.util.*;
  16. /**
  17. * 薪酬方案计算函数服务扩展类。
  18. * <p>
  19. * 提供以下薪酬计算相关的扩展函数:
  20. * <ul>
  21. * <li>当月入职/离职/转正人数统计</li>
  22. * <li>月末净增人数(入职-离职)</li>
  23. * <li>获取适用薪酬方案名称</li>
  24. * <li>获取薪酬档案中的固定薪资项目值</li>
  25. * </ul>
  26. */
  27. public class CalSubmitSchemeFunctionServiceEx extends CalSubmitSchemeFunctionService {
  28. private static final Logger logger = LoggerFactory.getLogger(CalSubmitSchemeFunctionServiceEx.class);
  29. /**
  30. * 人才留存奖金
  31. * @param ctx
  32. * @param paramMap
  33. * @param tempMap
  34. * @param mainTableName
  35. * @param mainTableRowFilter
  36. * @return
  37. */
  38. public double getTalentRetentionBonus(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter,double isFixed) {
  39. try {
  40. //todo
  41. //计算的员工id
  42. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  43. //计算的数据行
  44. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  45. String Effect = getEffect(ctx, tempMap, mainTableName, mainTableRowFilter, id);
  46. StringBuilder sql = new StringBuilder();
  47. sql.append(" /*dialect*/ ");
  48. // 主查询:按核算表和生效月份分组统计入职人数
  49. sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
  50. // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次入职记录
  51. sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
  52. sql.append(" from CT_REC_ApplicantBeisen ab ");
  53. sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
  54. sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
  55. // 取每个人员最近一次入职记录(ROW_NUMBER + rn=1)
  56. sql.append(" left join ( ");
  57. sql.append(" SELECT FPERSONID, FBIZDATE ");
  58. sql.append(" FROM ( ");
  59. sql.append(" SELECT ");
  60. sql.append(" e.FPERSONID, ");
  61. sql.append(" e.FBIZDATE, ");
  62. sql.append(" ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
  63. sql.append(" FROM T_HR_EmpEnrollBizBillEntry e ");
  64. sql.append(" LEFT JOIN T_HR_EmpEnrollBizBill b ON b.fid = e.FBillID ");
  65. sql.append(" where b.FBillState=3 ");
  66. sql.append(" ) t ");
  67. sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
  68. // 入职日期在生效日期当月范围内:[当月第一天, 下月第一天)
  69. sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)"); // 当月第一天 00:00:00.000
  70. sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) "); // 下个月第一天 00:00:00.000
  71. sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
  72. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  73. //fixPayMap = new HashMap<>();
  74. while (rs.next()) {
  75. //获取人数
  76. String fpersonid = rs.getString("fpersonid");
  77. String FEffectDate = rs.getString("FEffectDate");
  78. int pNum = rs.getInt("pNum");
  79. //fixPayMap.put(fpersonid + "_" + FEffectDate, pNum);
  80. }
  81. }catch (Exception e){
  82. logger.error(e.getMessage(),e);
  83. }
  84. return 0D;
  85. }
  86. /**
  87. * 当月净增人数 = 当月入职人数 - 当月离职人数(通过招聘渠道入职但当月即离职的人员统计)。
  88. * @param ctx 上下文
  89. * @param paramMap 参数(含 FPERSONID、FID)
  90. * @param tempMap 临时缓存 Map
  91. * @param mainTableName 计算表对象名
  92. * @param mainTableRowFilter 计算表数据行过滤条件
  93. * @return 当月净增人数
  94. */
  95. public double getEndMonthRetainedUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
  96. try {
  97. //计算的员工id
  98. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  99. //计算的数据行
  100. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  101. String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
  102. // 获取当月入职人数
  103. double ruzhiNum = getMonthRuzhiUsers(ctx, paramMap, tempMap, mainTableName, mainTableRowFilter);
  104. // 获取当月离职人数
  105. double lizhiNum = getMonthLiZhiUsers(ctx, paramMap, tempMap, mainTableName, mainTableRowFilter);
  106. // 净增 = 入职 - 离职
  107. return ((double) ruzhiNum - lizhiNum);
  108. } catch (Exception ex) {
  109. logger.error(ex.getMessage(), ex);
  110. //ex.printStackTrace();
  111. }
  112. return (double) 0.0F;
  113. }
  114. /**
  115. * 当月入职人数(通过招聘渠道入职且在生效日期当月入职的人数)。
  116. * @param ctx 上下文
  117. * @param paramMap 参数(含 FPERSONID、FID)
  118. * @param tempMap 临时缓存 Map
  119. * @param mainTableName 计算表对象名
  120. * @param mainTableRowFilter 计算表数据行过滤条件
  121. * @return 当前人员在生效日期对应的当月入职人数
  122. */
  123. public double getMonthRuzhiUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
  124. try {
  125. //计算的员工id
  126. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  127. //计算的数据行
  128. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  129. String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
  130. // 入职人数缓存 key
  131. String ruZhiMapKey = "getMonthRuzhiUsers_Map";
  132. // 查询生效日期当月入职的人数
  133. getRuZhiPersonNum(ctx, tempMap, mainTableName, mainTableRowFilter, ruZhiMapKey);
  134. Map<String, Integer> fixPayMap = (Map) tempMap.get(ruZhiMapKey);
  135. String key = personId + "_" + Effect;
  136. if (fixPayMap.containsKey(key)) {
  137. int num = fixPayMap.containsKey(key) ? fixPayMap.get(key) : 0;
  138. return (double) (num);
  139. } else {
  140. return (double) 0.0F;
  141. }
  142. } catch (Exception ex) {
  143. logger.error(ex.getMessage(), ex);
  144. //ex.printStackTrace();
  145. }
  146. return (double) 0.0F;
  147. }
  148. /**
  149. * 当月转正人数(通过招聘渠道入职且在生效日期当月转正的人数)。
  150. * @param ctx 上下文
  151. * @param paramMap 参数(含 FPERSONID、FID)
  152. * @param tempMap 临时缓存 Map
  153. * @param mainTableName 计算表对象名
  154. * @param mainTableRowFilter 计算表数据行过滤条件
  155. * @return 当前人员在生效日期对应的当月转正人数
  156. */
  157. public double getMonthZhuanZhengUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
  158. try {
  159. //计算的员工id
  160. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  161. //计算的数据行
  162. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  163. String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
  164. // 转正人数缓存 key
  165. String zhuanZhengMapKey = "getMonthZhuanZhengUsers_Map";
  166. // 查询生效日期当月转正的人数
  167. getZhuanZhengPersonNum(ctx, tempMap, mainTableName, mainTableRowFilter, zhuanZhengMapKey);
  168. Map<String, Integer> fixPayMap = (Map) tempMap.get(zhuanZhengMapKey);
  169. String key = personId + "_" + Effect;
  170. if (fixPayMap.containsKey(key)) {
  171. int num = fixPayMap.containsKey(key) ? fixPayMap.get(key) : 0;
  172. return (double) (num);
  173. } else {
  174. return (double) 0.0F;
  175. }
  176. } catch (Exception ex) {
  177. logger.error(ex.getMessage(), ex);
  178. //ex.printStackTrace();
  179. }
  180. return (double) 0.0F;
  181. }
  182. /**
  183. * 当月离职人数(通过招聘渠道入职且在生效日期当月离职的人数)。
  184. * @param ctx 上下文
  185. * @param paramMap 参数(含 FPERSONID、FID)
  186. * @param tempMap 临时缓存 Map
  187. * @param mainTableName 计算表对象名
  188. * @param mainTableRowFilter 计算表数据行过滤条件
  189. * @return 当前人员在生效日期对应的当月离职人数
  190. */
  191. public double getMonthLiZhiUsers(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
  192. try {
  193. //计算的员工id
  194. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  195. //计算的数据行
  196. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  197. String Effect = getEffect(ctx, tempMap, mainTableName,mainTableRowFilter, id);
  198. // 离职人数缓存 key
  199. String liZhiMapKey = "getMonthLiZhiUsers_Map";
  200. // 查询生效日期当月离职的人数
  201. getLiZhiPersonNum(ctx, tempMap, mainTableName, mainTableRowFilter, liZhiMapKey);
  202. Map<String, Integer> fixPayMap = (Map) tempMap.get(liZhiMapKey);
  203. String key = personId + "_" + Effect;
  204. if (fixPayMap.containsKey(key)) {
  205. int num = fixPayMap.containsKey(key) ? fixPayMap.get(key) : 0;
  206. return (double) (num);
  207. } else {
  208. return (double) 0.0F;
  209. }
  210. } catch (Exception ex) {
  211. logger.error(ex.getMessage(), ex);
  212. //ex.printStackTrace();
  213. }
  214. return (double) 0.0F;
  215. }
  216. /**
  217. * 查询生效日期当月离职的人数,按"人员ID_生效年月"为 key 缓存到 tempMap。
  218. * <p>
  219. * 通过 CT_REC_ApplicantBeisen(北森应聘表)关联 T_HR_RESIGNBIZBILL(离职单据),
  220. * 统计招聘渠道入职的员工在生效日期当月离职的人数。
  221. * @param ctx 上下文
  222. * @param tempMap 临时缓存 Map
  223. * @param mainTableName 计算表对象名
  224. * @param mainTableRowFilter 计算表数据行过滤条件
  225. * @param lzZhiMapKey 缓存 key
  226. * @throws BOSException 数据库异常
  227. * @throws SQLException SQL异常
  228. */
  229. private static void getLiZhiPersonNum(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String lzZhiMapKey) throws BOSException, SQLException {
  230. Map<String, Integer> lzFixPayMap = (Map) tempMap.get(lzZhiMapKey);
  231. if(lzFixPayMap==null){
  232. StringBuilder sql = new StringBuilder();
  233. sql.append(" /*dialect*/ ");
  234. // 主查询:按核算表和生效月份分组统计离职人数
  235. sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
  236. // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次离职记录
  237. sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
  238. sql.append(" from CT_REC_ApplicantBeisen ab ");
  239. sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
  240. sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
  241. // 取每个人员最近一次离职记录(ROW_NUMBER + rn=1)
  242. sql.append(" left join ( ");
  243. sql.append(" SELECT FPERSONID, FBIZDATE ");
  244. sql.append(" FROM ( ");
  245. sql.append(" SELECT ");
  246. sql.append(" e.FPERSONID, ");
  247. sql.append(" e.FBIZDATE, ");
  248. sql.append(" ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
  249. sql.append(" FROM T_HR_RESIGNBIZBILLENTRY e ");
  250. sql.append(" LEFT JOIN T_HR_RESIGNBIZBILL b ON b.fid = e.FBillID ");
  251. sql.append(" where b.FBillState=3 ");
  252. sql.append(" ) t ");
  253. sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
  254. // 离职日期在生效日期当月范围内:[当月第一天, 下月第一天)
  255. sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)"); // 当月第一天 00:00:00.000
  256. sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) "); // 下个月第一天 00:00:00.000
  257. sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
  258. IRowSet rs = DbUtil.executeQuery(ctx,sql.toString());
  259. lzFixPayMap = new HashMap<>();
  260. while (rs.next()) {
  261. //获取人数
  262. String fpersonid = rs.getString("fpersonid");
  263. String FEffectDate = rs.getString("FEffectDate");
  264. int pNum = rs.getInt("pNum");
  265. lzFixPayMap.put(fpersonid+"_"+FEffectDate,pNum);
  266. }
  267. tempMap.put(lzZhiMapKey, lzFixPayMap);
  268. }
  269. }
  270. /**
  271. * 查询生效日期当月入职的人数,按"人员ID_生效年月"为 key 缓存到 tempMap。
  272. * <p>
  273. * 通过 CT_REC_ApplicantBeisen(北森应聘表)关联 T_HR_EmpEnrollBizBill(入职单据),
  274. * 统计招聘渠道入职的员工在生效日期当月入职的人数。
  275. * @param ctx 上下文
  276. * @param tempMap 临时缓存 Map
  277. * @param mainTableName 计算表对象名
  278. * @param mainTableRowFilter 计算表数据行过滤条件
  279. * @param ruZhiMapKey 缓存 key
  280. * @throws BOSException 数据库异常
  281. * @throws SQLException SQL异常
  282. */
  283. private static void getRuZhiPersonNum(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String ruZhiMapKey) throws BOSException, SQLException {
  284. Map<String, Integer> fixPayMap = (Map) tempMap.get(ruZhiMapKey);
  285. if(fixPayMap==null){
  286. StringBuilder sql = new StringBuilder();
  287. sql.append(" /*dialect*/ ");
  288. // 主查询:按核算表和生效月份分组统计入职人数
  289. sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
  290. // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次入职记录
  291. sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
  292. sql.append(" from CT_REC_ApplicantBeisen ab ");
  293. sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
  294. sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
  295. // 取每个人员最近一次入职记录(ROW_NUMBER + rn=1)
  296. sql.append(" left join ( ");
  297. sql.append(" SELECT FPERSONID, FBIZDATE ");
  298. sql.append(" FROM ( ");
  299. sql.append(" SELECT ");
  300. sql.append(" e.FPERSONID, ");
  301. sql.append(" e.FBIZDATE, ");
  302. sql.append(" ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
  303. sql.append(" FROM T_HR_EmpEnrollBizBillEntry e ");
  304. sql.append(" LEFT JOIN T_HR_EmpEnrollBizBill b ON b.fid = e.FBillID ");
  305. sql.append(" where b.FBillState=3 ");
  306. sql.append(" ) t ");
  307. sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
  308. // 入职日期在生效日期当月范围内:[当月第一天, 下月第一天)
  309. sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)"); // 当月第一天 00:00:00.000
  310. sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) "); // 下个月第一天 00:00:00.000
  311. sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
  312. IRowSet rs = DbUtil.executeQuery(ctx,sql.toString());
  313. fixPayMap = new HashMap<>();
  314. while (rs.next()) {
  315. //获取人数
  316. String fpersonid = rs.getString("fpersonid");
  317. String FEffectDate = rs.getString("FEffectDate");
  318. int pNum = rs.getInt("pNum");
  319. fixPayMap.put(fpersonid+"_"+FEffectDate,pNum);
  320. }
  321. tempMap.put(ruZhiMapKey, fixPayMap);
  322. }
  323. }
  324. /**
  325. * 查询生效日期当月转正的人数,按"人员ID_生效年月"为 key 缓存到 tempMap。
  326. * <p>
  327. * 通过 CT_REC_ApplicantBeisen(北森应聘表)关联 T_HR_EMPHIREBIZBILL(转正单据),
  328. * 统计招聘渠道入职的员工在生效日期当月转正的人数。
  329. * @param ctx 上下文
  330. * @param tempMap 临时缓存 Map
  331. * @param mainTableName 计算表对象名
  332. * @param mainTableRowFilter 计算表数据行过滤条件
  333. * @param ruZhiMapKey 缓存 key
  334. * @throws BOSException 数据库异常
  335. * @throws SQLException SQL异常
  336. */
  337. private static void getZhuanZhengPersonNum(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String ruZhiMapKey) throws BOSException, SQLException {
  338. Map<String, Integer> fixPayMap = (Map) tempMap.get(ruZhiMapKey);
  339. if(fixPayMap==null){
  340. StringBuilder sql = new StringBuilder();
  341. sql.append(" /*dialect*/ ");
  342. // 主查询:按核算表和生效月份分组统计转正人数
  343. sql.append(" SELECT mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) FEffectDate,count(abt.pid) pNum from ").append(mainTableName).append(" mt ");
  344. // 关联子查询:通过北森应聘表匹配到人员,再关联其最近一次转正记录
  345. sql.append(" left join ( select ab.CFRecruitOwnerID,p.fid pid,t.fbizDate bizDate ");
  346. sql.append(" from CT_REC_ApplicantBeisen ab ");
  347. sql.append(" inner join T_REC_ResumeBaseRec rb on rb.CFCANDIDATEID = ab.CFCANDIDATEID ");
  348. sql.append(" inner join t_bd_person p on isnull(p.FIDCARDNO,p.FPASSPORTNO)=isnull(rb.FIdentityCardNo,rb.FPassportNo) ");
  349. // 取每个人员最近一次转正记录(ROW_NUMBER + rn=1)
  350. sql.append(" left join ( ");
  351. sql.append(" SELECT FPERSONID, FBIZDATE ");
  352. sql.append(" FROM ( ");
  353. sql.append(" SELECT ");
  354. sql.append(" e.FPERSONID, ");
  355. sql.append(" e.FBIZDATE,e.FEnrollDate, ");
  356. sql.append(" ROW_NUMBER() OVER (PARTITION BY e.FPERSONID ORDER BY e.FBIZDATE DESC) AS rn ");
  357. sql.append(" FROM T_HR_EMPHIREBIZBILLENTRY e ");
  358. sql.append(" LEFT JOIN T_HR_EMPHIREBIZBILL b ON b.fid = e.FBillID ");
  359. sql.append(" where b.FBillState=3 ");
  360. sql.append(" ) t ");
  361. sql.append(" WHERE rn = 1) t on t.fpersonid=p.fid ) abt on abt.CFRecruitOwnerID=mt.fpersonid ");
  362. // 转正日期在生效日期当月范围内:[当月第一天, 下月第一天)
  363. sql.append(" AND abt.bizDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate), 0)"); // 当月第一天 00:00:00.000
  364. sql.append(" AND abt.bizDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, mt.FEffectDate) + 1, 0) "); // 下个月第一天 00:00:00.000
  365. sql.append(" AND mt.fid in(").append(mainTableRowFilter).append(") group by mt.fpersonid,substring(convert(varchar,mt.FEffectDate,120),0,8) ");
  366. IRowSet rs = DbUtil.executeQuery(ctx,sql.toString());
  367. fixPayMap = new HashMap<>();
  368. while (rs.next()) {
  369. //获取人数
  370. String fpersonid = rs.getString("fpersonid");
  371. String FEffectDate = rs.getString("FEffectDate");
  372. int pNum = rs.getInt("pNum");
  373. fixPayMap.put(fpersonid+"_"+FEffectDate,pNum);
  374. }
  375. tempMap.put(ruZhiMapKey, fixPayMap);
  376. }
  377. }
  378. /**
  379. * 获取生效日期的月份(yyyy-MM 格式)。
  380. * <p>
  381. * 优先从 tempMap 中以 "getEffect_" + id 为 key 查找缓存;
  382. * 缓存未命中时,一次性批量查询 mainTableRowFilter 范围内所有行的 FEffectDate,
  383. * 逐行以 "getEffect_" + fid 为 key 写入 tempMap,后续同一批次其他行直接命中缓存。
  384. * @param ctx 上下文
  385. * @param tempMap 临时缓存 Map
  386. * @param mainTableName 计算表对象名
  387. * @param mainTableRowFilter 计算表数据行过滤条件(用于批量加载)
  388. * @param id 当前数据行 FID
  389. * @return 生效日期对应的 yyyy-MM 格式月份字符串,若未查到则为空字符串
  390. * @throws BOSException 数据库异常
  391. * @throws SQLException SQL异常
  392. */
  393. private static String getEffect(Context ctx, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter, String id) throws BOSException, SQLException {
  394. String cacheKey = "getEffect_" + id;
  395. if (tempMap.containsKey(cacheKey)) {
  396. return (String) tempMap.get(cacheKey);
  397. }
  398. IRowSet rowSet = DbUtil.executeQuery(ctx, " SELECT mt.fid,mt.fpersonid,mt.FEffectDate from " + mainTableName + " mt where mt.fid in("+mainTableRowFilter+")");
  399. String Effect = "";
  400. while (rowSet.next()) {
  401. String fid = rowSet.getString("fid");
  402. LocalDate effectDate = rowSet.getDate("FEffectDate").toLocalDate();
  403. Effect = effectDate.format(DateTimeFormatter.ofPattern("yyyy-MM"));
  404. String Key = "getEffect_" + fid;
  405. tempMap.put(Key, Effect);
  406. }
  407. return (String) tempMap.getOrDefault(cacheKey, "");
  408. }
  409. /**
  410. * 获取适用薪酬方案名称(fname_l2)。
  411. * <p>
  412. * 通过计算表关联 T_HR_SBatchSubmitShemeBill 和 T_HR_SCalSubmitScheme,
  413. * 查询当前计算范围对应的薪酬方案名称,结果以"name"为 key 缓存。
  414. * @param ctx 上下文
  415. * @param paramMap 参数(含 FPERSONID、FID)
  416. * @param tempMap 临时缓存 Map
  417. * @param mainTableName 计算表对象名
  418. * @param mainTableRowFilter 计算表数据行过滤条件
  419. * @return 方案名称,未找到则返回空字符串
  420. */
  421. public String getApplicablePlan(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter) {
  422. try {
  423. //计算的员工id
  424. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  425. //计算的数据行
  426. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  427. //根据计算范围初始化薪酬项目信息
  428. String fixPayMapKey = "getApplicablePlan_SchemeName";
  429. if (tempMap.get(fixPayMapKey) == null) {
  430. StringBuilder sql = new StringBuilder();
  431. sql.append(" select sss.fname_l2 from "+mainTableName+" sbe ");
  432. sql.append(" left join T_HR_SBatchSubmitShemeBill ssb on ssb.fid=sbe.FBillID ");
  433. sql.append(" left join T_HR_SCalSubmitScheme sss on sss.fid=ssb.FSubmitSchemeID ");
  434. sql.append(" where sbe.fid in(" + mainTableRowFilter + ")");
  435. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  436. if (rs.next()) {
  437. Map<String, String> objectMap = new HashMap<>();
  438. objectMap.put("name", rs.getString("fname_l2"));
  439. tempMap.put(fixPayMapKey, objectMap);
  440. }
  441. }
  442. //获取薪酬项目值
  443. Map<String, String> fixPayMap = (Map) tempMap.get(fixPayMapKey);
  444. String key = "name";
  445. if (fixPayMap.containsKey(key)) {
  446. String schemeName = fixPayMap.get(key);
  447. return schemeName;
  448. } else {
  449. return "";
  450. }
  451. } catch (Exception e) {
  452. logger.error(e.getMessage(), e);
  453. //e.printStackTrace();
  454. return "";
  455. }
  456. }
  457. /**
  458. * 获取薪酬档案中指定薪资项目的金额。
  459. * <p>
  460. * 根据人员 ID 和薪酬项目名称,从 T_HR_SFIXADJUSTSALARY 表中查询固定薪酬数据。
  461. * 结果以 "personId_cmpItem" 为 key 缓存到 tempMap,后续同一计算范围直接复用。
  462. * @param ctx 上下文
  463. * @param paramMap 参数(含 FPERSONID、FID)
  464. * @param tempMap 临时缓存 Map
  465. * @param mainTableName 计算表对象名
  466. * @param mainTableRowFilter 计算表数据行过滤条件
  467. * @param cmpItem 薪酬项目名称(对应 T_HR_SCMPITEM.FNAME_L2)
  468. * @param isFixed 是否固定薪酬(0=按FEffectDate匹配, 非0=按FEffectDate匹配;预留扩展)
  469. * @return 薪资项目金额,若人员或项目不存在则返回 0.0
  470. */
  471. public double getItemSalary(Context ctx, Map<String, Object> paramMap, HashMap<String, Object> tempMap, String mainTableName, String mainTableRowFilter,
  472. String cmpItem, double isFixed) {
  473. try {
  474. //计算的员工id
  475. String personId = (String) paramMap.get(mainTableName.toUpperCase() + "_FPERSONID");
  476. //计算的数据行
  477. String id = (String) paramMap.get(mainTableName.toUpperCase() + "_FID");
  478. //根据计算范围初始化薪酬项目信息
  479. String fixPayMapKey = "getItemSalary_fixPayMap_" + isFixed;
  480. if (!StringUtils.isEmpty(cmpItem) && personId != null) {
  481. if (tempMap.get(fixPayMapKey) == null) {
  482. tempMap.put(fixPayMapKey, this.initFixPayForMoneyMap(ctx, mainTableName, isFixed, mainTableRowFilter));
  483. }
  484. //获取薪酬项目值
  485. Map<String, BigDecimal> fixPayMap = (Map) tempMap.get(fixPayMapKey);
  486. String key = personId + "_" + cmpItem;
  487. if (fixPayMap.containsKey(key)) {
  488. BigDecimal money = fixPayMap.containsKey(key) ? (BigDecimal) fixPayMap.get(key) : new BigDecimal(0);
  489. double pay = money.doubleValue();
  490. return pay;
  491. } else {
  492. return (double) 0.0F;
  493. }
  494. } else {
  495. return (double) 0.0F;
  496. }
  497. } catch (Exception e) {
  498. logger.error(e.getMessage(), e);
  499. //e.printStackTrace();
  500. return (double) 0.0F;
  501. }
  502. }
  503. /**
  504. * 初始化固定薪酬数据 Map,以 "personId_cmpItem" 为 key,金额为 value。
  505. * <p>
  506. * 查询 T_HR_SFIXADJUSTSALARY(固定调整薪酬表),按生效日期匹配核算表中的薪酬项目。
  507. * 查询结果按 type 优先级排序:type=1(相同组织)优先于 type=0(无组织限制),
  508. * 同一 key 仅保留首次出现的记录(优先级最高的)。
  509. * @param ctx 上下文
  510. * @param mainTableName 计算表对象名
  511. * @param isFixed 是否固定薪酬(0=按FEffectDate匹配, 非0=按FEffectDate匹配;预留扩展)
  512. * @param mainTableRowFilter 计算表数据行过滤条件
  513. * @return key 为 "personId_cmpItem",value 为对应金额的 Map
  514. * @throws Exception 数据库异常
  515. */
  516. public Map<String, BigDecimal> initFixPayForMoneyMap(Context ctx, String mainTableName, double isFixed, String mainTableRowFilter) throws Exception {
  517. StringBuilder sb = new StringBuilder();
  518. String effectDayField = "";
  519. String key = null;
  520. String personId = "";
  521. String cmpItem = "";
  522. Map<String, BigDecimal> fixPayMap = new HashMap();
  523. Set<String> set = new HashSet();
  524. if (isFixed == (double) 0.0F) {
  525. effectDayField = "calTable.FEffectDate";
  526. } else {
  527. effectDayField = "calTable.FEffectDate";
  528. }
  529. sb.append(" SELECT DISTINCT CALTABLE.FPERSONID as personId, FIX.FMONEY MONEY, FIX.FCURRENCYID, ");
  530. sb.append(" CMPITEM.FNAME_L2 CMPITEM,FIX.FEFFECTDAY, ");
  531. // type: 0=组织为空(通用), 1=组织匹配(优先), 2=组织不匹配(排除)
  532. 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");
  533. sb.append(" FROM " + mainTableName + " CALTABLE ");
  534. sb.append(" INNER JOIN T_HR_SFIXADJUSTSALARY FIX ON CALTABLE.FPERSONID=FIX.FPERSONID AND (FIX.FCurrencyID = CALTABLE.FCurrencyID OR FIX.FCurrencyID IS NULL)");
  535. sb.append(" LEFT JOIN T_HR_SCMPITEM CMPITEM ON CMPITEM.FID=FIX.FCMPITEMID ");
  536. sb.append(" WHERE FIX.feffectday <= ").append(effectDayField).append(" AND FIX.fleffectday >= ").append(effectDayField);
  537. sb.append(" AND CALTABLE.fid in(").append(mainTableRowFilter).append(")");
  538. IRowSet rs = DbUtil.executeQuery(ctx, sb.toString());
  539. List<Map<String, Object>> dataList = this.getDataFromExecuteQuery(rs);
  540. Iterator<Map<String, Object>> it = dataList.iterator();
  541. while (it.hasNext()) {
  542. Map<String, Object> data = (Map) it.next();
  543. personId = (String) data.get("personId");
  544. cmpItem = (String) data.get("CMPITEM");
  545. key = personId + "_" + cmpItem;
  546. if (!set.contains(key)) {
  547. set.add(key);
  548. BigDecimal money = (BigDecimal) data.get("MONEY");
  549. fixPayMap.put(key, money);
  550. }
  551. }
  552. return fixPayMap;
  553. }
  554. /**
  555. * 将 IRowSet 查询结果按优先级排序后转换为 List&lt;Map&gt;。
  556. * <p>
  557. * 排序规则:type=1(相同组织)的记录优先,type=0(无组织限制)的次之,type=2(组织不匹配)的排除。
  558. * 同一人员同一薪酬项目的 key 只保留首次出现(即优先级最高)的记录。
  559. * @param executeQuery 查询结果集
  560. * @return 按优先级排序后的数据列表
  561. * @throws SQLException SQL异常
  562. */
  563. public List<Map<String, Object>> getDataFromExecuteQuery(IRowSet executeQuery) throws SQLException {
  564. Map<String, Object> map = null;
  565. List<Map<String, Object>> dataList = null;
  566. Map<String, List<Map<String, Object>>> mapList = null;
  567. Map<String, List<Map<String, Object>>> mapListOne = null;
  568. Map<String, List<Map<String, Object>>> mapListZero = null;
  569. List<Map<String, Object>> list = null;
  570. List<Map<String, Object>> oneList = null;
  571. List<Map<String, Object>> zeroList = null;
  572. if (null != executeQuery) {
  573. String personId = null;
  574. dataList = new ArrayList();
  575. mapList = new HashMap();
  576. mapListOne = new HashMap();
  577. mapListZero = new HashMap();
  578. while (executeQuery.next()) {
  579. map = this.executeQueryToMap(executeQuery);
  580. personId = executeQuery.getString("personId");
  581. int type = executeQuery.getInt("type");
  582. if (mapList.containsKey(personId)) {
  583. list = (List) mapList.get(personId);
  584. list.add(map);
  585. } else {
  586. list = new ArrayList();
  587. list.add(map);
  588. mapList.put(personId, list);
  589. }
  590. // type=1: 组织匹配,优先使用
  591. if (type == 1) {
  592. if (mapListOne.containsKey(personId)) {
  593. oneList = (List) mapListOne.get(personId);
  594. oneList.add(map);
  595. } else {
  596. oneList = new ArrayList();
  597. oneList.add(map);
  598. mapListOne.put(personId, oneList);
  599. }
  600. // type=0: 无组织限制,次优
  601. } else if (type == 0) {
  602. if (mapListZero.containsKey(personId)) {
  603. zeroList = (List) mapListZero.get(personId);
  604. zeroList.add(map);
  605. } else {
  606. zeroList = new ArrayList();
  607. zeroList.add(map);
  608. mapListZero.put(personId, zeroList);
  609. }
  610. }
  611. // type=2(组织不匹配)排除,不加入结果
  612. }
  613. List<Map<String, Object>> var21 = null;
  614. // 合并结果:先加入 type=1(组织匹配),再加入 type=0(无组织限制)
  615. // 后续去重逻辑会保留首次出现的记录,从而实现优先级
  616. for (String str : mapList.keySet()) {
  617. List<Map<String, Object>> var22 = (List) mapList.get(str);
  618. if (mapListOne.containsKey(str)) {
  619. dataList.addAll((Collection) mapListOne.get(str));
  620. }
  621. if (mapListZero.containsKey(str)) {
  622. dataList.addAll((Collection) mapListZero.get(str));
  623. }
  624. }
  625. }
  626. return (List<Map<String, Object>>) (null != dataList && !dataList.isEmpty() ? dataList : new ArrayList(0));
  627. }
  628. /**
  629. * 将 IRowSet 当前行转换为 Map,提取固定薪酬相关字段。
  630. * @param executeQuery 查询结果集(已定位到某一行)
  631. * @return 包含 personId、MONEY、FCURRENCYID、CMPITEM、FEFFECTDAY 的 Map
  632. * @throws SQLException SQL异常
  633. */
  634. public Map<String, Object> executeQueryToMap(IRowSet executeQuery) throws SQLException {
  635. Map<String, Object> map = new HashMap();
  636. map.put("personId", executeQuery.getString("personId"));
  637. map.put("MONEY", executeQuery.getBigDecimal("MONEY") == null ? new BigDecimal(0) : executeQuery.getBigDecimal("MONEY"));
  638. map.put("FCURRENCYID", executeQuery.getString("FCURRENCYID"));
  639. map.put("CMPITEM", executeQuery.getString("CMPITEM"));
  640. map.put("FEFFECTDAY", executeQuery.getDate("FEFFECTDAY"));
  641. return map;
  642. }
  643. }