PersonnelStructureAnalysisHandler.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. package com.kingdee.eas.custom.perfweb.handler;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.kingdee.bos.BOSException;
  4. import com.kingdee.bos.Context;
  5. import com.kingdee.bos.ctrl.swing.StringUtils;
  6. import com.kingdee.jdbc.rowset.IRowSet;
  7. import com.kingdee.eas.custom.perfweb.util.ReportFilterUtil;
  8. import com.kingdee.eas.custom.perfweb.util.ReportOrgUtil;
  9. import com.kingdee.eas.util.app.DbUtil;
  10. import com.kingdee.shr.base.syssetting.context.SHRContext;
  11. import com.kingdee.shr.base.syssetting.exception.SHRWebException;
  12. import com.kingdee.shr.base.syssetting.json.GridDataEntity;
  13. import com.kingdee.shr.base.syssetting.web.handler.ListHandler;
  14. import org.apache.log4j.Logger;
  15. import org.springframework.ui.ModelMap;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;
  18. import java.sql.SQLException;
  19. import java.util.*;
  20. /**
  21. * 类名称: PersonnelStructureAnalysisHandler
  22. *
  23. * 功能描述: 人员属性分析报表(按中心统计学历/年龄/司龄/性别结构,末尾追加品质部与合计行)
  24. * 创建日期: 2026-06-08
  25. * 作 者: 青梧
  26. * 版 本: 1.3
  27. *
  28. * 返回字段说明:
  29. * centerCode - 中心/部门编码(FNUMBER,同名取第一个)
  30. * centerName - 中心/部门名称(跨公司同名合并,排除130;品质部为单独汇总行)
  31. * masterAbove - 硕士及以上人数(学历06/07/08)
  32. * masterAboveRate - 硕士及以上人数占比(分母totalCount)
  33. * bachelor - 本科人数(学历05)
  34. * bachelorRate - 本科人数占比
  35. * college - 大专人数(学历04)
  36. * collegeRate - 大专人数占比
  37. * highSchool - 高中/中专人数(学历02/03)
  38. * highSchoolRate - 高中/中专人数占比
  39. * juniorBelow - 初中及以下人数(学历01)
  40. * juniorBelowRate - 初中及以下人数占比
  41. * eduTotal - 学历合计(=totalCount)
  42. * below25 - 25岁以下人数
  43. * below25Rate - 25岁以下人数占比
  44. * age26_30 - 26-30岁人数
  45. * age26_30Rate - 26-30岁人数占比
  46. * age31_35 - 31-35岁人数
  47. * age31_35Rate - 31-35岁人数占比
  48. * age36_40 - 36-40岁人数
  49. * age36_40Rate - 36-40岁人数占比
  50. * above40 - 40岁以上人数
  51. * above40Rate - 40岁以上人数占比
  52. * ageTotal - 年龄合计
  53. * below6Months - 司龄6个月以内人数
  54. * below6MonthsRate - 司龄6个月以内人数占比
  55. * age6M_1Y - 司龄6个月-1年人数
  56. * age6M_1YRate - 司龄6个月-1年人数占比
  57. * age1_3Y - 司龄1-3年人数
  58. * age1_3YRate - 司龄1-3年人数占比
  59. * age3_5Y - 司龄3-5年人数
  60. * age3_5YRate - 司龄3-5年人数占比
  61. * age5_10Y - 司龄5-10年人数
  62. * age5_10YRate - 司龄5-10年人数占比
  63. * above10Y - 司龄10年以上人数
  64. * above10YRate - 司龄10年以上人数占比
  65. * workAgeTotal - 司龄合计
  66. * maleCount - 男性人数(FGender枚举1)
  67. * maleCountRate - 男性人数占比
  68. * femaleCount - 女性人数(FGender枚举2)
  69. * femaleCountRate - 女性人数占比
  70. * totalCount - 总人数(学历合计,各档占比统一分母)
  71. * eduCoefficient - 学历系数=(硕士*5+本科*4+大专*3+高中中专*2+初中及以下*1)/totalCount
  72. */
  73. public class PersonnelStructureAnalysisHandler extends ListHandler {
  74. private static Logger logger = Logger.getLogger(PersonnelStructureAnalysisHandler.class);
  75. Context ctx = SHRContext.getInstance().getContext();
  76. /** 单独汇总的品质部名称(跨公司合并) */
  77. private static final String QUALITY_DEPT_NAME = "品质部";
  78. /**
  79. * 列表查询入口:遍历中心,统计学历/年龄/司龄/性别结构,支持公司F7筛选,末尾追加品质部与合计行。
  80. */
  81. @Override
  82. protected GridDataEntity getGridRequestData(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
  83. logger.error("PersonnelStructureAnalysisHandler getGridRequestData------------");
  84. GridDataEntity gridDataEntity = new GridDataEntity();
  85. try {
  86. // 快筛: 公司FID(company字段非中心层级时) 限定统计范围
  87. JSONObject fastFilterItemsJson = ReportFilterUtil.parseFastFilterJson(request);
  88. logger.error("fastFilterItemsJson: " + fastFilterItemsJson);
  89. // 公司F7:限定人员所属公司,不影响中心名称列表
  90. Map<String, String> paramMap = ReportFilterUtil.parseCompanyParams(fastFilterItemsJson, logger, false);
  91. String companyId = paramMap.get("companyId");
  92. // centers: 去重后的中心名称编码列表(跨公司同名合并,排除130)
  93. List<ReportOrgUtil.OrgNameCode> centers = ReportOrgUtil.getDistinctOrgNameCodes(ctx, ReportOrgUtil.LAYER_CENTER, companyId);
  94. List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
  95. // ---------- 遍历中心,汇总各维度结构 ----------
  96. List<Map<String, Object>> centerRows = new ArrayList<Map<String, Object>>();
  97. for (ReportOrgUtil.OrgNameCode center : centers) {
  98. centerRows.add(buildStructureRow(center.orgCode, center.orgName, companyId, false));
  99. }
  100. dataList.addAll(centerRows);
  101. // ---------- 品质部单独一行(跨公司合并,与中心行无互斥关系) ----------
  102. dataList.add(buildStructureRow(resolveQualityDeptCode(companyId), QUALITY_DEPT_NAME, companyId, true));
  103. // ---------- 合计行(仅累加各中心,不含品质部,避免重复) ----------
  104. dataList.add(buildSummaryRow(centerRows));
  105. gridDataEntity.setRows(dataList);
  106. gridDataEntity.setTotal(dataList.size());
  107. gridDataEntity.setPage(1);
  108. gridDataEntity.setRecords(dataList.size());
  109. logger.error("人员属性分析报表数据查询成功,总记录数: " + dataList.size());
  110. } catch (SQLException e) {
  111. logger.error("数据库查询失败", e);
  112. throw new SHRWebException("数据查询失败: " + e.getMessage());
  113. } catch (BOSException e) {
  114. logger.error("业务异常", e);
  115. throw new SHRWebException("业务处理失败: " + e.getMessage());
  116. } catch (Exception e) {
  117. logger.error("未知异常", e);
  118. throw new SHRWebException("系统异常: " + e.getMessage());
  119. }
  120. ReportFilterUtil.applyGridUserData(request, gridDataEntity);
  121. return gridDataEntity;
  122. }
  123. /** 查询品质部编码(跨公司同名取第一个) */
  124. private String resolveQualityDeptCode(String companyId) throws BOSException, SQLException {
  125. List<ReportOrgUtil.OrgNameCode> depts = ReportOrgUtil.getDistinctOrgNameCodes(ctx, ReportOrgUtil.LAYER_DEPT, companyId);
  126. for (ReportOrgUtil.OrgNameCode dept : depts) {
  127. if (QUALITY_DEPT_NAME.equals(dept.orgName)) {
  128. return dept.orgCode;
  129. }
  130. }
  131. return "";
  132. }
  133. /**
  134. * 构建一行结构数据(人数+占比+学历系数)。
  135. *
  136. * @param deptScope true=按部门名称跨公司统计,false=按中心统计
  137. */
  138. private Map<String, Object> buildStructureRow(String orgCode, String orgName, String companyId, boolean deptScope)
  139. throws BOSException, SQLException {
  140. Map<String, Integer> educationStat = getEducationStat(orgName, companyId, deptScope);
  141. Map<String, Integer> ageStat = getAgeStat(orgName, companyId, deptScope);
  142. Map<String, Integer> workAgeStat = getWorkAgeStat(orgName, companyId, deptScope);
  143. // genderStat[0]=男, genderStat[1]=女
  144. int[] genderStat = getGenderStat(orgName, companyId, deptScope);
  145. int totalCount = sumMap(educationStat);
  146. Map<String, Object> row = new LinkedHashMap<String, Object>();
  147. row.put("centerCode", orgCode); // centerCode: 中心/部门编码
  148. row.put("centerName", orgName); // centerName: 中心/部门名称
  149. putCountWithRate(row, "masterAbove", educationStat.get("masterAbove"), totalCount);
  150. putCountWithRate(row, "bachelor", educationStat.get("bachelor"), totalCount);
  151. putCountWithRate(row, "college", educationStat.get("college"), totalCount);
  152. putCountWithRate(row, "highSchool", educationStat.get("highSchool"), totalCount);
  153. putCountWithRate(row, "juniorBelow", educationStat.get("juniorBelow"), totalCount);
  154. row.put("eduTotal", totalCount); // eduTotal: 学历合计
  155. putCountWithRate(row, "below25", ageStat.get("below25"), totalCount);
  156. putCountWithRate(row, "age26_30", ageStat.get("age26_30"), totalCount);
  157. putCountWithRate(row, "age31_35", ageStat.get("age31_35"), totalCount);
  158. putCountWithRate(row, "age36_40", ageStat.get("age36_40"), totalCount);
  159. putCountWithRate(row, "above40", ageStat.get("above40"), totalCount);
  160. row.put("ageTotal", sumMap(ageStat)); // ageTotal: 年龄合计
  161. putCountWithRate(row, "below6Months", workAgeStat.get("below6Months"), totalCount);
  162. putCountWithRate(row, "age6M_1Y", workAgeStat.get("age6M_1Y"), totalCount);
  163. putCountWithRate(row, "age1_3Y", workAgeStat.get("age1_3Y"), totalCount);
  164. putCountWithRate(row, "age3_5Y", workAgeStat.get("age3_5Y"), totalCount);
  165. putCountWithRate(row, "age5_10Y", workAgeStat.get("age5_10Y"), totalCount);
  166. putCountWithRate(row, "above10Y", workAgeStat.get("above10Y"), totalCount);
  167. row.put("workAgeTotal", sumMap(workAgeStat)); // workAgeTotal: 司龄合计
  168. putCountWithRate(row, "maleCount", genderStat[0], totalCount);
  169. putCountWithRate(row, "femaleCount", genderStat[1], totalCount);
  170. row.put("totalCount", totalCount); // totalCount: 总人数
  171. row.put("eduCoefficient", formatEduCoefficient(calculateEduCoefficient(educationStat, totalCount))); // eduCoefficient: 学历系数
  172. return row;
  173. }
  174. /**
  175. * 合计行:各中心人数字段累加,占比与学历系数按合计totalCount重算。
  176. */
  177. private Map<String, Object> buildSummaryRow(List<Map<String, Object>> centerRows) {
  178. Map<String, Integer> educationStat = initEducationStat();
  179. educationStat.put("masterAbove", sumIntField(centerRows, "masterAbove"));
  180. educationStat.put("bachelor", sumIntField(centerRows, "bachelor"));
  181. educationStat.put("college", sumIntField(centerRows, "college"));
  182. educationStat.put("highSchool", sumIntField(centerRows, "highSchool"));
  183. educationStat.put("juniorBelow", sumIntField(centerRows, "juniorBelow"));
  184. Map<String, Integer> ageStat = initAgeStat();
  185. ageStat.put("below25", sumIntField(centerRows, "below25"));
  186. ageStat.put("age26_30", sumIntField(centerRows, "age26_30"));
  187. ageStat.put("age31_35", sumIntField(centerRows, "age31_35"));
  188. ageStat.put("age36_40", sumIntField(centerRows, "age36_40"));
  189. ageStat.put("above40", sumIntField(centerRows, "above40"));
  190. Map<String, Integer> workAgeStat = initWorkAgeStat();
  191. workAgeStat.put("below6Months", sumIntField(centerRows, "below6Months"));
  192. workAgeStat.put("age6M_1Y", sumIntField(centerRows, "age6M_1Y"));
  193. workAgeStat.put("age1_3Y", sumIntField(centerRows, "age1_3Y"));
  194. workAgeStat.put("age3_5Y", sumIntField(centerRows, "age3_5Y"));
  195. workAgeStat.put("age5_10Y", sumIntField(centerRows, "age5_10Y"));
  196. workAgeStat.put("above10Y", sumIntField(centerRows, "above10Y"));
  197. int maleCount = sumIntField(centerRows, "maleCount");
  198. int femaleCount = sumIntField(centerRows, "femaleCount");
  199. int totalCount = sumMap(educationStat);
  200. Map<String, Object> row = new LinkedHashMap<String, Object>();
  201. row.put("centerCode", "");
  202. row.put("centerName", "合计");
  203. putCountWithRate(row, "masterAbove", educationStat.get("masterAbove"), totalCount);
  204. putCountWithRate(row, "bachelor", educationStat.get("bachelor"), totalCount);
  205. putCountWithRate(row, "college", educationStat.get("college"), totalCount);
  206. putCountWithRate(row, "highSchool", educationStat.get("highSchool"), totalCount);
  207. putCountWithRate(row, "juniorBelow", educationStat.get("juniorBelow"), totalCount);
  208. row.put("eduTotal", totalCount);
  209. putCountWithRate(row, "below25", ageStat.get("below25"), totalCount);
  210. putCountWithRate(row, "age26_30", ageStat.get("age26_30"), totalCount);
  211. putCountWithRate(row, "age31_35", ageStat.get("age31_35"), totalCount);
  212. putCountWithRate(row, "age36_40", ageStat.get("age36_40"), totalCount);
  213. putCountWithRate(row, "above40", ageStat.get("above40"), totalCount);
  214. row.put("ageTotal", sumMap(ageStat));
  215. putCountWithRate(row, "below6Months", workAgeStat.get("below6Months"), totalCount);
  216. putCountWithRate(row, "age6M_1Y", workAgeStat.get("age6M_1Y"), totalCount);
  217. putCountWithRate(row, "age1_3Y", workAgeStat.get("age1_3Y"), totalCount);
  218. putCountWithRate(row, "age3_5Y", workAgeStat.get("age3_5Y"), totalCount);
  219. putCountWithRate(row, "age5_10Y", workAgeStat.get("age5_10Y"), totalCount);
  220. putCountWithRate(row, "above10Y", workAgeStat.get("above10Y"), totalCount);
  221. row.put("workAgeTotal", sumMap(workAgeStat));
  222. putCountWithRate(row, "maleCount", maleCount, totalCount);
  223. putCountWithRate(row, "femaleCount", femaleCount, totalCount);
  224. row.put("totalCount", totalCount);
  225. row.put("eduCoefficient", formatEduCoefficient(calculateEduCoefficient(educationStat, totalCount)));
  226. return row;
  227. }
  228. /** 累加各行指定整型字段 */
  229. private int sumIntField(List<Map<String, Object>> rows, String field) {
  230. int sum = 0;
  231. for (Map<String, Object> row : rows) {
  232. Object value = row.get(field);
  233. if (value instanceof Integer) {
  234. sum += ((Integer) value).intValue();
  235. }
  236. }
  237. return sum;
  238. }
  239. /** 写入人数字段及对应占比(分母totalCount) */
  240. private void putCountWithRate(Map<String, Object> row, String countKey, int count, int totalCount) {
  241. row.put(countKey, count);
  242. row.put(countKey + "Rate", formatPercent(count, totalCount));
  243. }
  244. /** 格式化占比为百分数字符串(保留两位小数) */
  245. private String formatPercent(int count, int totalCount) {
  246. if (totalCount <= 0) {
  247. return "0.00%";
  248. }
  249. return String.format("%.2f", count * 100.0 / totalCount) + "%";
  250. }
  251. /**
  252. * 计算学历系数。
  253. * 公式:(硕士*5+本科*4+大专*3+高中中专*2+初中及以下*1)/totalCount
  254. */
  255. private double calculateEduCoefficient(Map<String, Integer> educationStat, int totalCount) {
  256. if (totalCount <= 0) {
  257. return 0.0;
  258. }
  259. double weighted = educationStat.get("masterAbove") * 5
  260. + educationStat.get("bachelor") * 4
  261. + educationStat.get("college") * 3
  262. + educationStat.get("highSchool") * 2
  263. + educationStat.get("juniorBelow");
  264. return weighted / totalCount;
  265. }
  266. /** 格式化学历系数(保留两位小数) */
  267. private String formatEduCoefficient(double coefficient) {
  268. return String.format("%.2f", coefficient);
  269. }
  270. /** 追加人员组织范围(中心或部门) */
  271. private void appendPersonScope(StringBuilder sql, String orgName, String companyId, boolean deptScope)
  272. throws BOSException, SQLException {
  273. if (deptScope) {
  274. ReportOrgUtil.appendDeptPersonScope(sql, ctx, orgName, companyId);
  275. } else {
  276. ReportOrgUtil.appendCenterPersonScope(sql, ctx, orgName, companyId);
  277. }
  278. }
  279. /**
  280. * 按最高学历(FIsHighest=1)分组统计各学历区间人数。
  281. *
  282. * @param orgName 中心或部门名称(FNAME_L2)
  283. * @param companyId 公司FID,可为空
  284. * @param deptScope true=部门范围,false=中心范围
  285. * @return key=学历区间编码, value=人数
  286. */
  287. private Map<String, Integer> getEducationStat(String orgName, String companyId, boolean deptScope)
  288. throws BOSException, SQLException {
  289. Map<String, Integer> stat = initEducationStat();
  290. StringBuilder sql = new StringBuilder();
  291. sql.append("SELECT diploma.FNumber as eduLevel, COUNT(*) as personCount ");
  292. appendPersonBaseFrom(sql);
  293. sql.append("LEFT JOIN T_HR_PersonDegree edu ON edu.FPersonId = person.FID ");
  294. sql.append("LEFT JOIN T_BD_HRDiploma diploma ON diploma.FID = edu.FDiploma ");
  295. sql.append("WHERE type.FIsOnTheStrength = 1 ");
  296. sql.append("AND edu.FIsHighest = 1 ");
  297. appendPersonScope(sql, orgName, companyId, deptScope);
  298. sql.append("GROUP BY diploma.FNumber");
  299. logger.error("学历统计SQL[" + orgName + "]: " + sql.toString());
  300. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  301. while (rs.next()) {
  302. // 学历编码
  303. String eduLevel = rs.getString("eduLevel");
  304. // 该学历人数
  305. int count = rs.getInt("personCount");
  306. if (StringUtils.isEmpty(eduLevel)) {
  307. continue;
  308. }
  309. if ("06".equals(eduLevel) || "07".equals(eduLevel) || "08".equals(eduLevel)) {
  310. stat.put("masterAbove", stat.get("masterAbove") + count);
  311. } else if ("05".equals(eduLevel)) {
  312. stat.put("bachelor", stat.get("bachelor") + count);
  313. } else if ("04".equals(eduLevel)) {
  314. stat.put("college", stat.get("college") + count);
  315. } else if ("02".equals(eduLevel) || "03".equals(eduLevel)) {
  316. stat.put("highSchool", stat.get("highSchool") + count);
  317. } else if ("01".equals(eduLevel)) {
  318. stat.put("juniorBelow", stat.get("juniorBelow") + count);
  319. }
  320. }
  321. logger.error("学历统计结果[" + orgName + "]: " + stat);
  322. return stat;
  323. }
  324. /**
  325. * 按出生日期计算年龄并分组统计。
  326. *
  327. * @return key=年龄区间, value=人数
  328. */
  329. private Map<String, Integer> getAgeStat(String orgName, String companyId, boolean deptScope)
  330. throws BOSException, SQLException {
  331. Map<String, Integer> stat = initAgeStat();
  332. StringBuilder sql = new StringBuilder();
  333. sql.append("SELECT DATEDIFF(YEAR, person.FBirthday, GETDATE()) as age, COUNT(*) as personCount ");
  334. appendPersonBaseFrom(sql);
  335. sql.append("WHERE type.FIsOnTheStrength = 1 ");
  336. sql.append("AND person.FBirthday IS NOT NULL ");
  337. appendPersonScope(sql, orgName, companyId, deptScope);
  338. sql.append("GROUP BY DATEDIFF(YEAR, person.FBirthday, GETDATE())");
  339. logger.error("年龄统计SQL[" + orgName + "]: " + sql.toString());
  340. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  341. while (rs.next()) {
  342. // 年龄值
  343. int age = rs.getInt("age");
  344. // 该年龄人数
  345. int count = rs.getInt("personCount");
  346. if (age < 25) {
  347. stat.put("below25", stat.get("below25") + count);
  348. } else if (age <= 30) {
  349. stat.put("age26_30", stat.get("age26_30") + count);
  350. } else if (age <= 35) {
  351. stat.put("age31_35", stat.get("age31_35") + count);
  352. } else if (age <= 40) {
  353. stat.put("age36_40", stat.get("age36_40") + count);
  354. } else {
  355. stat.put("above40", stat.get("above40") + count);
  356. }
  357. }
  358. logger.error("年龄统计结果[" + orgName + "]: " + stat);
  359. return stat;
  360. }
  361. /**
  362. * 按入司日期(FJoinDate)计算司龄月数并分组统计。
  363. *
  364. * @return key=司龄区间, value=人数
  365. */
  366. private Map<String, Integer> getWorkAgeStat(String orgName, String companyId, boolean deptScope)
  367. throws BOSException, SQLException {
  368. Map<String, Integer> stat = initWorkAgeStat();
  369. StringBuilder sql = new StringBuilder();
  370. sql.append("SELECT DATEDIFF(MONTH, personPos.FJoinDate, GETDATE()) as workMonths, COUNT(*) as personCount ");
  371. appendPersonBaseFrom(sql);
  372. sql.append("WHERE type.FIsOnTheStrength = 1 ");
  373. sql.append("AND personPos.FJoinDate IS NOT NULL ");
  374. appendPersonScope(sql, orgName, companyId, deptScope);
  375. sql.append("GROUP BY DATEDIFF(MONTH, personPos.FJoinDate, GETDATE())");
  376. logger.error("司龄统计SQL[" + orgName + "]: " + sql.toString());
  377. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  378. while (rs.next()) {
  379. // 司龄月数
  380. int workMonths = rs.getInt("workMonths");
  381. // 该区间人数
  382. int count = rs.getInt("personCount");
  383. if (workMonths < 6) {
  384. stat.put("below6Months", stat.get("below6Months") + count);
  385. } else if (workMonths < 12) {
  386. stat.put("age6M_1Y", stat.get("age6M_1Y") + count);
  387. } else if (workMonths < 36) {
  388. stat.put("age1_3Y", stat.get("age1_3Y") + count);
  389. } else if (workMonths < 60) {
  390. stat.put("age3_5Y", stat.get("age3_5Y") + count);
  391. } else if (workMonths < 120) {
  392. stat.put("age5_10Y", stat.get("age5_10Y") + count);
  393. } else {
  394. stat.put("above10Y", stat.get("above10Y") + count);
  395. }
  396. }
  397. logger.error("司龄统计结果[" + orgName + "]: " + stat);
  398. return stat;
  399. }
  400. /**
  401. * 按性别FGender枚举(Genders: 1=男, 2=女)统计男女人数。
  402. *
  403. * @return int[0]=男性人数, int[1]=女性人数
  404. */
  405. private int[] getGenderStat(String orgName, String companyId, boolean deptScope)
  406. throws BOSException, SQLException {
  407. int male = 0;
  408. int female = 0;
  409. StringBuilder sql = new StringBuilder();
  410. sql.append("SELECT person.FGender as genderValue, COUNT(*) as personCount ");
  411. appendPersonBaseFrom(sql);
  412. sql.append("WHERE type.FIsOnTheStrength = 1 ");
  413. sql.append("AND person.FGender IS NOT NULL ");
  414. appendPersonScope(sql, orgName, companyId, deptScope);
  415. sql.append("GROUP BY person.FGender");
  416. logger.error("性别统计SQL[" + orgName + "]: " + sql.toString());
  417. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  418. while (rs.next()) {
  419. // 性别枚举(1=男, 2=女)
  420. int genderValue = rs.getInt("genderValue");
  421. // 该性别人数
  422. int count = rs.getInt("personCount");
  423. if (isMale(genderValue)) {
  424. male += count;
  425. } else if (isFemale(genderValue)) {
  426. female += count;
  427. }
  428. }
  429. logger.error("性别统计结果[" + orgName + "]: male=" + male + ", female=" + female);
  430. return new int[]{male, female};
  431. }
  432. /**
  433. * 人员统计公共FROM子句(人员+任职+员工类型)。
  434. */
  435. private void appendPersonBaseFrom(StringBuilder sql) {
  436. sql.append("FROM T_BD_PERSON person ");
  437. sql.append("LEFT JOIN T_HR_PersonPosition personPos ON person.FID = personPos.FPERSONID ");
  438. sql.append("LEFT JOIN T_HR_BDEmployeeType type ON type.FID = person.FEMPLOYEETYPEID ");
  439. }
  440. /** 判断是否为男性(FGender枚举1) */
  441. private boolean isMale(int genderValue) {
  442. return genderValue == 1;
  443. }
  444. /** 判断是否为女性(FGender枚举2) */
  445. private boolean isFemale(int genderValue) {
  446. return genderValue == 2;
  447. }
  448. /** 汇总Map中所有计数值 */
  449. private int sumMap(Map<String, Integer> stat) {
  450. int total = 0;
  451. for (Integer value : stat.values()) {
  452. total += value == null ? 0 : value.intValue();
  453. }
  454. return total;
  455. }
  456. /** 初始化学历统计Map,各区间默认0 */
  457. private Map<String, Integer> initEducationStat() {
  458. Map<String, Integer> stat = new HashMap<String, Integer>();
  459. stat.put("masterAbove", 0);
  460. stat.put("bachelor", 0);
  461. stat.put("college", 0);
  462. stat.put("highSchool", 0);
  463. stat.put("juniorBelow", 0);
  464. return stat;
  465. }
  466. /** 初始化年龄统计Map */
  467. private Map<String, Integer> initAgeStat() {
  468. Map<String, Integer> stat = new HashMap<String, Integer>();
  469. stat.put("below25", 0);
  470. stat.put("age26_30", 0);
  471. stat.put("age31_35", 0);
  472. stat.put("age36_40", 0);
  473. stat.put("above40", 0);
  474. return stat;
  475. }
  476. /** 初始化司龄统计Map */
  477. private Map<String, Integer> initWorkAgeStat() {
  478. Map<String, Integer> stat = new HashMap<String, Integer>();
  479. stat.put("below6Months", 0);
  480. stat.put("age6M_1Y", 0);
  481. stat.put("age1_3Y", 0);
  482. stat.put("age3_5Y", 0);
  483. stat.put("age5_10Y", 0);
  484. stat.put("above10Y", 0);
  485. return stat;
  486. }
  487. }