package com.kingdee.eas.custom.weaverTodo.util; import com.alibaba.fastjson.JSONObject; import com.kingdee.bos.BOSException; import com.kingdee.bos.Context; import com.kingdee.bos.dao.ormapping.ObjectUuidPK; import com.kingdee.bos.metadata.IMetaDataLoader; import com.kingdee.bos.metadata.MetaDataLoaderFactory; import com.kingdee.bos.metadata.entity.EntityObjectInfo; import com.kingdee.bos.util.BOSUuid; import com.kingdee.eas.base.message.AssignReadFactory; import com.kingdee.eas.base.message.AssignReadInfo; import com.kingdee.eas.base.message.IAssignRead; import com.kingdee.eas.base.message.URLInfo; import com.kingdee.eas.base.message.util.ProcessCenterUtil; import com.kingdee.eas.base.permission.IUser; import com.kingdee.eas.base.permission.UserFactory; import com.kingdee.eas.base.permission.UserInfo; import com.kingdee.eas.common.EASBizException; import com.kingdee.eas.custom.weaverTodo.config.WeaverTodoConfig; import com.kingdee.eas.custom.weaverTodo.enums.WeaverCallOperTypeEnum; import com.kingdee.eas.custom.weaverTodo.enums.WeaverIsRemarkEnum; import com.kingdee.eas.custom.weaverTodo.enums.WeaverRequestStatusEnum; import com.kingdee.eas.custom.weaverTodo.enums.WeaverViewTypeEnum; import com.kingdee.eas.util.app.DbUtil; import com.kingdee.jdbc.rowset.IRowSet; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Date; /** * 统一待办字段拼装、相对链接、人员工号、本地日志表。 */ public class WeaverTodoSupport { private static final Logger logger = Logger.getLogger(WeaverTodoSupport.class); private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private final WeaverTodoConfig config; public WeaverTodoSupport(WeaverTodoConfig config) { this.config = config; } public void delayForWf() { try { Thread.sleep(config.getWfDelayMs()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public String sanitize(String text) { if (text == null) { return ""; } String s = text.replace("'", "") .replace(" ", " ") .replace("<", "") .replace(">", "") .replace("&", "") .replace("\"", ""); if (s.length() > 2000) { s = s.substring(0, 2000); } return s.trim(); } public String nowDatetime() { return SDF.format(new Date()); } public String workflowCodeOf(String workflowname) { String name = sanitize(workflowname); if (StringUtils.isBlank(name)) { name = "SHR流程"; } return String.valueOf(name.hashCode()); } public String getAssignIdByMsgId(Context ctx, String msgId) throws BOSException, EASBizException { if (StringUtils.isBlank(msgId)) { return ""; } IAssignRead assignReadIns = AssignReadFactory.getLocalInstance(ctx); AssignReadInfo assignReadInfo = assignReadIns.getAssignReadInfo(new ObjectUuidPK(msgId)); if (assignReadInfo == null || assignReadInfo.getAssignID() == null) { return ""; } return assignReadInfo.getAssignID().toString(); } public String resolveBillId(Context ctx, String billId, String assignId) throws Exception { if (StringUtils.isNotBlank(billId)) { return billId; } if (StringUtils.isBlank(assignId)) { return ""; } URLInfo info = ProcessCenterUtil.getUrlInfos(assignId, ctx); return info == null ? "" : StringUtils.defaultString(info.getBillID()); } public String resolveWorkflowName(Context ctx, String msgId, String billId, String assignId) { String flowTypeName = "SHR流程"; try { String resolvedBill = resolveBillId(ctx, billId, assignId); if (StringUtils.isBlank(resolvedBill) && StringUtils.isNotBlank(msgId)) { assignId = getAssignIdByMsgId(ctx, msgId); resolvedBill = resolveBillId(ctx, billId, assignId); } if (StringUtils.isBlank(resolvedBill)) { return flowTypeName; } IMetaDataLoader loader = MetaDataLoaderFactory.getRemoteMetaDataLoader(); EntityObjectInfo vo = loader.getEntity(BOSUuid.read(resolvedBill).getType()); if (vo != null && StringUtils.isNotBlank(vo.getDisplayName())) { flowTypeName = vo.getDisplayName(); } } catch (Exception e) { logger.warn("解析流程类型名失败,使用默认", e); } return sanitize(flowTypeName); } /** * 报业返回人员工号 pp.FNumber(与 SSO PERSON_NUMBER 一致) */ public String resolveReceiverWorkcode(Context ctx, String userIdOrNumber) { if (StringUtils.isBlank(userIdOrNumber)) { return ""; } String key = userIdOrNumber.trim(); try { String sql = "select uu.FNumber userNumber from T_PM_User uu " + "where uu.FForbidden = 0 and (uu.FNumber = ? or uu.FID = ?) " + "order by uu.FIsDelete"; IRowSet rs = DbUtil.executeQuery(ctx, sql, new Object[]{key, key}); if (rs.next()) { String userNumber = rs.getString("userNumber"); if (StringUtils.isNotBlank(userNumber)) { return userNumber.trim(); } } } catch (Exception e) { logger.warn("SQL 解析工号失败 key=" + key + ": " + e.getMessage()); } return ""; } /** * 按用户 id 取 UserInfo(BMC 发送人等)。 */ public UserInfo getUserById(Context ctx, String userId) { if (StringUtils.isBlank(userId)) { return null; } try { IUser iUser = UserFactory.getLocalInstance(ctx); return iUser.getUserInfo(new ObjectUuidPK(userId.trim())); } catch (Exception e) { logger.warn("按 id 取用户失败 userId=" + userId + ": " + e.getMessage()); return null; } } /** * 泛微要求相对路径。落地走 SSO:entryPath?appId&redirect=审批页绝对地址(编码)。 * entryPath 来自 weaver.sso.entryPath,默认 /qy/auth/weaver/app.do。 */ public String buildRelativePcUrl(Context ctx, String billId, String assignId) throws Exception { String resolvedBill = resolveBillId(ctx, billId, assignId); String approveAbs; if (StringUtils.isNotBlank(assignId)) { approveAbs = config.getShrServerName() + config.getApprovePcPath() + "?AssignmentId=" + URLEncoder.encode(assignId, StandardCharsets.UTF_8.name()) + "&billID=" + URLEncoder.encode(StringUtils.defaultString(resolvedBill), StandardCharsets.UTF_8.name()); } else { approveAbs = config.getShrServerName() + "/shr/dynamic.do?uipk=shr.workflow.view&inFrame=true"; } String entryPath = StringUtils.defaultIfBlank(config.getSsoEntryPath(), "/qy/auth/weaver/app.do"); String relative = entryPath + "?appId=" + URLEncoder.encode(config.getSsoAppId(), StandardCharsets.UTF_8.name()) + "&redirect=" + URLEncoder.encode(approveAbs, StandardCharsets.UTF_8.name()); if (relative.startsWith("http://") || relative.startsWith("https://")) { throw new IllegalStateException("pcurl 不允许绝对地址"); } return relative; } public String buildRelativeAppUrl(Context ctx, String billId, String assignId) throws Exception { // 移动端暂与 PC 同路径,由泛微外链前缀区分端;后续可接 MBOS return buildRelativePcUrl(ctx, billId, assignId); } public JSONObject buildPayload(String flowid, String requestname, String workflowname, String nodename, String receiver, String creator, String pcurl, String appurl, WeaverIsRemarkEnum isRemark, WeaverViewTypeEnum viewType, WeaverRequestStatusEnum requestStatus) { JSONObject body = new JSONObject(); body.put("syscode", config.getSyscode()); body.put("flowid", StringUtils.defaultString(flowid)); body.put("requestname", sanitize(requestname)); String typeName = sanitize(workflowname); if (StringUtils.isBlank(typeName)) { typeName = "SHR流程"; } body.put("workflowname", typeName); body.put("workflowcode", workflowCodeOf(typeName)); body.put("nodename", sanitize(StringUtils.defaultIfBlank(nodename, "审批节点"))); body.put("createdatetime", nowDatetime()); body.put("receiver", sanitize(receiver)); body.put("receivedatetime", nowDatetime()); body.put("isremark", isRemark.getCode()); body.put("viewtype", viewType.getCode()); body.put("receivets", String.valueOf(System.currentTimeMillis())); if (StringUtils.isNotBlank(creator)) { body.put("creator", sanitize(creator)); } if (StringUtils.isNotBlank(pcurl)) { body.put("pcurl", pcurl); } if (StringUtils.isNotBlank(appurl)) { body.put("appurl", appurl); } if (requestStatus != null) { body.put("requestStatus", requestStatus.getCode()); } return body; } public void saveAssignRecord(Context ctx, String msgId, String assignId, String billId, String workflowname, String procInstId, String receiver) { // 优先带 receiver 列;老表没有该列则回退 try { String sql = "insert into assignReadRecord(MsgId,assignID,billId,workflowname,procInstId,receiver) values(?,?,?,?,?,?)"; Object[] params = new Object[]{ nvl(msgId), nvl(assignId), nvl(billId), nvl(workflowname), nvl(procInstId), nvl(receiver) }; DbUtil.execute(ctx, sql, params); return; } catch (Exception e) { logger.warn("带 receiver 写入 assignReadRecord 失败,尝试无 receiver 列: " + e.getMessage()); } try { String sql = "insert into assignReadRecord(MsgId,assignID,billId,workflowname,procInstId) values(?,?,?,?,?)"; Object[] params = new Object[]{ nvl(msgId), nvl(assignId), nvl(billId), nvl(workflowname), nvl(procInstId) }; DbUtil.execute(ctx, sql, params); } catch (Exception e) { logger.warn("写入 assignReadRecord 失败(表可能已存在同键或未建表): " + e.getMessage()); } } /** 统一待办接口 URL(不含 access_token,便于落库)。 */ public String buildReceiveTodoUrl() { return StringUtils.defaultString(config.getOpenapiHost()) + StringUtils.defaultString(config.getReceivePath()); } /** 消息中心 sendMsg URL(不含 access_token)。 */ public String buildSendMsgUrl() { return StringUtils.defaultString(config.getOpenapiHost()) + StringUtils.defaultString(config.getSendMsgPath()); } public void addCallLog(Context ctx, String msgId, String assignId, String billId, WeaverCallOperTypeEnum operType, String requestUrl, String requestParams, String responseResult, int statusCode, String message) { try { logger.info("[WEAVER-TODO] CallLog write OperType=" + (operType == null ? "" : operType.getLabel()) + " msgId=" + msgId + " assignId=" + assignId + " billId=" + billId + " requestUrl=" + requestUrl + " statusCode=" + statusCode + " message=" + message); String sql = "insert into OAMsgCallLog(MsgId,assignID,billId,OperType,RequestUrl,RequestParams,ResponseResult,StatusCode,Message,CreateTime) " + "values(?,?,?,?,?,?,?,?,?,?)"; String operLabel = operType == null ? "" : operType.getLabel(); Object[] params = new Object[]{ msgId, assignId, billId, operLabel, StringUtils.defaultString(requestUrl), requestParams, responseResult, statusCode, message, new Timestamp(System.currentTimeMillis()) }; DbUtil.execute(ctx, sql, params); } catch (Exception e) { logger.warn("写入 OAMsgCallLog 失败: " + e.getMessage()); } } /** * 截断字符串 * @param s * @param maxLen * @return */ private static String cutStr(String s, int maxLen) { if (s == null) { return ""; } if (s.length() <= maxLen) { return s; } return s.substring(0, maxLen); } public String getMsgIdByAssignId(Context ctx, String assignId) { try { IRowSet rs = DbUtil.executeQuery(ctx, "select MsgId from assignReadRecord where assignID = ?", new Object[]{assignId}); if (rs.next()) { return rs.getString("MsgId"); } } catch (Exception e) { logger.warn("按 assignId 查 msgId 失败", e); } return ""; } public String getProcInstIdByAssignId(Context ctx, String assignId) { try { IRowSet rs = DbUtil.executeQuery(ctx, "select procInstId from assignReadRecord where assignID = ?", new Object[]{assignId}); if (rs.next()) { return rs.getString("procInstId"); } } catch (Exception e) { logger.warn("按 assignId 查 procInstId 失败", e); } return ""; } public String getBillIdByAssignId(Context ctx, String assignId) { try { IRowSet rs = DbUtil.executeQuery(ctx, "select billId from assignReadRecord where assignID = ?", new Object[]{assignId}); if (rs.next()) { return StringUtils.defaultString(rs.getString("billId")); } } catch (Exception e) { logger.warn("get billId by assignId failed", e); } return ""; } /** * 删除/撤销必须用当初推待办的 workflowname,否则泛微 upsert 键对不上。 */ public String getWorkflowNameByAssignId(Context ctx, String assignId) { try { IRowSet rs = DbUtil.executeQuery(ctx, "select workflowname from assignReadRecord where assignID = ?", new Object[]{assignId}); if (rs.next()) { String name = rs.getString("workflowname"); if (StringUtils.isNotBlank(name)) { return sanitize(name); } } } catch (Exception e) { logger.warn("get workflowname by assignId failed: " + e.getMessage()); } try { IRowSet rs = DbUtil.executeQuery(ctx, "select RequestParams from OAMsgCallLog where assignID = ? and OperType = ? order by CreateTime desc", new Object[]{assignId, WeaverCallOperTypeEnum.TODO_ADD.getLabel()}); if (rs.next()) { String raw = rs.getString("RequestParams"); if (StringUtils.isNotBlank(raw)) { JSONObject json = JSONObject.parseObject(raw); String name = json.getString("workflowname"); if (StringUtils.isNotBlank(name)) { return sanitize(name); } } } } catch (Exception e) { logger.warn("parse workflowname from CallLog failed", e); } return ""; } public String getReceiverByAssignId(Context ctx, String assignId) { try { IRowSet rs = DbUtil.executeQuery(ctx, "select receiver from assignReadRecord where assignID = ?", new Object[]{assignId}); if (rs.next()) { String r = rs.getString("receiver"); if (StringUtils.isNotBlank(r)) { return r; } } } catch (Exception e) { logger.warn("按 assignId 查 receiver 列失败,回退 CallLog: " + e.getMessage()); } try { IRowSet rs = DbUtil.executeQuery(ctx, "select RequestParams from OAMsgCallLog where assignID = ? and OperType = ? order by CreateTime desc", new Object[]{assignId, WeaverCallOperTypeEnum.TODO_ADD.getLabel()}); if (rs.next()) { String raw = rs.getString("RequestParams"); if (StringUtils.isNotBlank(raw)) { JSONObject json = JSONObject.parseObject(raw); String r = json.getString("receiver"); if (StringUtils.isNotBlank(r)) { return r; } } } } catch (Exception e) { logger.warn("从 OAMsgCallLog 解析 receiver 失败", e); } return ""; } private static String nvl(String s) { return s == null ? "" : s; } }