飞书集成苍穹工作流原创
金蝶云社区-技术支持与赋能部_ZH
技术支持与赋能部_ZH
20人赞赏了该文章 4,912次浏览 未经作者许可,禁止转载编辑于2024年08月30日 13:43:26
summary-icon摘要由AI智能服务提供

本文档描述了将飞书(FeiShu)集成到现有工作流系统中的过程,包括需求、思路与方案以及实现过程。当前系统已支持多种消息推送渠道,但对飞书的支持需通过二次开发实现。具体步骤包括调研飞书接口、同步飞书组织人员信息、开发飞书消息渠道和解析类,以及实现飞书通过单点登录到主系统的功能。在预研过程中,详细调研了飞书提供的消息和待办接口,并考虑了接口的兼容性和替代方案。通过飞书提供的组织人员接口实现人员同步,并建立了系统间的映射关系。在系统管理中新增飞书消息渠道,并开发相应的解析类来处理消息推送和待办任务。此外,还涉及了飞书通过审批单点登录到主系统的插件开发。整个集成过程复杂但详细,确保了飞书与现有工作流系统的无缝对接。

关键词:飞书,工作流,集成

一、需求

目前标准产品5.0已经内置了企业微信,钉钉,云之家,welink等多个渠道得消息推送需求。若用户使用其他第三方移动办公软件如飞书,就需要二开实现。

二、思路与方案

2.1分析思路

难点是需要调研飞书得接口,不确定是否能满足苍穹审批流得需求


2.2实现方案

(1)飞书接口调研;(2)做人员同步,建立两个系统之间得关联。确定人员同步机制;(3)二开飞书消息渠道

重写渠道解析类,向飞书推送消息;(4)飞书要通过审批单点登录到苍穹,需要开发单点登录插件


三、实现过程

1、预研飞书提供的对外消息&待办接口,重点可以查看一下几个HttpAPI:

(1)普通消息推送接口,推送文本消息和链接消息;(2)待办创建接口,创建飞书待办任务;(3)待办状态更新接口,待办变已办任务;(4)待办删除接口,删除待办;

用户可以根据自己需求,结合飞书提供的接口。

比如经过调研,飞书没有删除待办得接口,那么我们需要利用待办状态更新的接口,进行处理


2、飞书组织人员同步

同步主要逻辑是,通过飞书提供的组织人员接口,获取到人员信息,并以两个系统中人员的焦点(比如手机号)作为比较条件,建立两个系统的人员映射关系,并存储至苍穹系统中。同步功能需要兼容多次同步新增人员的需求。可以通过集成云实现

https://developer.kingdee.com/article/159353315313129216?productLineId=29&isKnowledge=2


3、二开消息渠道

在系统管理,消息渠道列表新增飞书渠道,实现类需要二开,需要继承AbstractMessageServiceHandler类

image.png

4、二开渠道解析类

下面只展示渠道解析类的方法,封装的service类调用飞书接口等步骤放在下面附件源码中,请自行查看

package sn.common.message;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.dataentity.resource.ResManager;
import kd.bos.dataentity.utils.StringUtils;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.QueryServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.bos.servicehelper.workflow.WorkflowServiceHelper;
import kd.bos.workflow.engine.WfConfigurationUtil;
import kd.bos.workflow.engine.msg.AbstractMessageServiceHandler;
import kd.bos.workflow.engine.msg.ctx.MessageContext;
import kd.bos.workflow.engine.msg.handler.YunzhijiaServiceHandler;
import kd.bos.workflow.engine.msg.info.MessageInfo;
import kd.bos.workflow.engine.msg.info.ToDoInfo;
import kd.bos.workflow.engine.msg.model.yzj.YzjToDoState;
import sn.common.FeiShuMService.FeiShuMService;
import sn.common.Impl.FeiShuServiceImpl;
import sn.common.helper.FeiShuHelper;
import java.util.*;
/**
 * @description(类描述):飞书消息渠道二开类
 */
public class FeiShuHandler extends AbstractMessageServiceHandler {
    private static Log logger = LogFactory.getLog(YunzhijiaServiceHandler.class);

    @Override
    //    1.   流程节点任务,创建待办;
    //    2.   todoInfo&messageContext中包含了绝大部分场景中待办所需参数数据;
    //    3.   在该方法中调用泛微接口,推送待办任务;
     //   4.   标准产品中没有记录待办日志,二开根据需求决定是否将待办日志落库处理,数据库表以及落库方式没有要求;
     //   5.   在方法的逻辑执行过程中,若是出现异常,请向上抛出,系统会根据异常,自动重试再次推送待办;
     //   对于待办链接的改造可以在该方法中进行
    public void createToDo(MessageContext ctx, ToDoInfo toDoInfo) {
        if (WfConfigurationUtil.isEnabled("feishu")) {
            Map<String, Object> properties = WorkflowServiceHelper.getProcessInstancePropertiesByBusinesskey(ctx.getBusinessKey());
            // 创建人的手机号
            List<String> createdUserPhones = FeiShuHelper.getUserPhoneById(Arrays.asList(Long.valueOf(properties.get("startUserId").toString())));
            // 审批人的手机号
            List<String> taskUserPhones = FeiShuHelper.getUserPhoneById(toDoInfo.getUserIds());

            FeiShuMService feiShuService = FeiShuServiceImpl.getInstance();
            String token = feiShuService.getToken();
            // 创建人的openid
            List<Map<String, Object>> createdUsers= feiShuService.getFSUserByPhone(token,"",createdUserPhones);
            // 审批人的openid
            List<Map<String, Object>> taskOpenUsers= feiShuService.getFSUserByPhone(token,"",taskUserPhones);
            // 审批人的userid
            List<Map<String, Object>> taskUserIds= feiShuService.getFSUserByPhone(token,"user_id",taskUserPhones);

            // 创建三方审批定义
            String approvalCode = feiShuService.externalApprovals(token);


            Map<String, Object> instanceSentMap = null;
            for (Map<String, Object> createdUserMap : createdUsers) {
                // 构造【创建三方实例】已发送json
                instanceSentMap = FeiShuHelper.getInstanceSentMap(approvalCode, ctx, toDoInfo, createdUserMap, "PENDING");
                List<Map<String, Object>> taskList = new ArrayList<>();
                for (Map<String, Object> taskUserMap : taskOpenUsers) {
                    // 构造【创建三方实例】待办json
                    taskList.add(FeiShuHelper.getInstanceTaskMap(ctx, toDoInfo, "PENDING", taskUserMap));
                }
                instanceSentMap.put("task_list",taskList);
            }
            // 调用飞书同步三方实例创建待办
            feiShuService.externalInstances(token,instanceSentMap);

            FeiShuHelper.sendBotMessage(ctx,toDoInfo,feiShuService,token,"1008",createdUsers,taskOpenUsers,taskUserIds);
        }

    }

    @Override
        //1. 流程处理待办任务;
        //2. 在该方法中调用泛微接口,推送待办处理请求;
    public void dealToDo(MessageContext ctx, ToDoInfo toDoInfo) {
        if (WfConfigurationUtil.isEnabled("feishu")) {
            QFilter qf = new QFilter("taskid", QCP.equals, toDoInfo.getTaskId());
            DynamicObject wfOperation = BusinessDataServiceHelper.loadSingle("wf_operationlog", "id,decisiontype", qf.toArray());
            String status = wfOperation.getString("decisiontype");
            if (StringUtils.equals("approve", status)) {
                status = "APPROVED";
            }else{
                status = "REJECTED";
            }
            if (StringUtils.isNotBlank(status)) {

                Map<String, Object> properties = WorkflowServiceHelper.getProcessInstancePropertiesByBusinesskey(ctx.getBusinessKey());
                // 创建人的手机号
                List<String> createdUserPhones = FeiShuHelper.getUserPhoneById(Arrays.asList(Long.valueOf(properties.get("startUserId").toString())));
                // 审批人的手机号
                List<String> taskUserPhones = FeiShuHelper.getUserPhoneById(toDoInfo.getUserIds());

                FeiShuMService feiShuService = FeiShuServiceImpl.getInstance();
                String token = feiShuService.getToken();
                // 创建人的openid
                List<Map<String, Object>> createdUsers= feiShuService.getFSUserByPhone(token,"",createdUserPhones);
                // 审批人的openid
                List<Map<String, Object>> taskUsers= feiShuService.getFSUserByPhone(token,"",taskUserPhones);
                // 创建三方审批定义
                String approvalCode = feiShuService.externalApprovals(token);

                Map<String, Object> instanceSentMap = new HashMap<>();
                for (Map<String, Object> createdUser : createdUsers) {
                    // 构造【创建三方实例】已发送json
                    instanceSentMap = FeiShuHelper.getInstanceSentMap(approvalCode, ctx, toDoInfo, createdUser, status);
                    List<Map<String, Object>> taskList = new ArrayList<>();
                    for (Map<String, Object> taskUser : taskUsers) {
                        // 构造【创建三方实例】待办json
                        taskList.add(FeiShuHelper.getInstanceTaskMap(ctx, toDoInfo, status, taskUser));
                    }
                    instanceSentMap.put("task_list", taskList);
                }

                // 调用飞书同步三方实例创建待办
                feiShuService.externalInstances(token,instanceSentMap);

                String messageId = FeiShuHelper.getMessageId(ctx.getTaskId().toString());
                if(StringUtils.isNotBlank(messageId)){
                    feiShuService.updateBotMessage(token,messageId);

                    DynamicObject taskBotRelate = FeiShuHelper.saveBotMessageId(String.valueOf(toDoInfo.getTaskId()), messageId, "B");
                    SaveServiceHelper.save(new DynamicObject[]{taskBotRelate});
                }

            }
        }
    }

    @Override
        // 1.   删除待办任务方法;
        //2.   转交任务、任务的处理都会调用该方法,删除掉泛微方的待办;
        //3.   工作流中撤回,或多人收到任务,其中一人审批,删除其他人的待办,会调用该方法
在该方法中调用泛微接口,发起删除泛微待办请求;
    public void deleteToDo(MessageContext ctx, ToDoInfo toDoInfo) {
        if (WfConfigurationUtil.isEnabled("feishu")) {

            Map<String, Object> properties = WorkflowServiceHelper.getProcessInstancePropertiesByBusinesskey(ctx.getBusinessKey());
            // 创建人的手机号
            List<String> createdUserPhones = FeiShuHelper.getUserPhoneById(Arrays.asList(Long.valueOf(properties.get("startUserId").toString())));
            // 审批人的手机号
            List<String> taskUserPhones = FeiShuHelper.getUserPhoneById(toDoInfo.getUserIds());

            FeiShuMService feiShuService = FeiShuServiceImpl.getInstance();
            String token = feiShuService.getToken();
            // 创建人的openid
            List<Map<String, Object>> createdUsers= feiShuService.getFSUserByPhone(token,"",createdUserPhones);
            // 审批人的openid
            List<Map<String, Object>> taskUsers= feiShuService.getFSUserByPhone(token,"",taskUserPhones);
            // 创建三方审批定义
            String approvalCode = feiShuService.externalApprovals(token);

            Map<String, Object> instanceSentMap = null;
            for (Map<String, Object> createdUserMap : createdUsers) {
                // 构造【创建三方实例】已发送json
                instanceSentMap = FeiShuHelper.getInstanceSentMap(approvalCode, ctx, toDoInfo, createdUserMap, "DELETED");
                List<Map<String, Object>> taskList = null;
                for (Map<String, Object> taskUserMap : taskUsers) {
                    // 构造【创建三方实例】待办json
                    taskList.add(FeiShuHelper.getInstanceTaskMap(ctx, toDoInfo, "REJECTED", taskUserMap));
                }
                instanceSentMap.put("task_list",taskList);
            }

            // 调用飞书同步三方实例创建待办
            feiShuService.externalInstances(token,instanceSentMap);

            String messageId = FeiShuHelper.getMessageId(ctx.getTaskId().toString());
            if(StringUtils.isNotBlank(messageId)){
                feiShuService.updateBotMessage(token,messageId);

                DynamicObject taskBotRelate = FeiShuHelper.saveBotMessageId(String.valueOf(toDoInfo.getTaskId()), messageId, "B");
                SaveServiceHelper.save(new DynamicObject[]{taskBotRelate});
            }

        }
    }
}

5、测试

触发待办需要同时启用消息渠道,和消息类型

image.png

飞书可以收到苍穹审批信息记录

image.png

6、飞书审批,单点登录到苍穹

已有标准方案支持,请参考

https://vip.kingdee.com/knowledge/specialDetail/228892721203874816?category=228893083373702400&id=238758427345172736&productLineId=29&lang=zh-CN



四、效果图

image.png

五、开发环境版本

V5.0.002

六、注意事项

1、开启第三方认证后,可能无法通过账密登录,需要用管理员登录,进入基础服务模块——系统参数配置——登录参数配置,将使用sso时启用账密登录开关打开,此开关只有开启第三方认证后才会显示

image.png

2、由于苍穹与第三方系统之间,通过网络关联,由于网略不稳定性等因素,可能导致消息推送失败,此时,可以在sendMessage方法中抛出异常,该异常被上游功能捕获,失败消息记录到消息日志列表中,等待重试机制重试;

3、待办创建、处理、删除若是遇到调用接口失败的情况,请抛出异常给上游逻辑,触发重试机制;

4、第三方系统中待办请重视待办ID的唯一性问题,苍穹中多人处理的任务,taskId是一样的,针对有的第三方系统,可以拼接人员Id做唯一性处理;

5、需要掌握苍穹触发待办和消息得时机,知道什么时候是异常,什么时候是没有触发。便于调试

七、参考资料

开发平台

学习成长中心

消息中心应用开发手册

单点登录原理

八、源代码


源码.rar(12.93KB)

图标赞 20
20人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!

您的鼓励与嘉奖将成为创作者们前进的动力,如果觉得本文还不错,可以给予作者创作打赏哦!

请选择打赏金币数 *

10金币20金币30金币40金币50金币60金币
可用金币: 0