processflow流程图多人协作预热

前言

在线上办公如火如荼的今天,多人协作功能是每个应用绕不开的门槛。processflow在线流程图(前身基于drawio二次开发)沉寂两年之久,经过长时间设计开发,调整,最终完成了多人协作的核心模块设计。废话不多说,上操作视频展示下效果:

Video_2023-09-04_150131

多人协作技术原理剖析

秉着都2023年了,谁还重复造轮子的理念,这里现学现用,利用了一些商业成熟的技术和软件来实现自己的目标。主要用到了两点技术:

  • 利用websocket的push&subscribe实现多人之间的行为共享(鼠标点击;移动;图标内容变更)
  • 利用onedrive实现历史快照文件的存储

 

client生产事件主要分为以下几种:

                    switch (data.action){//传递鼠标移动和点击事件case 'message':processMsg(data.msg, data.from);break;case 'clientsList':clientsList(data.msg);break;case 'signal':signal(data.msg);break;case 'newClient':newClient(data.msg);break;case 'clientLeft':clientLeft(data.msg);break;case 'sendSignalFailed':sendSignalFailed(data.msg);break;}

 server端主要作用就是接收client端的各个事件,然后对事件进行处理,然后广播出去;

比如:newClient新客户端加入,server端会将clientId和session记录到缓存中,然后将新的clientId广播出去,所有的客户端接收到有新协作者加入后,会进行相关准备操作。

clientLeft:当有客户端下线,也会将session剔除,然后广播出去,所有客户端会将下线的client的光标移除。

push队列主要对各个客户端的事件进行排序。幂等操作,然后发布。 

 存储端:

主要用onedrive进行存储,历史版本管理,冲突解决。这里插一句,不要问为什么用onedrive,因为drawio集成了onedrive,这里不想重写一套新的文件操作api,直接用现成的了。鉴于国内对onedrive网络支持的不太友好,这也是为什么迟迟不上线的原因之一,后续考虑将接口统一走后端代理,由后端统一访问onedrive。

private Response contactOAuthServer(String authSrvUrl, String code, String refreshToken, String secret,String client, String redirectUri, boolean directResp, int retryCount) {HttpURLConnection con = null;Response response = new Response();try {URL obj = new URL(authSrvUrl);con = (HttpURLConnection) obj.openConnection();con.setRequestMethod("POST");boolean jsonResponse = false;StringBuilder urlParameters = new StringBuilder();if (postType == X_WWW_FORM_URLENCODED) {con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");if (withAcceptJsonHeader) {con.setRequestProperty("Accept", "application/json");}urlParameters.append("client_id=");urlParameters.append(Utils.encodeURIComponent(client, "UTF-8"));urlParameters.append("&client_secret=");urlParameters.append(Utils.encodeURIComponent(secret, "UTF-8"));if (code != null) {if (withRedirectUrl) {urlParameters.append("&redirect_uri=");urlParameters.append(Utils.encodeURIComponent(redirectUri, "UTF-8"));}urlParameters.append("&code=");urlParameters.append(Utils.encodeURIComponent(code, "UTF-8"));urlParameters.append("&grant_type=authorization_code");} else {if (withRedirectUrlInRefresh) {urlParameters.append("&redirect_uri=");urlParameters.append(Utils.encodeURIComponent(redirectUri, "UTF-8"));}urlParameters.append("&refresh_token=");urlParameters.append(Utils.encodeURIComponent(refreshToken, "UTF-8"));urlParameters.append("&grant_type=refresh_token");jsonResponse = true;}} else if (postType == JSON) {con.setRequestProperty("Content-Type", "application/json");JsonObject urlParamsObj = new JsonObject();urlParamsObj.addProperty("client_id", client);urlParamsObj.addProperty("redirect_uri", redirectUri);urlParamsObj.addProperty("client_secret", secret);if (code != null) {urlParamsObj.addProperty("code", code);urlParamsObj.addProperty("grant_type", "authorization_code");} else {urlParamsObj.addProperty("refresh_token", refreshToken);urlParamsObj.addProperty("grant_type", "refresh_token");jsonResponse = true;}urlParameters.append(urlParamsObj.toString());}// Send post requestcon.setDoOutput(true);DataOutputStream wr = new DataOutputStream(con.getOutputStream());wr.writeBytes(urlParameters.toString());wr.flush();wr.close();BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));String inputLine;StringBuffer authRes = new StringBuffer();while ((inputLine = in.readLine()) != null) {authRes.append(inputLine);}in.close();response.status = con.getResponseCode();Gson gson = new Gson();JsonObject json = gson.fromJson(authRes.toString(), JsonElement.class).getAsJsonObject();String accessToken = getAccessToken(json);int expiresIn = getExpiresIn(json);response.refreshToken = getRefreshToken(json);response.accessToken = accessToken;JsonObject respObj = new JsonObject();respObj.addProperty("access_token", accessToken);if (expiresIn > -1) {respObj.addProperty("expires_in", expiresIn);}if (directResp) {response.content = respObj.toString();} else {// Writes JavaScript coderesponse.content = processAuthResponse(respObj.toString(), jsonResponse);}} catch (IOException e) {StringBuilder details = new StringBuilder("");if (con != null) {try {BufferedReader in = new BufferedReader(new InputStreamReader(con.getErrorStream()));String inputLine;while ((inputLine = in.readLine()) != null) {System.err.println(inputLine);details.append(inputLine);details.append("\n");}in.close();} catch (Exception e2) {// Ignore}}if (e.getMessage() != null && e.getMessage().contains("401")) {response.status = HttpServletResponse.SC_UNAUTHORIZED;} else if (retryCount > 0 && e.getMessage() != null && e.getMessage().contains("Connection timed out")) {return contactOAuthServer(authSrvUrl, code, refreshToken, secret,client, redirectUri, directResp, --retryCount);} else {response.status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;e.printStackTrace();log.error("AUTH-SERVLET: [" + authSrvUrl + "] ERROR: " + e.getMessage() + " -> " + details.toString());}if (DEBUG) {StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);e.printStackTrace(pw);pw.println(details.toString());pw.flush();response.content = sw.toString();}}return response;}

感悟心得

开源不易,请大家多给drawio点点star,没有他,就没有中国繁荣的流程图商业化盛景...拿着人家的代码来挣钱,一个start也不愿意给人家点。可悲。

虽然作者挺操蛋的,多人协作的代码没有开源(我只能靠研究官方网站的协作功能和数据格式一点点猜测用到了哪些逻辑),白板转流程图的代码也没开源。不过我觉得他做的挺对的,农夫与蛇的故事见的太多了。所以我决定和他一样,让白嫖的人踩坑去吧,这里只做原理讲解,不提供源码内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/125483.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringSecurity】九、Base64与JWT

文章目录 1、base64编码2、Base64Url3、JWT的产生背景4、JWT介绍5、JWT组成5.1 Header5.2 Payload5.3 Signature 6、JWT的使用方式7、JWT的几个特点 1、base64编码 base64是一种编码方式,不是加密方式。 所谓Base64,就是说选出64个字符:小写…

【办公类-19-03】办公中的思考——Python批量制作word单元格照片和文字(小照片系列)

背景需求: 工会老师求助:如何在word里面插入4*8的框,我怎么也拉不到4*8大小(她用的是我WORD 文本框) 我一听,这又是要手动反复黏贴“文本框”“照片”“文字”的节奏哦 我问:你要做几个人&…

搭建hadoop集群的常见问题及解决办法

问题一: namenode -format重复初始化 出现问题的原因是重复初始化时会重新生成集群ID,而dn还是原先的集群ID,两者不匹配时无法启动相应的dn进程。 怎么查找问题原因:在logs目录下找到对应节点的.log文件,使用tail -200 文件名来查…

远程工作面试:特殊情况下的面试技巧

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

Web3 solidity编写cancelorder取消订单函数 并梳理讲述逻辑

上文 Web3 solidity订单池操作 中 我们讲述了订单池的基本概念 并手动编写了创建订单的操作 最近的 我们还是先将 ganache 环境起起来 然后 我们打开项目 上文中 我们写了makeOrder创建订单的函数 但是 也带出一个问题 我们创建之后 如果不要了 怎么干掉呀? js中我…

MongoDB常用的比较符号和一些功能符号

比较符号 results collection.find({age: {$gt: 20}})功能符号 results collection.find({name: {$regex: ^M.*}})

电水壶上要求亚马逊美国站SOR/2016-181和CSA22.1标准?

电水壶作为一种常见的小家电,受到了广大消费者的喜爱。然而,由于安全问题的日益重视,亚马逊加拿大站决定加强对电水壶产品的审核,以确保消费者的安全和权益。 近日,亚马逊平台发布公告,要求在加拿大站销售…

鸿蒙应用程序入口UIAbility详解

一、UIAbility概述 UIAbility是一种包含用户界面的应用组件,主要用于和用户进行交互。UIAbility也是系统调度的单元,为应用提供窗口在其中绘制界面。每一个UIAbility实例,都对应于一个最近任务列表中的任务。一个应用可以有一个UIAbility&am…

java从入门到起飞(八)——循环和递归

文章目录 Java循环1. 什么是循环?1.1 为什么需要循环?1.2 循环的分类 2. Java中的循环结构2.1 for循环2.2 while循环2.3 do-while循环 3. 循环控制语句3.1 break语句3.2 continue语句 4. 总结 Java递归1. 什么是递归2. 递归的原理3. 递归的实现4. 递归的…

【二分答案 dp】 Bare Minimum Difference

分析: 首先我们能够得知这个优秀值具有单调性: 如果一个优秀值 x 1 x1 x1能够满足题目要求,那么任何 x ( x > x 1 ) x(x>x1) x(x>x1)显然都能符合要求 基于这一特性,我们想到二分答案 直接二分这个答案好像难以维护。 …

Php“梦寻”淘宝天猫商品详情数据接口,淘宝商品详情数据API接口,淘宝API接口申请指南(含代码示例)

淘宝商品详情接口 API 是开放平台提供的一种 API 接口,它可以帮助开发者获取淘宝商品的详细信息,包括商品的标题、描述、图片等信息。在淘宝电商平台的开发中,淘宝详情接口 API 是非常常用的 API,因此本文将详细介绍淘宝详情接口 …

【笔试强训选择题】Day37.习题(错题)解析

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!! 文章目录 前言一、Day…

macbookpro怎么删除软件没有鼠标

macbookpro怎么删除软件没有鼠标,macbookpro触摸板可以替代鼠标进行操作。左右键功能与鼠标相同,可用于执行删除操作。此外,还可以利用键盘上的Delete键来删除选中的文件。 删除软件方法 方法1、打开应用程序,键盘按住control,加点…

Android Automotive编译

系统准备 安装系统 准备一台安装Ubuntu系统的机器(windows系统的机器可以通过WSL安装ubuntu系统) 安装docker 本文使用docker进行编译,因此提前安装docker。参考网络链接安装docker并设置为不使用sudo进行docker操作。 参考链接&#xff…

E5071C是德科技网络分析仪

描述 E5071C网络分析仪提供同类产品中最高的RF性能和最快的速度,具有宽频率范围和多功能。E5071C是制造和R&D工程师评估频率范围高达20 GHz的RF元件和电路的理想解决方案。特点: 宽动态范围:测试端口的动态范围> 123 dB(典型值)快速测量速度:41毫秒全2端口…

什么是IIFE(Immediately Invoked Function Expression)?它有什么作用?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐IIFE 的基本语法⭐IIFE 的主要作用⭐如何使用 IIFE 来创建私有变量和模块封装⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅…

界面控件DevExpress WinForms工具栏菜单组件,模拟流行办公软件!

DevExpress WinForms的工具栏和菜单组件灵感来自于Microsoft Office,并针对WinForms开发人员进行了优化,可以帮助开发者快速模拟当下流行的办公软件应用程序。 DevExpress WinForms有180组件和UI库,能为Windows Forms平台创建具有影响力的业…

Matlab(画图进阶)

目录 大纲 1.特殊的Plots 1.1 loglog(双对数刻度图) ​1.3 plotyy(创建具有两个y轴的图形) 1.4yyaxis(创建具有两个y轴的图) 1.5 bar 3D条形图(bar3) 1.6 pie(饼图) 3D饼图 1.7 polar 2.Stairs And Ste阶梯图 3.Boxplot 箱型图和Error Bar误差条形图 3.1 boxplot 3.2 …

ASP.NET Core 中的 MVC架构

MVC 架构 MVC架构把 App 按照逻辑分成三层: Controllers,接收 http request,配合 model,通过http response 返回 view,尽量不做别的事Models, 负责业务逻辑,App 的状态,以及数据处理Views&…

借助AI分析哥斯拉木马原理与Tomcat回显链路挖掘

前言 本次分析使用了ChatGPT进行辅助分析&#xff0c;大大提升了工作效率&#xff0c;很快就分析出木马的工作流程和构造出利用方式。 分析 首先对该木马进行格式化,以增强代码的可读性。得到如下代码 <jsp:root xmlns:jsp"http://java.sun.com/JSP/Page" vers…