给宝宝做辅食的网站,小网站托管费用,专业郑州做网站的公司,茂名优化网站建设前言
在线上办公如火如荼的今天#xff0c;多人协作功能是每个应用绕不开的门槛。processflow在线流程图#xff08;前身基于drawio二次开发#xff09;沉寂两年之久#xff0c;经过长时间设计开发#xff0c;调整#xff0c;最终完成了多人协作的核心模块设计。废话不多…前言
在线上办公如火如荼的今天多人协作功能是每个应用绕不开的门槛。processflow在线流程图前身基于drawio二次开发沉寂两年之久经过长时间设计开发调整最终完成了多人协作的核心模块设计。废话不多说上操作视频展示下效果 Video_2023-09-04_150131 多人协作技术原理剖析
秉着都2023年了谁还重复造轮子的理念这里现学现用利用了一些商业成熟的技术和软件来实现自己的目标。主要用到了两点技术
利用websocket的pushsubscribe实现多人之间的行为共享鼠标点击移动图标内容变更利用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_typeauthorization_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_typerefresh_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也不愿意给人家点。可悲。
虽然作者挺操蛋的多人协作的代码没有开源我只能靠研究官方网站的协作功能和数据格式一点点猜测用到了哪些逻辑白板转流程图的代码也没开源。不过我觉得他做的挺对的农夫与蛇的故事见的太多了。所以我决定和他一样让白嫖的人踩坑去吧这里只做原理讲解不提供源码内容。