dot.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package svg
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "html/template"
  7. "strings"
  8. "go-common/app/admin/main/aegis/model/net"
  9. "go-common/library/log"
  10. )
  11. var tpl = `
  12. <!DOCTYPE html>
  13. <html lang="en">
  14. <head>
  15. <meta charset="utf-8">
  16. <title>流程网描述图</title>
  17. <script src="https://cdn.bootcss.com/viz.js/2.1.1/viz.js"></script>
  18. <script src="https://cdn.bootcss.com/viz.js/2.1.1/full.render.js"></script>
  19. <script>
  20. var json={{.ActionDesc}};
  21. var actions = {{.Actions}};
  22. window.onload = function () {
  23. for( let i =0; i<actions.length; i++){
  24. document.getElementById(actions[i]).onclick = function(e){
  25. var x = e.clientX;
  26. var y = e.clientY;
  27. var content = document.getElementById("content");
  28. var table = '<table bgcolor="LightSteelBlue" border="1">'
  29. var data = JSON.parse(json[actions[i]]);
  30. for (var key in data){
  31. table +='<tr>'
  32. table+='<td>'+key+'</td>'
  33. table+='<td>'+data[key]+'</td>'
  34. table +='</tr>'
  35. }
  36. table += '</table>'
  37. content.innerHTML = table;
  38. content.style.left = x+"px";
  39. content.style.top = y+"px";
  40. content.style.position="absolute";
  41. content.style.display = "block";
  42. content.disabled = true
  43. console.log(content);
  44. }
  45. };
  46. };
  47. </script>
  48. </head>
  49. <body>
  50. <label id="content" background-color="red"></label>
  51. <script>
  52. var viz = new Viz();
  53. viz.renderSVGElement('DotCode')
  54. .then(function(element) {
  55. document.body.appendChild(element);
  56. })
  57. .catch(error => {
  58. // Create a new Viz instance (@see Caveats page for more info)
  59. viz = new Viz();
  60. // Possibly display the error
  61. console.error(error);
  62. });
  63. </script>
  64. </body>
  65. </html>
  66. `
  67. var (
  68. _labelFlow = `%s [label="%s",shape=circle]`
  69. _labelTransition = `%s [label="%s",shape=box,color=lightgrey,style=filled;]`
  70. _labelToken = `%d [label="%s",shape=Mdiamond]`
  71. _labelDirection = `%s->%s`
  72. )
  73. //NetView .
  74. type NetView struct {
  75. *template.Template
  76. Dot *Dot
  77. Data struct {
  78. NetDesc string
  79. DotCode string
  80. Actions []string
  81. ActionDesc map[string]string
  82. }
  83. }
  84. //NewNetView .
  85. func NewNetView() *NetView {
  86. return &NetView{}
  87. }
  88. //SetDot .
  89. func (nv *NetView) SetDot(dot *Dot) {
  90. nv.Dot = dot
  91. nv.Data.DotCode = dot.String()
  92. nv.Data.Actions = append(dot.nodes[:], dot.edges...)
  93. nv.Data.ActionDesc = dot.mapactions
  94. ntpl := strings.Replace(tpl, "DotCode", nv.Data.DotCode, 1)
  95. nv.Template = template.New("流程网概览")
  96. nv.Template, _ = nv.Template.Parse(ntpl)
  97. }
  98. /*
  99. 节点用record表示,节点绑定的令牌用在同一个record里面
  100. 变迁用diamond表示,若变迁绑定了令牌,则用图包含变迁和令牌
  101. */
  102. //Dot .
  103. type Dot struct {
  104. *bytes.Buffer
  105. nodes []string
  106. edges []string
  107. mapactions map[string]string
  108. flows map[int64]*net.Flow
  109. trans map[int64]*net.Transition
  110. dirs map[int64]*net.Direction
  111. tokens map[string][]*net.TokenBindDetail
  112. }
  113. //NewDot .
  114. func NewDot() *Dot {
  115. d := &Dot{
  116. mapactions: make(map[string]string),
  117. flows: make(map[int64]*net.Flow),
  118. trans: make(map[int64]*net.Transition),
  119. dirs: make(map[int64]*net.Direction),
  120. tokens: make(map[string][]*net.TokenBindDetail),
  121. }
  122. return d
  123. }
  124. //StartDot .
  125. func (d *Dot) StartDot() *Dot {
  126. d.Buffer = bytes.NewBufferString(`digraph net{rankdir=LR;`)
  127. return d
  128. }
  129. //End .
  130. func (d *Dot) End() string {
  131. d.WriteString("}")
  132. return d.String()
  133. }
  134. //AddTokenBinds .
  135. func (d *Dot) AddTokenBinds(tbs ...*net.TokenBindDetail) *Dot {
  136. for _, tb := range tbs {
  137. key := fmt.Sprintf("%d_%d", tb.Type, tb.ElementID)
  138. if tks, ok := d.tokens[key]; ok {
  139. d.tokens[key] = append(tks, tb)
  140. } else {
  141. d.tokens[key] = []*net.TokenBindDetail{tb}
  142. }
  143. }
  144. return d
  145. }
  146. //AddFlow .
  147. func (d *Dot) AddFlow(flows ...*net.Flow) *Dot {
  148. for _, flow := range flows {
  149. d.flows[flow.ID] = flow
  150. node := fmt.Sprintf(_labelFlow, flow.Name, flow.ChName)
  151. //
  152. nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
  153. d.nodes = append(d.nodes, nodeid)
  154. bs, _ := json.Marshal(flow)
  155. d.mapactions[nodeid] = string(bs)
  156. //便利token,查找绑定
  157. if tks, ok := d.tokens[fmt.Sprintf("1_%d", flow.ID)]; ok {
  158. d.WriteString("subgraph cluster_" + flow.Name + " {")
  159. d.WriteString(node + ";")
  160. for _, tk := range tks {
  161. node := fmt.Sprintf(_labelToken, tk.ID, tk.ChName)
  162. d.WriteString(node + ";")
  163. nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
  164. d.nodes = append(d.nodes, nodeid)
  165. bs, _ := json.Marshal(tk)
  166. d.mapactions[nodeid] = string(bs)
  167. }
  168. d.WriteString("}")
  169. } else {
  170. d.WriteString(node + ";")
  171. }
  172. }
  173. return d
  174. }
  175. //AddTransitions .
  176. func (d *Dot) AddTransitions(trans ...*net.Transition) *Dot {
  177. for _, tran := range trans {
  178. d.trans[tran.ID] = tran
  179. node := fmt.Sprintf(_labelTransition, tran.Name, tran.ChName)
  180. nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
  181. d.nodes = append(d.nodes, nodeid)
  182. bs, _ := json.Marshal(tran)
  183. d.mapactions[nodeid] = string(bs)
  184. //便利token,查找绑定
  185. if tks, ok := d.tokens[fmt.Sprintf("2_%d", tran.ID)]; ok {
  186. d.WriteString("subgraph cluster_" + tran.Name + " {")
  187. d.WriteString(node + ";")
  188. for _, tk := range tks {
  189. node := fmt.Sprintf(_labelToken, tk.ID, tk.ChName)
  190. d.WriteString(node + ";")
  191. nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
  192. d.nodes = append(d.nodes, nodeid)
  193. bs, _ := json.Marshal(tk)
  194. d.mapactions[nodeid] = string(bs)
  195. }
  196. d.WriteString("}")
  197. } else {
  198. d.WriteString(node + ";")
  199. }
  200. }
  201. return d
  202. }
  203. //AddDirections .
  204. func (d *Dot) AddDirections(dirs ...*net.Direction) *Dot {
  205. for _, dir := range dirs {
  206. var (
  207. start, end string
  208. flow *net.Flow
  209. trans *net.Transition
  210. )
  211. flow = d.flows[dir.FlowID]
  212. trans = d.trans[dir.TransitionID]
  213. if flow == nil || trans == nil {
  214. log.Error("invalid direction(%+v)", dir)
  215. continue
  216. }
  217. if dir.Direction == 1 {
  218. start, end = flow.Name, trans.Name
  219. }
  220. if dir.Direction == 2 {
  221. start, end = trans.Name, flow.Name
  222. }
  223. edge := fmt.Sprintf(_labelDirection, start, end)
  224. d.WriteString(edge + ";")
  225. edgeid := fmt.Sprintf("edge%d", len(d.edges)+1)
  226. bs, _ := json.Marshal(dir)
  227. d.mapactions[edgeid] = string(bs)
  228. d.edges = append(d.edges, edgeid)
  229. }
  230. return d
  231. }
  232. var (
  233. flow1 = &net.Flow{ID: 1, ChName: "节点1", Name: "flow1"}
  234. flow2 = &net.Flow{ID: 2, ChName: "节点2", Name: "flow2"}
  235. flow3 = &net.Flow{ID: 3, ChName: "节点3", Name: "flow3"}
  236. flow4 = &net.Flow{ID: 4, ChName: "节点4", Name: "flow4"}
  237. flow5 = &net.Flow{ID: 5, ChName: "节点5", Name: "flow5"}
  238. flow6 = &net.Flow{ID: 6, ChName: "节点6", Name: "flow6"}
  239. flow7 = &net.Flow{ID: 7, ChName: "节点7", Name: "flow7"}
  240. tran1 = &net.Transition{ID: 1, ChName: "变迁1", Name: "tran1"}
  241. tran2 = &net.Transition{ID: 2, ChName: "变迁2", Name: "tran2"}
  242. tran3 = &net.Transition{ID: 3, ChName: "变迁3", Name: "tran3"}
  243. dir1 = &net.Direction{ID: 1, FlowID: 1, TransitionID: 1, Direction: 1}
  244. dir2 = &net.Direction{ID: 2, FlowID: 2, TransitionID: 1, Direction: 2}
  245. dir3 = &net.Direction{ID: 3, FlowID: 2, TransitionID: 2, Direction: 1}
  246. dir4 = &net.Direction{ID: 4, FlowID: 2, TransitionID: 3, Direction: 1}
  247. dir5 = &net.Direction{ID: 5, FlowID: 3, TransitionID: 2, Direction: 2}
  248. dir6 = &net.Direction{ID: 6, FlowID: 4, TransitionID: 2, Direction: 2}
  249. dir7 = &net.Direction{ID: 7, FlowID: 5, TransitionID: 3, Direction: 2}
  250. dir8 = &net.Direction{ID: 8, FlowID: 6, TransitionID: 2, Direction: 2}
  251. dir9 = &net.Direction{ID: 9, FlowID: 7, TransitionID: 3, Direction: 2}
  252. tk1 = &net.TokenBindDetail{
  253. ID: 1,
  254. Type: 1,
  255. ElementID: 1,
  256. ChName: "待审核",
  257. }
  258. tk2 = &net.TokenBindDetail{
  259. ID: 2,
  260. Type: 2,
  261. ElementID: 1,
  262. ChName: "通过",
  263. }
  264. )
  265. func DebugSVG() (nv *NetView) {
  266. dot := NewDot()
  267. dot.StartDot().AddTokenBinds(tk1, tk2).
  268. AddFlow(flow1, flow2, flow3, flow4, flow5, flow6, flow7).
  269. AddTransitions(tran1, tran2, tran3).
  270. AddDirections(dir1, dir2, dir3, dir4, dir5, dir6, dir7, dir8, dir9).
  271. End()
  272. nv = NewNetView()
  273. nv.SetDot(dot)
  274. return nv
  275. }