package com.kingdee.eas.custom.perfweb.handler; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.kingdee.bos.Context; import com.kingdee.bos.dao.ormapping.ObjectUuidPK; import com.kingdee.eas.base.permission.UserInfo; import com.kingdee.eas.custom.perfweb.AssessmentResultDescriptionFactory; import com.kingdee.eas.custom.perfweb.AssessmentResultDescriptionInfo; import com.kingdee.eas.custom.perfweb.IAssessmentResultDescription; import com.kingdee.eas.custom.perfweb.bizEnum.AutoStatusEnum; import com.kingdee.eas.custom.perfweb.service.CenterPerfStatService; import com.kingdee.eas.custom.perfweb.utils.FeishuMessageUtil; import com.kingdee.eas.custom.perfweb.utils.UrlParamUtil; import com.kingdee.eas.util.app.ContextUtil; import com.kingdee.eas.util.app.DbUtil; import com.kingdee.jdbc.rowset.IRowSet; import com.kingdee.shr.base.syssetting.context.SHRContext; import com.kingdee.shr.base.syssetting.exception.SHRWebException; import com.kingdee.shr.base.syssetting.exception.ShrWebBizException; import com.kingdee.shr.base.syssetting.json.GridDataEntity; import com.kingdee.shr.base.syssetting.web.handler.ListHandler; import com.kingdee.shr.base.syssetting.web.json.JSONUtils; import com.kingdee.shr.perfweb.app.base.evalplan.PerfPeriodFactory; import com.kingdee.shr.perfweb.app.base.evalplan.PerfPeriodInfo; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.springframework.ui.ModelMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * 绩效考核结果报表 ListHandler:getGridRequestData 加载表格数据,approve 审批发飞书待办。 */ public class CenterPerfStatReportHandlerEx extends ListHandler { private final Logger logger = Logger.getLogger(CenterPerfStatReportHandlerEx.class); private final Context ctx = SHRContext.getInstance().getContext(); /** * 报表列表数据:调 getCenterPerfStatService(CenterPerfStatService.queryReport)后封装 GridDataEntity。 * periodId 未选时由 Service 取考核中最新周期;组织未选时按系统行政组织范围过滤。 */ @Override protected GridDataEntity getGridRequestData(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException { try { ListFilterParams filterParams = parseListFilterParams(request); Map queryResult = CenterPerfStatService.queryReport(ctx, filterParams.periodId, filterParams.orgId, filterParams.adminOrgRangeIds); if (!"200".equals(String.valueOf(queryResult.get("code")))) { throw new ShrWebBizException(String.valueOf(queryResult.get("msg"))); } Map data = (Map) queryResult.get("data"); List> allRows = data != null && data.get("rows") != null ? (List>) data.get("rows") : new ArrayList<>(); int rows = Integer.parseInt(request.getParameter("rows")); int page = Integer.parseInt(request.getParameter("page")); int records = allRows.size(); int from = Math.max((page - 1) * rows, 0); int to = Math.min(from + rows, records); List> pageRows = from < to ? allRows.subList(from, to) : new ArrayList<>(); GridDataEntity grid = new GridDataEntity(); grid.setRecords(records); grid.setPage(page); grid.setTotal(records == 0 ? 0 : (records % rows == 0 ? records / rows : records / rows + 1)); grid.setRows(pageRows); Map userdata = new HashMap<>(); if (data != null) { userdata.put("periodId", data.get("periodId")); userdata.put("periodName", data.get("periodName")); userdata.put("gradePercents", data.get("gradePercents")); } grid.setUserdata(userdata); return grid; } catch (ShrWebBizException e) { throw e; } catch (Exception e) { logger.error("绩效考核结果报表加载失败", e); throw new ShrWebBizException(e.getMessage()); } } /** * 从高级过滤 JSON 取 F7 选中项 id(dataItem) */ private String getFilterId(JSONObject filterJson, String key) { if (filterJson == null || !filterJson.containsKey(key)) { return null; } JSONObject item = filterJson.getJSONObject(key); if (item == null) { return null; } JSONArray dataItem = item.getJSONArray("dataItem"); if (dataItem == null || dataItem.isEmpty()) { return null; } return dataItem.getJSONObject(0).getString("id"); } /** * 从高级过滤 JSON 取 values 字段(与前端 getFastFilterItems().period.values 一致) */ private String getFilterValues(JSONObject filterJson, String key) { if (filterJson == null || !filterJson.containsKey(key)) { return null; } JSONObject item = filterJson.getJSONObject(key); if (item == null) { return null; } return StringUtils.trimToNull(item.getString("values")); } private String resolvePeriodId(JSONObject filterJson, HttpServletRequest request) { String periodId = getFilterId(filterJson, "period"); if (StringUtils.isBlank(periodId)) { periodId = getFilterId(filterJson, "periodId"); } if (StringUtils.isBlank(periodId)) { periodId = getFilterValues(filterJson, "period"); } if (StringUtils.isBlank(periodId)) { periodId = getFilterValues(filterJson, "periodId"); } if (StringUtils.isBlank(periodId)) { periodId = StringUtils.trimToNull(request.getParameter("periodId")); } return periodId; } private String resolveOrgId(JSONObject filterJson) { String orgId = getFilterId(filterJson, "orgId"); if (StringUtils.isBlank(orgId)) { orgId = getFilterId(filterJson, "adminOrg"); } if (StringUtils.isBlank(orgId)) { orgId = getFilterValues(filterJson, "orgId"); } if (StringUtils.isBlank(orgId)) { orgId = getFilterValues(filterJson, "adminOrg"); } return orgId; } /** * 审批:action=hrStart|directorApprove|gmApprove(与前端 remoteCall method=approve 对应)。 * billIds 由 fastFilterItems 查询条件及单据状态自动解析,无需勾选行。 */ public void approveAction(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException { Map res = new LinkedHashMap<>(); String action = request.getParameter("action"); ListFilterParams filterParams = parseListFilterParams(request); String periodId = filterParams.periodId; UserInfo user = ContextUtil.getCurrentUserInfo(ctx); String creatorId = user != null && user.getPerson() != null ? user.getPerson().getNumber() : null; if (StringUtils.isBlank(creatorId)) { res.put("code", "500"); res.put("msg", "无法获取当前登录人工号"); JSONUtils.writeJson(response, res); return; } AutoStatusEnum expect; AutoStatusEnum target; String nextCode; String doneCode; String emptyMsg; String successMsgPrefix; if ("hrStart".equals(action)) { expect = AutoStatusEnum.WFQ; target = AutoStatusEnum.ZJSPZ; nextCode = "JXJG02"; doneCode = null; emptyMsg = "当前查询条件下没有可发起的单据"; successMsgPrefix = "发起审批成功"; } else if ("directorApprove".equals(action)) { expect = AutoStatusEnum.ZJSPZ; target = AutoStatusEnum.ZJLSPZ; nextCode = "JXJG03"; doneCode = "JXJG02"; emptyMsg = "当前查询条件下没有总监审批中的单据"; successMsgPrefix = "总监审批通过"; } else if ("gmApprove".equals(action)) { expect = AutoStatusEnum.ZJLSPZ; target = AutoStatusEnum.SPTG; nextCode = null; doneCode = "JXJG03"; emptyMsg = "当前查询条件下没有总经理审批中的单据"; successMsgPrefix = "总经理审批通过"; } else { res.put("code", "500"); res.put("msg", "不支持的 action:" + action); JSONUtils.writeJson(response, res); return; } String billIds; try { billIds = resolveApproveBillIds(periodId, filterParams.orgId, filterParams.adminOrgRangeIds, expect.getValue()); } catch (ShrWebBizException e) { res.put("code", "500"); res.put("msg", e.getMessage()); JSONUtils.writeJson(response, res); return; } if (StringUtils.isBlank(billIds)) { res.put("code", "500"); res.put("msg", emptyMsg); JSONUtils.writeJson(response, res); return; } if ("hrStart".equals(action)) { String gradeCheckMsg = CenterPerfStatService.validateGradeActualEqualsTotalForHrStart( ctx, periodId, filterParams.orgId, filterParams.adminOrgRangeIds, expect.getValue()); if (gradeCheckMsg != null) { res.put("code", "500"); res.put("msg", gradeCheckMsg); JSONUtils.writeJson(response, res); return; } } try { String periodName = ""; if (StringUtils.isBlank(periodId)) { periodId = CenterPerfStatService.resolveDefaultPeriodId(ctx); } if (StringUtils.isNotBlank(periodId)) { PerfPeriodInfo period = PerfPeriodFactory.getLocalInstance(ctx).getPerfPeriodInfo("where id = '" + periodId + "'"); if (period != null) { periodName = period.getName(); } } String title = "绩效考核结果待审批【" + periodName + "】,请及时处理"; String pcurl = UrlParamUtil.buildCenterPerfStatReportListOaUrl(periodId); String appurl = pcurl; String nextReceiver = nextCode != null ? getHrBaseNumber(ctx, nextCode) : null; if (nextCode != null && StringUtils.isBlank(nextReceiver)) { res.put("code", "500"); res.put("msg", "未配置审批人工号:" + nextCode); JSONUtils.writeJson(response, res); return; } IAssessmentResultDescription iBill = AssessmentResultDescriptionFactory.getLocalInstance(ctx); List validBillIds = new ArrayList<>(); for (String billId : billIds.split(",")) { billId = billId.trim(); if (StringUtils.isBlank(billId)) { continue; } AssessmentResultDescriptionInfo bill = iBill.getAssessmentResultDescriptionInfo(new ObjectUuidPK(billId)); if (bill == null) { res.put("code", "500"); res.put("msg", "未找到单据:" + billId); JSONUtils.writeJson(response, res); return; } if (bill.getAutoStatus() != expect) { res.put("code", "500"); res.put("msg", "单据[" + bill.getNumber() + "]状态不正确,无法执行该操作"); JSONUtils.writeJson(response, res); return; } validBillIds.add(billId); } // 办结上一节点待办:flowId 与推送时一致(周期 + 接收人 + 待办时状态) if (doneCode != null) { String doneReceiver = getHrBaseNumber(ctx, doneCode); if (StringUtils.isBlank(doneReceiver)) { res.put("code", "500"); res.put("msg", "未配置审批人工号:" + doneCode); JSONUtils.writeJson(response, res); return; } String doneFlowId = buildCenterPerfFlowId(periodId, doneReceiver, expect.getValue()); if (target == AutoStatusEnum.SPTG) { FeishuMessageUtil.sendFeishuCall(doneReceiver, title, pcurl, "绩效考核结果审批", appurl, doneFlowId, FeishuMessageUtil.VIEWTYPE_READ, FeishuMessageUtil.ISREMARK_COMPLETE, creatorId); } else { FeishuMessageUtil.sendFeishuCall(doneReceiver, title, pcurl, "绩效考核结果审批", appurl, doneFlowId, FeishuMessageUtil.VIEWTYPE_READ, FeishuMessageUtil.ISREMARK_DONE, creatorId); } } for (String billId : validBillIds) { AssessmentResultDescriptionInfo bill = iBill.getAssessmentResultDescriptionInfo(new ObjectUuidPK(billId)); bill.setAutoStatus(target); iBill.update(new ObjectUuidPK(billId), bill); } // 推送下一节点待办(每个周期每个接收人一条,总监审批后 HR 推给总监的那条 flowId 已被置为已办) if (nextReceiver != null && !validBillIds.isEmpty()) { String nextFlowId = buildCenterPerfFlowId(periodId, nextReceiver, target.getValue()); FeishuMessageUtil.sendFeishuTodo(nextReceiver, title, pcurl, "绩效考核结果审批", appurl, nextFlowId, creatorId); } int processedCount = validBillIds.size(); res.put("code", "200"); res.put("msg", buildSuccessMsg(successMsgPrefix, periodName, processedCount, action)); } catch (Exception e) { logger.error("绩效考核结果审批失败", e); res.put("code", "500"); res.put("msg", "审批失败:" + e.getMessage()); } JSONUtils.writeJson(response, res); } /** * 解析列表快速过滤:考核周期、组织及行政组织范围(与前端 getFastFilterItems 同源)。 */ private ListFilterParams parseListFilterParams(HttpServletRequest request) throws SHRWebException { ListFilterParams params = new ListFilterParams(); String fastFilterItems = request.getParameter("fastFilterItems"); JSONObject filterJson = StringUtils.isNotBlank(fastFilterItems) ? JSONObject.parseObject(fastFilterItems) : null; params.periodId = resolvePeriodId(filterJson, request); params.orgId = resolveOrgId(filterJson); if (StringUtils.isBlank(params.orgId)) { params.adminOrgRangeIds = getCurrentRequestAdminOrgRangeIdSet(request); } return params; } /** * 按查询条件及目标状态解析待处理单据 id。 */ private String resolveApproveBillIds(String periodId, String orgId, Set adminOrgRangeIds, String expectStatusValue) throws ShrWebBizException { Map queryResult = CenterPerfStatService.queryReport(ctx, periodId, orgId, adminOrgRangeIds); if (!"200".equals(String.valueOf(queryResult.get("code")))) { throw new ShrWebBizException(String.valueOf(queryResult.get("msg"))); } Map data = (Map) queryResult.get("data"); List> rows = data != null && data.get("rows") != null ? (List>) data.get("rows") : new ArrayList<>(); List billIds = new ArrayList<>(); for (Map row : rows) { if (!expectStatusValue.equals(String.valueOf(row.get("autoStatus")))) { continue; } Object id = row.get("id"); if (id != null && StringUtils.isNotBlank(String.valueOf(id))) { billIds.add(String.valueOf(id)); } } return StringUtils.join(billIds, ","); } private static class ListFilterParams { private String periodId; private String orgId; private Set adminOrgRangeIds; } private String buildSuccessMsg(String prefix, String periodName, int count, String action) { String periodPart = StringUtils.isNotBlank(periodName) ? "【" + periodName + "】" : ""; String countPart = ",共处理 " + count + " 条绩效考核结果"; if ("hrStart".equals(action)) { return prefix + periodPart + countPart + ",已推送总监审批待办"; } if ("directorApprove".equals(action)) { return prefix + periodPart + countPart + ",总监待办已办结,已推送总经理审批待办"; } if ("gmApprove".equals(action)) { return prefix + periodPart + countPart + ",审批流程已全部完成"; } return prefix + periodPart + countPart; } private String getHrBaseNumber(Context ctx, String code) throws Exception { if (StringUtils.isBlank(code)) { return null; } String sql = "select FSimpleName from T_HR_ZDY0HRBase where FNumber=? and FState=1"; IRowSet rowSet = DbUtil.executeQuery(ctx, sql, new Object[]{code}); if (rowSet != null && rowSet.next()) { return StringUtils.trimToNull(rowSet.getString("FSimpleName")); } return null; } /** * 飞书待办 flowId:{考核周期短码}_{接收人工号}_{状态} *

* 周期 ID 形如 LJiKDANrQyeBpHqSZW/o/UUaiOU=,仅取 / 前一段,避免 flowId 过长。 * 推送待办与办结须使用相同 flowId。例如 HR 发起推总监时状态为 ZJSPZ,总监审批办结时也用 ZJSPZ。 */ private String buildCenterPerfFlowId(String periodId, String receiverNumber, String statusValue) { return toShortPeriodId(periodId) + "_" + toFlowIdPart(receiverNumber) + "_" + toFlowIdPart(statusValue); } /** BOS 主键含 /、=,flowId 只保留第一段(/ 之前)。 */ private String toShortPeriodId(String periodId) { if (StringUtils.isBlank(periodId)) { return ""; } String trimmed = periodId.trim(); int slash = trimmed.indexOf('/'); if (slash > 0) { return trimmed.substring(0, slash); } return trimmed.replace("=", "_"); } private String toFlowIdPart(String value) { if (StringUtils.isBlank(value)) { return ""; } return value.trim().replace("/", "_").replace("=", "_"); } }