泛微OAe9-后端二开异步调用

文章目录

泛微OAe9-后端二开异步调用

在泛微OA二开的action接口中,大多数情况下都是同步执行一些回调的逻辑,比如单纯的调用SAP,生成一些SAP采购申请编号,生成完成,并且保存在流程里后,流程才往下批准。这种情况流程的审批效率,高度依赖调用SAP的耗时,假如SAP系统异常,OA流程就永远批不下去,影响流程审批效率。如果回调其他的一些接口不需要等响应结果,流程可以先放行,后台异步执行其他的回调,回调失败可以记录在表里,可以定时扫描表对失败的请求发起重试。

下面实战几个真实的案例:

案例一:同步调用SAP,生成SAP采购申请编号,再保存在流程里,保存成功后,再同步给OA人员发送消息,最后放行流程。

假设:
1、调用SAP,生成SAP采购申请编号,再保存在流程里,耗时5秒;
2、OA人员发送消息,耗时3秒。
那么案例一从批准流程,到流程放行,实际耗时:5+3=8秒。

这种场景不需要代码举例,就是最常见的同步代码,依次把代码逻辑写在接口里即可。

案例二:同步调用SAP,生成SAP采购申请编号,再保存在流程里,保存成功后直接放行流程,再后台异步给OA人员发送消息。

假设:
1、调用SAP,生成SAP采购申请编号,再保存在流程里,耗时5秒;
2、OA人员发送消息,耗时3秒。
那么案例二从批准流程,到流程放行,实际耗时:5秒。因为OA人员发送消息是异步的,是异步执行。

1 发送消息类:AsyncMessageAction.java
java 复制代码
public class AsyncMessageAction extends BaseCronJob {
    private static final Logger logger = LoggerFactory.getLogger(AsyncMessageAction.class);
    private static final int MESSAGE_TYPE = 132;

    @Override
    public void execute() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String todayStr = LocalDateTime.now().format(formatter);
        // 发送给我本人
        sendMessage(10795, todayStr, null, null);
    }

    /**
     * 模拟发送消息提醒
     * @param userid  通知人
     * @param todayStr  日期
     */
    private List<Object> sendMessage(int userid, String todayStr, String name, String url) {
        // 标题
        StringBuilder title = new StringBuilder();
        title.append("异步消息推送");
        Set<String> userSet = new HashSet();
        userSet.add(String.valueOf(userid));
        // 内容
        StringBuilder context = new StringBuilder();
        context.append("测试异步消息发送,时间:" + todayStr);

        MessageBean messageBean = null;
        List<Object> values = new ArrayList<>();
        try {
            messageBean = Util_Message.createMessage(MessageType.newInstance(MESSAGE_TYPE), userSet, title.toString(), context.toString(), url, url);
            // 创建人id
            messageBean.setCreater(1);
            String messageId = messageBean.getMessageId();
            boolean isTure = Util_Message.store(messageBean); // 发送
            if (isTure) {
                values.add(userid);
                values.add(context.toString());
                values.add(DateUtil.getCurrentDate() + " " + DateUtil.getCurrentHourMin());
            }
            return values;
        } catch (IOException e) {
            logger.error("AssetInventoryTimeoutReminder:通知盘点人发送消息提醒出错:" + e.toString());
            e.printStackTrace();
            return values;
        }
    }

}
2 发送消息类:AsyncMessageAction.java
java 复制代码
/**
 * @author: 
 * @date: 2025/5/13
 *
 * <p>1、研发组件/整机试制申请,业务类型为PCBA板卡时触发此接口 </p>
 * <p>2、研发PCBA打样申请</p>
 */
public class ProcessOutsourcingOrderCreateTest implements Action {
    private static final Log logger = LogFactory.getLog(ProcessOutsourcingOrderCreateTest.class);
    private static final String SAP_FUNCTION_NAME = "ZOA_PRODUCTIONORDER_CREATE";
    private static final String INPUT_TABLE = "T_ITEM";
    private static final String RETURN_TABLE = "T_RETURN";
    private static final String YF_PCBD = "ZP07";
    private static final String GZ_PCBD = "ZP06";
    private String formId;

    // 共享线程池
    private static final ExecutorService MESSAGE_EXECUTOR =
            new ThreadPoolExecutor(
                    4,
                    8,
                    60L,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(500),
                    new ThreadPoolExecutor.AbortPolicy()
            );

    // 异步调用发送消息
    private void sendMessageAsync() {
        try {
            MESSAGE_EXECUTOR.execute(() -> {
                try {
                    new AsyncMessageAction().execute();
                    logger.info("异步消息发送任务执行完成");
                } catch (Exception e) {
                    logger.error("异步消息发送失败", e);
                }
            });
        } catch (RejectedExecutionException e) {
            logger.error("异步消息任务提交失败,线程池队列已满", e);
        }
    }

    @Override
    public String execute(RequestInfo requestInfo) {
        String loggerHead = ">>>" + WorkflowFunction.printWorkflowTraceInfo(requestInfo);
        logger.info(loggerHead + " is starting...");
        RequestManager requestManager = requestInfo.getRequestManager();
        Map<String, String> mainTable = WorkflowFunction.getMainTable(requestInfo);
        int detailIndex = -1;
        if (Integer.parseInt(formId) == -1778) { // <研发组件/整机试制申请>
            detailIndex = 1;
        }
        if (Integer.parseInt(formId) == -1949) { // 研发PCBA打样申请
            detailIndex = 0;
        }
        try {
            if (detailIndex == -1) {
                throw new Exception("formid异常,请截图联系管理员.");
            }
            List<Map<String, String>> detailTable2 = WorkflowFunction.getDetailTable(requestInfo, detailIndex); // PCBA明细表
            if (detailIndex == 1) {
                String wlfl = mainTable.get("wlfl"); // 业务类型
                if (!wlfl.equals("2")) {
                    return Action.SUCCESS; // 非 PCBA板卡,接口退出
                }
            }
            String sfsyzfczzj = mainTable.get("sfsyzfczzj"); // 是否使用政府财政资金
            String zfxmbm = mainTable.get("zfxmbm"); // 政府项目编码
            String fkyxzh = mainTable.get("fkyxzh"); // 付款银行账号
            String zfxmzy = mainTable.get("zfxmzy"); // 政府项目摘要
            String fkbz = mainTable.get("fkbz"); // 付款备注
            String liush = mainTable.get("liush"); // 流水号
            String sfwgzpcba = mainTable.get("sfwgzpcba"); // 是否为工装PCBA

            List<Map<String, Object>> sapItItem = new ArrayList<>();
            Map<String, Object> rowData = null;
            int index = 0; // 行号
            for (Map<String, String> dt : detailTable2) { // 遍历构建SAP参数
                rowData = new HashMap<>();
                rowData.put("AUART", "0".equals(sfwgzpcba) ? GZ_PCBD : YF_PCBD); // 订单类型
                rowData.put("ZZFXMBM", "0".equals(sfsyzfczzj) ? zfxmbm : ""); // 政府项目编码
                rowData.put("ZFKYHZH", "0".equals(sfsyzfczzj) ? fkyxzh : ""); // 付款银行账号
                rowData.put("ZZFXMZY", "0".equals(sfsyzfczzj) ? zfxmzy : ""); // 政府项目摘要
                rowData.put("ZNOTES", "0".equals(sfsyzfczzj) ? fkbz : ""); // 付款备注
                rowData.put("ZZLCBH", liush); // 流水号
                rowData.put("CANUM", index); // 行号
                rowData.put("MATNR", dt.get("yjwlbmllan")); // 样机物料编码(浏览按钮)
                rowData.put("MENGE", dt.get("ylzs")); // 用量(整数)
                rowData.put("ZYFXM", dt.get("yfnbddh")); // 研发内部订单号
                rowData.put("ZBURN", dt.get("sfsl")); // 是否烧录
                rowData.put("ZAGING", dt.get("sflh")); // 是否老化
                rowData.put("ZTEST", dt.get("sfcs")); // 是否测试
                rowData.put("ZBURNCHIP", dt.get("sfyslxp")); // 是否有烧录芯片
                sapItItem.add(rowData);
                index ++;
            }

            // 调用SAP
            SapRemoteFunctionCaller srfc = new SapRemoteFunctionCaller(SAP_FUNCTION_NAME);
            logger.info(srfc.setInputTable(INPUT_TABLE, sapItItem));
            logger.info(srfc.setOutputTableNames(new String[]{RETURN_TABLE}));
            logger.info(srfc.call());
            Map<String, Object> result = srfc.getResult();
            logger.info(result.toString());

            List<Map<String, Object>> resultTable = (List<Map<String, Object>>) result.get(RETURN_TABLE);
            Collections.sort(resultTable, Comparator.comparingInt(row -> Integer.parseInt(String.valueOf(row.get("CANUM"))))); // sap返回行数据按行号排序
            List<Object> errorList = new ArrayList<>();
            for (int i=0; i<resultTable.size(); i++) {
                // SAP每一行都单独创建sap资产编号
                if (!"S".equals(resultTable.get(i).get("E_CODE").toString())) {
                    errorList.add("第"+ (i+1) + "行错误:" +resultTable.get(i).get("E_MSG"));
                    continue;
                }
                // 返回值=S,则成功
                detailTable2.get(i).put("sapcgsqdh", (String) resultTable.get(i).get("BANFN"));
            }
            if (errorList == null || errorList.isEmpty()) { // sap所有行采购申请单都成功
                WorkflowFunction.saveDetailTable(requestInfo, detailTable2, new String[]{"sapcgsqdh"}, detailIndex);
                // SAP成功且明细保存完成后,异步发送OA站内消息
                sendMessageAsync();
                return Action.SUCCESS;
            }
            throw new RuntimeException(errorList.toString());
        } catch (Exception e) {
            logger.info(loggerHead + " ProcessOutsourcingOrderCreate... error:" + e.toString());
            requestManager.setMessagecontent("系统异常,请截图此信息联系管理员:" + e.getMessage());
            return Action.FAILURE_AND_CONTINUE;
        }
    }
}
案例三:异步调用SAP,生成SAP采购申请编号,不保存在流程里;再异步给OA人员发送消息;再异步回调OMS传递流程数据。
1 业务回调分开编写
java 复制代码
/**
 * 异步调用 SAP
 */
private void executeSapAsync(RequestInfo requestInfo) {
    BUSINESS_EXECUTOR.execute(() -> {
        try {
            processSap(requestInfo); // 具体逻辑自行实现
        } catch (Exception e) {
            logger.error("异步调用 SAP 失败", e);
        }
    });
}

/**
 * 异步发送 OA 站内消息
 */
private void sendMessageAsync() {
    BUSINESS_EXECUTOR.execute(() -> {
        try {
            sendMessage(requestInfo);  // 具体逻辑自行实现
        } catch (Exception e) {
            logger.error("异步发送 OA 站内消息失败", e);
        }
    });
}

/**
 * 异步调用 OMS
 */
private void executeOmsAsync(RequestInfo requestInfo) {
    BUSINESS_EXECUTOR.execute(() -> {
        try {
            processOms(requestInfo);  // 具体逻辑自行实现
        } catch (Exception e) {
            logger.error("异步调用 OMS 失败", e);
        }
    });
}
2 acton类
java 复制代码
@Override
public String execute(RequestInfo requestInfo) {

    // 异步调用 SAP
    executeSapAsync(requestInfo);

    // 异步发送 OA 站内消息
    sendMessageAsync(requestInfo);

    // 异步调用 OMS
    executeOmsAsync(requestInfo);

    // 三个任务提交后,Action 立即返回
    return Action.SUCCESS;
}

假设:
1、调用SAP,生成SAP采购申请编号,耗时5秒;
2、OA人员发送消息,耗时3秒;
3、回调OMS传递流程数据,耗时10秒。
那么案例三从批准流程,到流程放行,实际耗时:10秒。取三次异步中,最长的那一次耗时。

相关推荐
秋天的怀念_David2 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放2 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友
用户3721574261352 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java
小嘉丶学长3 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节
字节探索3 小时前
Spring Boot 接口幂等性设计:从唯一索引到 Redis+Lua 的工程实践
java·spring boot
用户3721574261353 小时前
Java 实现 Excel 与 Markdown 相互转换:表格导出、格式设置与多表提取
java
SL_staff3 小时前
制造业订单交付总踩坑?用结构化节点协同模型重构计划系统
java·开源·全栈
永遠_4 小时前
YAML格式化工具
java