CenterPersonnelStatHandler.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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.eas.custom.perfweb.util.PersonnelStatUtil;
  7. import com.kingdee.eas.custom.perfweb.util.ReportFilterUtil;
  8. import com.kingdee.eas.custom.perfweb.util.ReportOrgUtil;
  9. import com.kingdee.shr.base.syssetting.context.SHRContext;
  10. import com.kingdee.shr.base.syssetting.exception.SHRWebException;
  11. import com.kingdee.shr.base.syssetting.json.GridDataEntity;
  12. import com.kingdee.shr.base.syssetting.web.handler.ListHandler;
  13. import org.apache.log4j.Logger;
  14. import org.springframework.ui.ModelMap;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.sql.SQLException;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. /**
  23. * 类名称: CenterPersonnelStatHandler
  24. * 功能描述: 按中心统计人员结构报表数据处理
  25. * 创建日期: 2026-06-05
  26. * 作 者: 青梧
  27. * 版 本: 1.1
  28. *
  29. * 返回字段说明:
  30. * centerCode - 中心/部门编码(FNUMBER,同名取第一个)
  31. * centerName - 中心/部门名称(合计/劳务工等特殊行;品质部为单独汇总行)
  32. * personCount - 在职总人数(含下级组织)
  33. * personPercentage - 人数占全部中心在职合计的百分比
  34. * formalCount - 正式员工人数(员工类型001)
  35. * formalPercentage - 正式员工占本中心总人数的百分比
  36. * trialCount - 试用员工人数(员工类型002)
  37. * trialPercentage - 试用员工占本中心总人数的百分比
  38. * keyTalentCount - 关键人才人数(CFGjrc=1)
  39. *
  40. * 品质部行说明:
  41. * 全集团同名「品质部」跨公司合并单独一行,与中心行无互斥关系,不计入合计行累加。
  42. */
  43. public class CenterPersonnelStatHandler extends ListHandler {
  44. private static Logger logger = Logger.getLogger(CenterPersonnelStatHandler.class);
  45. Context ctx = SHRContext.getInstance().getContext();
  46. /** 单独汇总的品质部名称(跨公司合并) */
  47. private static final String QUALITY_DEPT_NAME = "品质部";
  48. /**
  49. * 列表查询入口:按中心汇总在职人数、正式/试用/关键人才人数及占比,末尾追加品质部、劳务工与合计行。
  50. */
  51. @Override
  52. protected GridDataEntity getGridRequestData(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
  53. logger.error("CenterPersonnelStatHandler getGridRequestData------------");
  54. GridDataEntity gridDataEntity = new GridDataEntity();
  55. try {
  56. // 快筛(company字段可能为中心F7) + 列表筛选 filterItems
  57. String filterItem = ReportFilterUtil.getFilterItems(request);
  58. JSONObject fastFilterItemsJson = ReportFilterUtil.parseFastFilterJson(request);
  59. logger.error("filterItem: " + filterItem);
  60. logger.error("fastFilterItemsJson: " + fastFilterItemsJson);
  61. Map<String, String> paramMap = ReportFilterUtil.parseCompanyParams(fastFilterItemsJson, logger, false);
  62. ReportOrgUtil.OrgScope centerScope = ReportFilterUtil.buildCenterOrgScope(ctx, fastFilterItemsJson, logger);
  63. logger.error("查询参数: " + paramMap);
  64. // 公司F7筛选,用于限定中心所属公司
  65. String companyId = paramMap.get("companyId");
  66. // 劳务工人数来自基础资料手工维护,不参与中心明细循环
  67. int laborCount = PersonnelStatUtil.getLaborCount(ctx, logger);
  68. // 按中心名称去重合并(排除130)
  69. List<ReportOrgUtil.OrgNameCode> centers = getCenterList(companyId, filterItem, centerScope);
  70. int totalCount = 0;
  71. int totalFormalCount = 0;
  72. int totalTrialCount = 0;
  73. int totalKeyTalentCount = 0;
  74. List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
  75. for (ReportOrgUtil.OrgNameCode center : centers) {
  76. String centerName = center.orgName;
  77. PersonnelStatUtil.PersonnelStat stat = PersonnelStatUtil.countStructure(ctx, buildCenterStatScope(centerName, companyId), logger);
  78. Map<String, Object> row = new HashMap<String, Object>();
  79. row.put("centerCode", center.orgCode); // centerCode: 中心编码
  80. row.put("centerName", centerName); // centerName: 中心名称
  81. row.put("personCount", stat.totalCount); // personCount: 在职总人数
  82. row.put("personPercentage", 0.0); // personPercentage: 占比(第二遍回填)
  83. row.put("formalCount", stat.formalCount); // formalCount: 正式员工数
  84. row.put("formalPercentage", 0.0); // formalPercentage: 正式占比(第二遍回填)
  85. row.put("trialCount", stat.trialCount); // trialCount: 试用员工数
  86. row.put("trialPercentage", 0.0); // trialPercentage: 试用占比(第二遍回填)
  87. row.put("keyTalentCount", stat.keyTalentCount); // keyTalentCount: 关键人才数
  88. dataList.add(row);
  89. totalCount += stat.totalCount;
  90. totalFormalCount += stat.formalCount;
  91. totalTrialCount += stat.trialCount;
  92. totalKeyTalentCount += stat.keyTalentCount;
  93. }
  94. // 品质部单独一行(跨公司合并,与中心行无互斥关系,不计入合计累加)
  95. PersonnelStatUtil.PersonnelStat qualityStat = PersonnelStatUtil.countStructure(ctx,
  96. PersonnelStatUtil.StatScope.dept(QUALITY_DEPT_NAME, companyId), logger);
  97. Map<String, Object> qualityRow = new HashMap<String, Object>();
  98. qualityRow.put("centerCode", resolveQualityDeptCode(companyId)); // centerCode: 部门编码
  99. qualityRow.put("centerName", QUALITY_DEPT_NAME); // centerName: 品质部
  100. qualityRow.put("personCount", qualityStat.totalCount);
  101. qualityRow.put("personPercentage", 0.0);
  102. qualityRow.put("formalCount", qualityStat.formalCount);
  103. qualityRow.put("formalPercentage", 0.0);
  104. qualityRow.put("trialCount", qualityStat.trialCount);
  105. qualityRow.put("trialPercentage", 0.0);
  106. qualityRow.put("keyTalentCount", qualityStat.keyTalentCount);
  107. dataList.add(qualityRow);
  108. // 第二遍循环:基于各中心合计数回填各行占比(含品质部行)
  109. for (Map<String, Object> row : dataList) {
  110. int personCount = (Integer) row.get("personCount");
  111. int formalCount = (Integer) row.get("formalCount");
  112. int trialCount = (Integer) row.get("trialCount");
  113. // 人数占比 = 本行人数 / 全部中心在职合计
  114. double personPercentage = totalCount > 0 ? (personCount * 100.0 / totalCount) : 0.0;
  115. row.put("personPercentage", String.format("%.2f", personPercentage) + "%");
  116. // 正式员工占比 = 本行正式人数 / 本行总人数
  117. double formalPercentage = personCount > 0 ? (formalCount * 100.0 / personCount) : 0.0;
  118. row.put("formalPercentage", String.format("%.2f", formalPercentage) + "%");
  119. // 试用员工占比 = 本行试用人数 / 本行总人数
  120. double trialPercentage = personCount > 0 ? (trialCount * 100.0 / personCount) : 0.0;
  121. row.put("trialPercentage", String.format("%.2f", trialPercentage) + "%");
  122. }
  123. // 正式工合计行(仅统计各中心在编人员,不含品质部重复行与劳务工)
  124. Map<String, Object> totalRow = new HashMap<String, Object>();
  125. totalRow.put("centerCode", ""); // centerCode: 合计行留空
  126. totalRow.put("centerName", "合计"); // centerName: 正式工合计
  127. totalRow.put("personCount", totalCount); // personCount: 各中心在职合计
  128. totalRow.put("personPercentage", totalCount > 0 ? "100.00%" : "0.00%");
  129. totalRow.put("formalCount", totalFormalCount); // formalCount: 正式员工合计
  130. totalRow.put("formalPercentage", totalCount > 0 ? String.format("%.2f", totalFormalCount * 100.0 / totalCount) + "%" : "0.00%");
  131. totalRow.put("trialCount", totalTrialCount); // trialCount: 试用员工合计
  132. totalRow.put("trialPercentage", totalCount > 0 ? String.format("%.2f", totalTrialCount * 100.0 / totalCount) + "%" : "0.00%");
  133. totalRow.put("keyTalentCount", totalKeyTalentCount); // keyTalentCount: 关键人才合计
  134. dataList.add(totalRow);
  135. // 劳务工行:人数取自基础资料,无正式/试用等细分字段
  136. Map<String, Object> laborRow = new HashMap<String, Object>();
  137. laborRow.put("centerCode", ""); // centerCode: 劳务工行留空
  138. laborRow.put("centerName", "劳务工"); // centerName: 劳务工标识
  139. laborRow.put("personCount", laborCount); // personCount: 劳务工人数
  140. dataList.add(laborRow);
  141. // 含劳务工的总计行
  142. int totalWithLabor = totalCount + laborCount;
  143. Map<String, Object> totalWithLaborRow = new HashMap<String, Object>();
  144. totalWithLaborRow.put("centerCode", ""); // centerCode: 含劳务工合计行留空
  145. totalWithLaborRow.put("centerName", "合计(含劳务工)"); // centerName: 含劳务工合计
  146. totalWithLaborRow.put("personCount", totalWithLabor); // personCount: 正式+劳务工合计
  147. dataList.add(totalWithLaborRow);
  148. gridDataEntity.setRows(dataList);
  149. gridDataEntity.setTotal(dataList.size());
  150. gridDataEntity.setPage(1);
  151. gridDataEntity.setRecords(dataList.size());
  152. logger.error("按中心统计报表数据查询成功,总记录数: " + dataList.size());
  153. } catch (SQLException e) {
  154. logger.error("数据库查询失败", e);
  155. throw new SHRWebException("数据查询失败: " + e.getMessage());
  156. } catch (BOSException e) {
  157. logger.error("业务异常", e);
  158. throw new SHRWebException("业务处理失败: " + e.getMessage());
  159. } catch (Exception e) {
  160. logger.error("未知异常", e);
  161. throw new SHRWebException("系统异常: " + e.getMessage());
  162. }
  163. ReportFilterUtil.applyGridUserData(request, gridDataEntity);
  164. return gridDataEntity;
  165. }
  166. /**
  167. * 查询品质部编码(跨公司同名取第一个)。
  168. */
  169. private String resolveQualityDeptCode(String companyId) throws BOSException, SQLException {
  170. List<ReportOrgUtil.OrgNameCode> depts = ReportOrgUtil.getDistinctOrgNameCodes(ctx, ReportOrgUtil.LAYER_DEPT, companyId);
  171. for (ReportOrgUtil.OrgNameCode dept : depts) {
  172. if (QUALITY_DEPT_NAME.equals(dept.orgName)) {
  173. return dept.orgCode;
  174. }
  175. }
  176. return "";
  177. }
  178. /**
  179. * 查询去重后的中心名称编码列表(跨公司合并,排除130)。
  180. */
  181. private List<ReportOrgUtil.OrgNameCode> getCenterList(String companyId, String filterItemsStr,
  182. ReportOrgUtil.OrgScope centerScope) throws BOSException, SQLException {
  183. List<ReportOrgUtil.OrgNameCode> centers = ReportOrgUtil.getDistinctOrgNameCodes(
  184. ctx, ReportOrgUtil.LAYER_CENTER, companyId);
  185. centers = applyCenterFastFilter(centers, centerScope);
  186. List<ReportOrgUtil.OrgNameCode> result = ReportOrgUtil.filterOrgNameCodes(centers, filterItemsStr);
  187. logger.error("中心数量: " + result.size());
  188. return result;
  189. }
  190. private List<ReportOrgUtil.OrgNameCode> applyCenterFastFilter(List<ReportOrgUtil.OrgNameCode> centers,
  191. ReportOrgUtil.OrgScope centerScope) {
  192. if (centerScope == null) {
  193. return centers;
  194. }
  195. if (!StringUtils.isEmpty(centerScope.orgName)) {
  196. List<ReportOrgUtil.OrgNameCode> filtered = new ArrayList<ReportOrgUtil.OrgNameCode>();
  197. for (ReportOrgUtil.OrgNameCode center : centers) {
  198. if (centerScope.orgName.equals(center.orgName)) {
  199. filtered.add(center);
  200. }
  201. }
  202. return filtered.isEmpty() ? centers : filtered;
  203. }
  204. return centers;
  205. }
  206. private PersonnelStatUtil.StatScope buildCenterStatScope(String centerName, String companyId) {
  207. return PersonnelStatUtil.StatScope.center(centerName, companyId);
  208. }
  209. }