CenterPerfStatReportHandlerEx.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. package com.kingdee.eas.custom.perfweb.handler;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.kingdee.bos.Context;
  5. import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
  6. import com.kingdee.eas.base.permission.UserInfo;
  7. import com.kingdee.eas.custom.perfweb.AssessmentResultDescriptionFactory;
  8. import com.kingdee.eas.custom.perfweb.AssessmentResultDescriptionInfo;
  9. import com.kingdee.eas.custom.perfweb.IAssessmentResultDescription;
  10. import com.kingdee.eas.custom.perfweb.bizEnum.AutoStatusEnum;
  11. import com.kingdee.eas.custom.perfweb.service.CenterPerfStatService;
  12. import com.kingdee.eas.custom.perfweb.utils.FeishuMessageUtil;
  13. import com.kingdee.eas.custom.perfweb.utils.UrlParamUtil;
  14. import com.kingdee.eas.util.app.ContextUtil;
  15. import com.kingdee.eas.util.app.DbUtil;
  16. import com.kingdee.jdbc.rowset.IRowSet;
  17. import com.kingdee.shr.base.syssetting.context.SHRContext;
  18. import com.kingdee.shr.base.syssetting.exception.SHRWebException;
  19. import com.kingdee.shr.base.syssetting.exception.ShrWebBizException;
  20. import com.kingdee.shr.base.syssetting.json.GridDataEntity;
  21. import com.kingdee.shr.base.syssetting.web.handler.ListHandler;
  22. import com.kingdee.shr.base.syssetting.web.json.JSONUtils;
  23. import com.kingdee.shr.perfweb.app.base.evalplan.PerfPeriodFactory;
  24. import com.kingdee.shr.perfweb.app.base.evalplan.PerfPeriodInfo;
  25. import org.apache.commons.lang3.StringUtils;
  26. import org.apache.log4j.Logger;
  27. import org.springframework.ui.ModelMap;
  28. import javax.servlet.http.HttpServletRequest;
  29. import javax.servlet.http.HttpServletResponse;
  30. import java.util.ArrayList;
  31. import java.util.HashMap;
  32. import java.util.LinkedHashMap;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.Set;
  36. /**
  37. * 绩效考核结果报表 ListHandler:getGridRequestData 加载表格数据,approve 审批发飞书待办。
  38. */
  39. public class CenterPerfStatReportHandlerEx extends ListHandler {
  40. private final Logger logger = Logger.getLogger(CenterPerfStatReportHandlerEx.class);
  41. private final Context ctx = SHRContext.getInstance().getContext();
  42. /**
  43. * 报表列表数据:调 getCenterPerfStatService(CenterPerfStatService.queryReport)后封装 GridDataEntity。
  44. * periodId 未选时由 Service 取考核中最新周期;组织未选时按系统行政组织范围过滤。
  45. */
  46. @Override
  47. protected GridDataEntity getGridRequestData(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
  48. try {
  49. ListFilterParams filterParams = parseListFilterParams(request);
  50. Map<String, Object> queryResult = CenterPerfStatService.queryReport(ctx, filterParams.periodId, filterParams.orgId, filterParams.adminOrgRangeIds);
  51. if (!"200".equals(String.valueOf(queryResult.get("code")))) {
  52. throw new ShrWebBizException(String.valueOf(queryResult.get("msg")));
  53. }
  54. Map<String, Object> data = (Map<String, Object>) queryResult.get("data");
  55. List<Map<String, Object>> allRows = data != null && data.get("rows") != null ? (List<Map<String, Object>>) data.get("rows") : new ArrayList<>();
  56. int rows = Integer.parseInt(request.getParameter("rows"));
  57. int page = Integer.parseInt(request.getParameter("page"));
  58. int records = allRows.size();
  59. int from = Math.max((page - 1) * rows, 0);
  60. int to = Math.min(from + rows, records);
  61. List<Map<String, Object>> pageRows = from < to ? allRows.subList(from, to) : new ArrayList<>();
  62. GridDataEntity grid = new GridDataEntity();
  63. grid.setRecords(records);
  64. grid.setPage(page);
  65. grid.setTotal(records == 0 ? 0 : (records % rows == 0 ? records / rows : records / rows + 1));
  66. grid.setRows(pageRows);
  67. Map<String, Object> userdata = new HashMap<>();
  68. if (data != null) {
  69. userdata.put("periodId", data.get("periodId"));
  70. userdata.put("periodName", data.get("periodName"));
  71. userdata.put("gradePercents", data.get("gradePercents"));
  72. }
  73. grid.setUserdata(userdata);
  74. return grid;
  75. } catch (ShrWebBizException e) {
  76. throw e;
  77. } catch (Exception e) {
  78. logger.error("绩效考核结果报表加载失败", e);
  79. throw new ShrWebBizException(e.getMessage());
  80. }
  81. }
  82. /**
  83. * 从高级过滤 JSON 取 F7 选中项 id(dataItem)
  84. */
  85. private String getFilterId(JSONObject filterJson, String key) {
  86. if (filterJson == null || !filterJson.containsKey(key)) {
  87. return null;
  88. }
  89. JSONObject item = filterJson.getJSONObject(key);
  90. if (item == null) {
  91. return null;
  92. }
  93. JSONArray dataItem = item.getJSONArray("dataItem");
  94. if (dataItem == null || dataItem.isEmpty()) {
  95. return null;
  96. }
  97. return dataItem.getJSONObject(0).getString("id");
  98. }
  99. /**
  100. * 从高级过滤 JSON 取 values 字段(与前端 getFastFilterItems().period.values 一致)
  101. */
  102. private String getFilterValues(JSONObject filterJson, String key) {
  103. if (filterJson == null || !filterJson.containsKey(key)) {
  104. return null;
  105. }
  106. JSONObject item = filterJson.getJSONObject(key);
  107. if (item == null) {
  108. return null;
  109. }
  110. return StringUtils.trimToNull(item.getString("values"));
  111. }
  112. private String resolvePeriodId(JSONObject filterJson, HttpServletRequest request) {
  113. String periodId = getFilterId(filterJson, "period");
  114. if (StringUtils.isBlank(periodId)) {
  115. periodId = getFilterId(filterJson, "periodId");
  116. }
  117. if (StringUtils.isBlank(periodId)) {
  118. periodId = getFilterValues(filterJson, "period");
  119. }
  120. if (StringUtils.isBlank(periodId)) {
  121. periodId = getFilterValues(filterJson, "periodId");
  122. }
  123. if (StringUtils.isBlank(periodId)) {
  124. periodId = StringUtils.trimToNull(request.getParameter("periodId"));
  125. }
  126. return periodId;
  127. }
  128. private String resolveOrgId(JSONObject filterJson) {
  129. String orgId = getFilterId(filterJson, "orgId");
  130. if (StringUtils.isBlank(orgId)) {
  131. orgId = getFilterId(filterJson, "adminOrg");
  132. }
  133. if (StringUtils.isBlank(orgId)) {
  134. orgId = getFilterValues(filterJson, "orgId");
  135. }
  136. if (StringUtils.isBlank(orgId)) {
  137. orgId = getFilterValues(filterJson, "adminOrg");
  138. }
  139. return orgId;
  140. }
  141. /**
  142. * 审批:action=hrStart|directorApprove|gmApprove(与前端 remoteCall method=approve 对应)。
  143. * billIds 由 fastFilterItems 查询条件及单据状态自动解析,无需勾选行。
  144. */
  145. public void approveAction(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
  146. Map<String, Object> res = new LinkedHashMap<>();
  147. String action = request.getParameter("action");
  148. ListFilterParams filterParams = parseListFilterParams(request);
  149. String periodId = filterParams.periodId;
  150. UserInfo user = ContextUtil.getCurrentUserInfo(ctx);
  151. String creatorId = user != null && user.getPerson() != null ? user.getPerson().getNumber() : null;
  152. if (StringUtils.isBlank(creatorId)) {
  153. res.put("code", "500");
  154. res.put("msg", "无法获取当前登录人工号");
  155. JSONUtils.writeJson(response, res);
  156. return;
  157. }
  158. AutoStatusEnum expect;
  159. AutoStatusEnum target;
  160. String nextCode;
  161. String doneCode;
  162. String emptyMsg;
  163. String successMsgPrefix;
  164. if ("hrStart".equals(action)) {
  165. expect = AutoStatusEnum.WFQ;
  166. target = AutoStatusEnum.ZJSPZ;
  167. nextCode = "JXJG02";
  168. doneCode = null;
  169. emptyMsg = "当前查询条件下没有可发起的单据";
  170. successMsgPrefix = "发起审批成功";
  171. } else if ("directorApprove".equals(action)) {
  172. expect = AutoStatusEnum.ZJSPZ;
  173. target = AutoStatusEnum.ZJLSPZ;
  174. nextCode = "JXJG03";
  175. doneCode = "JXJG02";
  176. emptyMsg = "当前查询条件下没有总监审批中的单据";
  177. successMsgPrefix = "总监审批通过";
  178. } else if ("gmApprove".equals(action)) {
  179. expect = AutoStatusEnum.ZJLSPZ;
  180. target = AutoStatusEnum.SPTG;
  181. nextCode = null;
  182. doneCode = "JXJG03";
  183. emptyMsg = "当前查询条件下没有总经理审批中的单据";
  184. successMsgPrefix = "总经理审批通过";
  185. } else {
  186. res.put("code", "500");
  187. res.put("msg", "不支持的 action:" + action);
  188. JSONUtils.writeJson(response, res);
  189. return;
  190. }
  191. String billIds;
  192. try {
  193. billIds = resolveApproveBillIds(periodId, filterParams.orgId, filterParams.adminOrgRangeIds, expect.getValue());
  194. } catch (ShrWebBizException e) {
  195. res.put("code", "500");
  196. res.put("msg", e.getMessage());
  197. JSONUtils.writeJson(response, res);
  198. return;
  199. }
  200. if (StringUtils.isBlank(billIds)) {
  201. res.put("code", "500");
  202. res.put("msg", emptyMsg);
  203. JSONUtils.writeJson(response, res);
  204. return;
  205. }
  206. if ("hrStart".equals(action)) {
  207. String gradeCheckMsg = CenterPerfStatService.validateGradeActualEqualsTotalForHrStart(
  208. ctx, periodId, filterParams.orgId, filterParams.adminOrgRangeIds, expect.getValue());
  209. if (gradeCheckMsg != null) {
  210. res.put("code", "500");
  211. res.put("msg", gradeCheckMsg);
  212. JSONUtils.writeJson(response, res);
  213. return;
  214. }
  215. }
  216. try {
  217. String periodName = "";
  218. if (StringUtils.isBlank(periodId)) {
  219. periodId = CenterPerfStatService.resolveDefaultPeriodId(ctx);
  220. }
  221. if (StringUtils.isNotBlank(periodId)) {
  222. PerfPeriodInfo period = PerfPeriodFactory.getLocalInstance(ctx).getPerfPeriodInfo("where id = '" + periodId + "'");
  223. if (period != null) {
  224. periodName = period.getName();
  225. }
  226. }
  227. String title = "绩效考核结果待审批【" + periodName + "】,请及时处理";
  228. String pcurl = UrlParamUtil.buildCenterPerfStatReportListOaUrl(periodId);
  229. String appurl = pcurl;
  230. String nextReceiver = nextCode != null ? getHrBaseNumber(ctx, nextCode) : null;
  231. if (nextCode != null && StringUtils.isBlank(nextReceiver)) {
  232. res.put("code", "500");
  233. res.put("msg", "未配置审批人工号:" + nextCode);
  234. JSONUtils.writeJson(response, res);
  235. return;
  236. }
  237. IAssessmentResultDescription iBill = AssessmentResultDescriptionFactory.getLocalInstance(ctx);
  238. List<String> validBillIds = new ArrayList<>();
  239. for (String billId : billIds.split(",")) {
  240. billId = billId.trim();
  241. if (StringUtils.isBlank(billId)) {
  242. continue;
  243. }
  244. AssessmentResultDescriptionInfo bill = iBill.getAssessmentResultDescriptionInfo(new ObjectUuidPK(billId));
  245. if (bill == null) {
  246. res.put("code", "500");
  247. res.put("msg", "未找到单据:" + billId);
  248. JSONUtils.writeJson(response, res);
  249. return;
  250. }
  251. if (bill.getAutoStatus() != expect) {
  252. res.put("code", "500");
  253. res.put("msg", "单据[" + bill.getNumber() + "]状态不正确,无法执行该操作");
  254. JSONUtils.writeJson(response, res);
  255. return;
  256. }
  257. validBillIds.add(billId);
  258. }
  259. // 办结上一节点待办:flowId 与推送时一致(周期 + 接收人 + 待办时状态)
  260. if (doneCode != null) {
  261. String doneReceiver = getHrBaseNumber(ctx, doneCode);
  262. if (StringUtils.isBlank(doneReceiver)) {
  263. res.put("code", "500");
  264. res.put("msg", "未配置审批人工号:" + doneCode);
  265. JSONUtils.writeJson(response, res);
  266. return;
  267. }
  268. String doneFlowId = buildCenterPerfFlowId(periodId, doneReceiver, expect.getValue());
  269. if (target == AutoStatusEnum.SPTG) {
  270. FeishuMessageUtil.sendFeishuCall(doneReceiver, title, pcurl, "绩效考核结果审批", appurl, doneFlowId,
  271. FeishuMessageUtil.VIEWTYPE_READ, FeishuMessageUtil.ISREMARK_COMPLETE, creatorId);
  272. } else {
  273. FeishuMessageUtil.sendFeishuCall(doneReceiver, title, pcurl, "绩效考核结果审批", appurl, doneFlowId,
  274. FeishuMessageUtil.VIEWTYPE_READ, FeishuMessageUtil.ISREMARK_DONE, creatorId);
  275. }
  276. }
  277. for (String billId : validBillIds) {
  278. AssessmentResultDescriptionInfo bill = iBill.getAssessmentResultDescriptionInfo(new ObjectUuidPK(billId));
  279. bill.setAutoStatus(target);
  280. iBill.update(new ObjectUuidPK(billId), bill);
  281. }
  282. // 推送下一节点待办(每个周期每个接收人一条,总监审批后 HR 推给总监的那条 flowId 已被置为已办)
  283. if (nextReceiver != null && !validBillIds.isEmpty()) {
  284. String nextFlowId = buildCenterPerfFlowId(periodId, nextReceiver, target.getValue());
  285. FeishuMessageUtil.sendFeishuTodo(nextReceiver, title, pcurl, "绩效考核结果审批", appurl, nextFlowId, creatorId);
  286. }
  287. int processedCount = validBillIds.size();
  288. res.put("code", "200");
  289. res.put("msg", buildSuccessMsg(successMsgPrefix, periodName, processedCount, action));
  290. } catch (Exception e) {
  291. logger.error("绩效考核结果审批失败", e);
  292. res.put("code", "500");
  293. res.put("msg", "审批失败:" + e.getMessage());
  294. }
  295. JSONUtils.writeJson(response, res);
  296. }
  297. /**
  298. * 解析列表快速过滤:考核周期、组织及行政组织范围(与前端 getFastFilterItems 同源)。
  299. */
  300. private ListFilterParams parseListFilterParams(HttpServletRequest request) throws SHRWebException {
  301. ListFilterParams params = new ListFilterParams();
  302. String fastFilterItems = request.getParameter("fastFilterItems");
  303. JSONObject filterJson = StringUtils.isNotBlank(fastFilterItems) ? JSONObject.parseObject(fastFilterItems) : null;
  304. params.periodId = resolvePeriodId(filterJson, request);
  305. params.orgId = resolveOrgId(filterJson);
  306. if (StringUtils.isBlank(params.orgId)) {
  307. params.adminOrgRangeIds = getCurrentRequestAdminOrgRangeIdSet(request);
  308. }
  309. return params;
  310. }
  311. /**
  312. * 按查询条件及目标状态解析待处理单据 id。
  313. */
  314. private String resolveApproveBillIds(String periodId, String orgId, Set<String> adminOrgRangeIds,
  315. String expectStatusValue) throws ShrWebBizException {
  316. Map<String, Object> queryResult = CenterPerfStatService.queryReport(ctx, periodId, orgId, adminOrgRangeIds);
  317. if (!"200".equals(String.valueOf(queryResult.get("code")))) {
  318. throw new ShrWebBizException(String.valueOf(queryResult.get("msg")));
  319. }
  320. Map<String, Object> data = (Map<String, Object>) queryResult.get("data");
  321. List<Map<String, Object>> rows = data != null && data.get("rows") != null ? (List<Map<String, Object>>) data.get("rows") : new ArrayList<>();
  322. List<String> billIds = new ArrayList<>();
  323. for (Map<String, Object> row : rows) {
  324. if (!expectStatusValue.equals(String.valueOf(row.get("autoStatus")))) {
  325. continue;
  326. }
  327. Object id = row.get("id");
  328. if (id != null && StringUtils.isNotBlank(String.valueOf(id))) {
  329. billIds.add(String.valueOf(id));
  330. }
  331. }
  332. return StringUtils.join(billIds, ",");
  333. }
  334. private static class ListFilterParams {
  335. private String periodId;
  336. private String orgId;
  337. private Set<String> adminOrgRangeIds;
  338. }
  339. private String buildSuccessMsg(String prefix, String periodName, int count, String action) {
  340. String periodPart = StringUtils.isNotBlank(periodName) ? "【" + periodName + "】" : "";
  341. String countPart = ",共处理 " + count + " 条绩效考核结果";
  342. if ("hrStart".equals(action)) {
  343. return prefix + periodPart + countPart + ",已推送总监审批待办";
  344. }
  345. if ("directorApprove".equals(action)) {
  346. return prefix + periodPart + countPart + ",总监待办已办结,已推送总经理审批待办";
  347. }
  348. if ("gmApprove".equals(action)) {
  349. return prefix + periodPart + countPart + ",审批流程已全部完成";
  350. }
  351. return prefix + periodPart + countPart;
  352. }
  353. private String getHrBaseNumber(Context ctx, String code) throws Exception {
  354. if (StringUtils.isBlank(code)) {
  355. return null;
  356. }
  357. String sql = "select FSimpleName from T_HR_ZDY0HRBase where FNumber=? and FState=1";
  358. IRowSet rowSet = DbUtil.executeQuery(ctx, sql, new Object[]{code});
  359. if (rowSet != null && rowSet.next()) {
  360. return StringUtils.trimToNull(rowSet.getString("FSimpleName"));
  361. }
  362. return null;
  363. }
  364. /**
  365. * 飞书待办 flowId:{考核周期短码}_{接收人工号}_{状态}
  366. * <p>
  367. * 周期 ID 形如 LJiKDANrQyeBpHqSZW/o/UUaiOU=,仅取 / 前一段,避免 flowId 过长。
  368. * 推送待办与办结须使用相同 flowId。例如 HR 发起推总监时状态为 ZJSPZ,总监审批办结时也用 ZJSPZ。
  369. */
  370. private String buildCenterPerfFlowId(String periodId, String receiverNumber, String statusValue) {
  371. return toShortPeriodId(periodId) + "_" + toFlowIdPart(receiverNumber)
  372. + "_" + toFlowIdPart(statusValue);
  373. }
  374. /** BOS 主键含 /、=,flowId 只保留第一段(/ 之前)。 */
  375. private String toShortPeriodId(String periodId) {
  376. if (StringUtils.isBlank(periodId)) {
  377. return "";
  378. }
  379. String trimmed = periodId.trim();
  380. int slash = trimmed.indexOf('/');
  381. if (slash > 0) {
  382. return trimmed.substring(0, slash);
  383. }
  384. return trimmed.replace("=", "_");
  385. }
  386. private String toFlowIdPart(String value) {
  387. if (StringUtils.isBlank(value)) {
  388. return "";
  389. }
  390. return value.trim().replace("/", "_").replace("=", "_");
  391. }
  392. }