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'
  'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg width='100%' height='100%' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://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 id="b_rect" x='25' y='0' width='230' height='20' fill="#c9dae5"/>
	  <animateColor id="b_rect" 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 id="t_blink" x="377.0" dy="1em">节点开始时间</tspan>
		<animateColor id="t_blink" 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="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>公安机关财务报销申请</title>
  <g id="svg_60">
   <rect id="svg_11" x="206" y="232" width="123" height="38" fill="#cbeddb" stroke="#0000bf" stroke-width="2"/>
   <text id="svg_12" 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 id="svg_13" 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" id="svg_24" fill="#ff0000" stroke="#bf0000" stroke-width="2" stroke-dasharray="null"/>
  </g>
  <g id="svg_61">
   <path d="m253,140l11,5.199997l-11,7.800003l0,-13z" id="svg_32" 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" id="svg_33" stroke="#000000" stroke-dasharray="null" fill="none"/>
  </g>
  <g id="svg_62">
   <rect stroke-opacity="0.71" x="198" y="56" width="123" height="38" id="svg_1" 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" id="svg_4" 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" id="svg_5" y="69" x="264"><<Start State>></text>
   <g id="svg_23">
    <circle cx="211.500001" cy="78" r="8.425827" id="svg_21" 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" id="svg_22" fill="#d0e0d0" stroke="#007f00" stroke-width="null" stroke-dasharray="null"/>
   </g>
  </g>
  <g id="svg_63">
   <rect id="svg_8" x="203" y="153" width="123" height="38" fill="#cbeddb" stroke="#ff0000" stroke-width="2"/>
   <text id="svg_9" 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 id="svg_10" 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 id="svg_31">
    <rect x="211" y="169" width="13.5" height="13.935483" id="svg_25" fill="#e5e5e5" stroke="#a59898" stroke-width="2" stroke-dasharray="null"/>
    <g id="svg_30">
     <rect x="213.5" y="171.064516" width="13.5" height="13.935483" fill="#e5e5e5" stroke="#a59898" stroke-width="2" stroke-dasharray="null" id="svg_26"/>
     <line x1="214.5" y1="174.677419" x2="225.5" y2="174.677419" id="svg_27" 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" id="svg_28"/>
     <line x1="214.5" y1="180.870967" x2="225.5" y2="180.870967" stroke="#a59898" stroke-width="2" stroke-dasharray="null" fill="none" id="svg_29"/>
    </g>
   </g>
  </g>
  <g id="svg_64">
   <rect x="189" y="311" width="123" height="38" fill="#cbeddb" stroke="#0000bf" stroke-width="2" id="svg_43"/>
   <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" id="svg_44">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" id="svg_45"><<Dubbo Node>></text>
   <g id="svg_55">
    <circle cx="200.090371" cy="326.131744" r="4.032028" fill="#f7f77e" stroke="#e5e557" stroke-width="2" stroke-dasharray="null" id="svg_56"/>
    <circle cx="204.73279" cy="334.544693" r="4.032028" fill="#f98a6b" stroke="#ff5656" stroke-width="2" stroke-dasharray="null" id="svg_57"/>
    <circle cx="196.267203" cy="334.868268" r="4.032028" fill="#5692ce" stroke="#5858d3" stroke-width="2" stroke-dasharray="null" id="svg_58"/>
   </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~

    欢迎大家对设计,实现部分提出异议,大笑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值