本文档描述了将飞书(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类
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、测试
触发待办需要同时启用消息渠道,和消息类型
飞书可以收到苍穹审批信息记录
6、飞书审批,单点登录到苍穹
已有标准方案支持,请参考
四、效果图
五、开发环境版本
V5.0.002
六、注意事项
1、开启第三方认证后,可能无法通过账密登录,需要用管理员登录,进入基础服务模块——系统参数配置——登录参数配置,将使用sso时启用账密登录开关打开,此开关只有开启第三方认证后才会显示
2、由于苍穹与第三方系统之间,通过网络关联,由于网略不稳定性等因素,可能导致消息推送失败,此时,可以在sendMessage方法中抛出异常,该异常被上游功能捕获,失败消息记录到消息日志列表中,等待重试机制重试;
3、待办创建、处理、删除若是遇到调用接口失败的情况,请抛出异常给上游逻辑,触发重试机制;
4、第三方系统中待办请重视待办ID的唯一性问题,苍穹中多人处理的任务,taskId是一样的,针对有的第三方系统,可以拼接人员Id做唯一性处理;
5、需要掌握苍穹触发待办和消息得时机,知道什么时候是异常,什么时候是没有触发。便于调试
七、参考资料
八、源代码
源码.rar(12.93KB)
推荐阅读
您的鼓励与嘉奖将成为创作者们前进的动力,如果觉得本文还不错,可以给予作者创作打赏哦!
请选择打赏金币数 *