Mind,Diagram,Design
最近在設計新版的流程渲染引擎,考慮采用無損失放縮的矢量圖SVG去搞,從技術調研到實現蠻有心得的,這裏貼出自己的設計思路,歡迎拍磚~
首先說下訴求,簡單說就是繪製流程地圖。其次簡單列舉下需要注意的技術點:
(1) 圖形元素和坐標元素解析(合並or分拆)
(2) DOM樹解析,原生JAXP,DOM4J or SVG DOM utils(混用會帶來很多問題)
(3) REST Service Lib選型
(4) SVG 元素渲染技巧
(5)一些幾何算法(旋轉矢量,斜率,兩點中距,碰撞檢測)
(6)XML,XPath等一些奇淫技巧(Axis,namespace,doctype,qname,EntityResolver等)
好,Mind有了,我們上圖評設計;
圖1 : 原型 - 比較醜
圖2 : 預期 - 比較美
圖3 : Core Class Diagram
圖4 : REST Request Response Sequence Diagram
圖5: Process-Figure Render Sequence Diagram
SVG代碼片斷一:(偽table樣式)
<?xml version='1.0' standalone='no'?> <!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'> <svg width='100%' height='100%' xmlns='https://www.w3.org/2000/svg' xmlns:xlink='https://www.w3.org/1999/xlink'> <title>SVG Tables</title> <g id='columnGroup'> <rect x='25' y='30' width='35' height='70' fill="#c9dae5"/> <rect x='195' y='30' width='35' height='70' fill="#e0f0f9"/> <rect x='395' y='30' width='35' height='70' fill="#e0f0f9"/> <text x='30' y='30' font-size='12' text-anchor='left'> <tspan x='30' dy='1em'>表頭</tspan> <tspan x='30' dy='2em'>表頭</tspan> <tspan x='30' dy='2em'>表頭</tspan> </text> <text x='100' y='30' font-size='12' text-anchor='left'> <tspan x='100' dy='1em'>同意</tspan> <tspan x='100' dy='2em'>調查</tspan> <tspan x='100' dy='2em'>中國</tspan> </text> <text x='200' y='30' font-size='12' text-anchor='left'> <tspan x='200' dy='1em'>同意</tspan> <tspan x='200' dy='2em'>調查</tspan> <tspan x='200' dy='2em'>中國</tspan> </text> <text x='300' y='30' font-size='12' text-anchor='left'> <tspan x='300' dy='1em'>同意</tspan> <tspan x='300' dy='2em'>調查</tspan> <tspan x='300' dy='2em'>中國</tspan> </text> <text x='400' y='30' font-size='12' text-anchor='left'> <tspan x='400' dy='1em'>同意</tspan> <tspan x='400' dy='2em'>調查</tspan> <tspan x='400' dy='2em'>中國</tspan> </text> </g> <g id='rowGroup' transform='translate(0, 160)'> <rect x='25' y='0' width='230' height='20' fill="#c9dae5"/> <animateColor attributeName="stroke" from="none" to="green" repeatCount="indefinite" dur="1.3s" calcMode="spline" /> <rect x='25' y='40' width='230' height='20' fill="#e0f0f9"/> <rect x='25' y='80' width='230' height='20' fill="#e0f0f9"/> <text x='30' y='0' font-size='12' text-anchor='left'> <tspan x='30' dy='1em'>表頭</tspan> <tspan x='100'>表頭</tspan> <tspan x='200'>表頭</tspan> </text> <text x='30' y='0' font-size='12' text-anchor='left'> <tspan x='30' dy='2.7em'>同意</tspan> <tspan x='100'>Expenses</tspan> <tspan x='200'>中國</tspan> </text> <text x='30' y='0' font-size='12' text-anchor='left'> <tspan x='30' dy='4.4em' >同意</tspan> <tspan x='100'>Expenses</tspan> <tspan x='200'>中國</tspan> </text> <text x='30' y='0' font-size='12' text-anchor='left'> <tspan x='30' dy='6.1em'>同意</tspan> <tspan x='100'>Expenses</tspan> <tspan x='200'>中國</tspan> </text> <text x='30' y='0' font-size='12' text-anchor='left'> <tspan x='30' dy='7.8em'>同意</tspan> <tspan x='100'>Expenses</tspan> <tspan x='200'>中國</tspan> </text> </g> <g> <rect x="370.0" y="144.0" width="600" height="15" fill="#c9dae5"/> <text x="377.0" y="144.0" font-size="11" text-anchor="left"> <tspan x="377.0" dy="1em">節點開始時間</tspan> <animateColor attributeName="stroke" from="none" to="red" repeatCount="indefinite" dur="1.3s" calcMode="discrete" /> <tspan x="507.0">節點結束時間</tspan> <tspan x="637.0">計劃處理人</tspan> <tspan x="767.0">實際處理人</tspan> <tspan x="897.0">審批決策</tspan> </text> <text x="377.0" y="144.0" font-size="11" text-anchor="left"> <tspan x="377.0" dy="2.5em">12-10-26 上午9:40</tspan> <tspan x="507.0" >12-10-26 下午2:53</tspan> <tspan x="637.0" >yun.ma</tspan> <tspan x="637.0" dy="1.5em">@alibaba-inc.com</tspan> <tspan x="767.0">admin@xbpms</tspan> <tspan x="897.0">重新進入</tspan> </text> </g> </svg>
SVG代碼片斷二:(流程結點)
<?xml version="1.0" standalone="no"?> <svg width="640" height="480" xmlns:xlink="https://www.w3.org/1999/xlink" xmlns="https://www.w3.org/2000/svg"> <!-- Created with SVG-edit - https://svg-edit.googlecode.com/ --> <g> <title>公安機關財務報銷申請</title> <g > <rect x="206" y="232" width="123" height="38" fill="#cbeddb" stroke="#0000bf" stroke-width="2"/> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#000000" y="263" x="274">結束</text> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#008000" y="245" x="272"><<End State>></text> <rect x="216" y="247" width="12" height="12" fill="#ff0000" stroke="#bf0000" stroke-width="2" stroke-dasharray="null"/> </g> <g > <path d="m253,140l11,5.199997l-11,7.800003l0,-13z" fill="#000000" stroke="#000000" stroke-dasharray="null" transform="rotate(92.72631072998047 258.50000000000006,146.5) "/> <line x1="254" y1="95.000002" x2="259" y2="140" stroke="#000000" stroke-dasharray="null" fill="none"/> </g> <g > <rect stroke-opacity="0.71" x="198" y="56" width="123" height="38" fill="#cbeddb" stroke="#261b1b" stroke-width="2"/> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#000000" y="88" x="266">財務報銷申請</text> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#008000" y="69" x="264"><<Start State>></text> <g > <circle cx="211.500001" cy="78" r="8.425827" fill="#007f00" stroke="#007f00" stroke-width="null" stroke-dasharray="null"/> <path d="m208.96106,71.672707l0.005127,12.181953l10.376938,-6.133987l-10.382065,-6.047951z" fill="#d0e0d0" stroke="#007f00" stroke-width="null" stroke-dasharray="null"/> </g> </g> <g > <rect x="203" y="153" width="123" height="38" fill="#cbeddb" stroke="#ff0000" stroke-width="2"/> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#000000" y="185" x="271">主管申請</text> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#008000" y="166" x="269"><<Task State>></text> <g > <rect x="211" y="169" width="13.5" height="13.935483" fill="#e5e5e5" stroke="#a59898" stroke-width="2" stroke-dasharray="null"/> <g > <rect x="213.5" y="171.064516" width="13.5" height="13.935483" fill="#e5e5e5" stroke="#a59898" stroke-width="2" stroke-dasharray="null" /> <line x1="214.5" y1="174.677419" x2="225.5" y2="174.677419" stroke="#a59898" stroke-width="2" stroke-dasharray="null" fill="none"/> <line x1="215" y1="177.774193" x2="226" y2="177.774193" stroke="#a59898" stroke-width="2" stroke-dasharray="null" fill="none" /> <line x1="214.5" y1="180.870967" x2="225.5" y2="180.870967" stroke="#a59898" stroke-width="2" stroke-dasharray="null" fill="none" /> </g> </g> </g> <g > <rect x="189" y="311" width="123" height="38" fill="#cbeddb" stroke="#0000bf" stroke-width="2" /> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#000000" y="342" x="259" >Dubbo遠程服務</text> <text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="12" stroke-dasharray="null" stroke-width="0" stroke="#000000" fill="#008000" y="327" x="256" ><<Dubbo Node>></text> <g > <circle cx="200.090371" cy="326.131744" r="4.032028" fill="#f7f77e" stroke="#e5e557" stroke-width="2" stroke-dasharray="null" /> <circle cx="204.73279" cy="334.544693" r="4.032028" fill="#f98a6b" stroke="#ff5656" stroke-width="2" stroke-dasharray="null" /> <circle cx="196.267203" cy="334.868268" r="4.032028" fill="#5692ce" stroke="#5858d3" stroke-width="2" stroke-dasharray="null" /> </g> </g> </g> </svg>
核心代碼片段
/** * REST API for SVG Process-Runtime-Figure * * @author Von Gosling * @version 1.0.0 */ @Singleton @Path("svg") public class SVGProcessFigureResource { private final Logger log = LoggerFactory.getLogger(getClass()); @SuppressWarnings("unchecked") @GET @Produces(MediaType.APPLICATION_SVG_XML) public Response get(@Context HttpServletRequest request, @DefaultValue("") @QueryParam("procId") String procId, @DefaultValue("") @QueryParam("procName") String procName, @DefaultValue("") @QueryParam("version") String version) { String content = ""; try { Document doc = processDefinitionReader.getXmlProcessDefinition(procName, NumberUtils.toInt(version, -1)); if (StringUtils.isNotBlank(procId)) { // 獲取靜態流程上下文 long procInstId = Long.parseLong(procId); HashMap<String, Object> staticProcessContext = (HashMap<String, Object>) processEngine .getProcInstVarData(procInstId).get(0); // 獲取流程運行期上下文 Map<String, String> dynamicProcessContext = getProcRuntimeContext(procInstId); content = SVGProcessFigureGenerator.getSVG(doc, staticProcessContext, dynamicProcessContext); } else { content = SVGProcessFigureGenerator.getSVG(doc); } } catch (Exception e) { log.error("Error occurs when getting SVG.", e); return Response.status(Status.INTERNAL_SERVER_ERROR) .entity("Error occurs,please visit later !").build(); } return Response.ok().entity(content).build(); }
/** * @author Von Gosling * @version 1.0.0 */ public class DomMappingPostProcessor { public static Document process(HashMap<String, Object> spContextMap, Map<String, String> dpContextMap, org.w3c.dom.Document svgDoc) throws DocumentException { //1. Xpath with namespace enabled Map<String, String> npURIs = Maps.newHashMap(); npURIs.put("svg", SVGConstants.SVG_NAMESPACE_URI); npURIs.put("xlink", SVGConstants.XLINK_NAMESPACE_URI); DOMReader reader = new DOMReader(); reader.getDocumentFactory().setXPathNamespaceURIs(npURIs); Document doc = reader.read(svgDoc); //doc.accept(new NameSpaceCleaner()); //doc.setEntityResolver(new IngoreDtdEntityResolver()); //2.Append inline and external script JSBuilder.buildInlineScript(spContextMap, doc); JSBuilder.buildExternalScript(doc); //3.Append process context ContextBuilder.buildStaticProcessContext(doc); ContextBuilder.buildDynamicProcessContext(dpContextMap, doc); return doc; } }
好了,差不多了,整個技術設計,編碼實現都圓滿KO~
歡迎大家對設計,實現部分提出異議,
最後更新:2017-04-02 00:06:48