package svg import ( "bytes" "encoding/json" "fmt" "html/template" "strings" "go-common/app/admin/main/aegis/model/net" "go-common/library/log" ) var tpl = ` 流程网描述图 ` var ( _labelFlow = `%s [label="%s",shape=circle]` _labelTransition = `%s [label="%s",shape=box,color=lightgrey,style=filled;]` _labelToken = `%d [label="%s",shape=Mdiamond]` _labelDirection = `%s->%s` ) //NetView . type NetView struct { *template.Template Dot *Dot Data struct { NetDesc string DotCode string Actions []string ActionDesc map[string]string } } //NewNetView . func NewNetView() *NetView { return &NetView{} } //SetDot . func (nv *NetView) SetDot(dot *Dot) { nv.Dot = dot nv.Data.DotCode = dot.String() nv.Data.Actions = append(dot.nodes[:], dot.edges...) nv.Data.ActionDesc = dot.mapactions ntpl := strings.Replace(tpl, "DotCode", nv.Data.DotCode, 1) nv.Template = template.New("流程网概览") nv.Template, _ = nv.Template.Parse(ntpl) } /* 节点用record表示,节点绑定的令牌用在同一个record里面 变迁用diamond表示,若变迁绑定了令牌,则用图包含变迁和令牌 */ //Dot . type Dot struct { *bytes.Buffer nodes []string edges []string mapactions map[string]string flows map[int64]*net.Flow trans map[int64]*net.Transition dirs map[int64]*net.Direction tokens map[string][]*net.TokenBindDetail } //NewDot . func NewDot() *Dot { d := &Dot{ mapactions: make(map[string]string), flows: make(map[int64]*net.Flow), trans: make(map[int64]*net.Transition), dirs: make(map[int64]*net.Direction), tokens: make(map[string][]*net.TokenBindDetail), } return d } //StartDot . func (d *Dot) StartDot() *Dot { d.Buffer = bytes.NewBufferString(`digraph net{rankdir=LR;`) return d } //End . func (d *Dot) End() string { d.WriteString("}") return d.String() } //AddTokenBinds . func (d *Dot) AddTokenBinds(tbs ...*net.TokenBindDetail) *Dot { for _, tb := range tbs { key := fmt.Sprintf("%d_%d", tb.Type, tb.ElementID) if tks, ok := d.tokens[key]; ok { d.tokens[key] = append(tks, tb) } else { d.tokens[key] = []*net.TokenBindDetail{tb} } } return d } //AddFlow . func (d *Dot) AddFlow(flows ...*net.Flow) *Dot { for _, flow := range flows { d.flows[flow.ID] = flow node := fmt.Sprintf(_labelFlow, flow.Name, flow.ChName) // nodeid := fmt.Sprintf("node%d", len(d.nodes)+1) d.nodes = append(d.nodes, nodeid) bs, _ := json.Marshal(flow) d.mapactions[nodeid] = string(bs) //便利token,查找绑定 if tks, ok := d.tokens[fmt.Sprintf("1_%d", flow.ID)]; ok { d.WriteString("subgraph cluster_" + flow.Name + " {") d.WriteString(node + ";") for _, tk := range tks { node := fmt.Sprintf(_labelToken, tk.ID, tk.ChName) d.WriteString(node + ";") nodeid := fmt.Sprintf("node%d", len(d.nodes)+1) d.nodes = append(d.nodes, nodeid) bs, _ := json.Marshal(tk) d.mapactions[nodeid] = string(bs) } d.WriteString("}") } else { d.WriteString(node + ";") } } return d } //AddTransitions . func (d *Dot) AddTransitions(trans ...*net.Transition) *Dot { for _, tran := range trans { d.trans[tran.ID] = tran node := fmt.Sprintf(_labelTransition, tran.Name, tran.ChName) nodeid := fmt.Sprintf("node%d", len(d.nodes)+1) d.nodes = append(d.nodes, nodeid) bs, _ := json.Marshal(tran) d.mapactions[nodeid] = string(bs) //便利token,查找绑定 if tks, ok := d.tokens[fmt.Sprintf("2_%d", tran.ID)]; ok { d.WriteString("subgraph cluster_" + tran.Name + " {") d.WriteString(node + ";") for _, tk := range tks { node := fmt.Sprintf(_labelToken, tk.ID, tk.ChName) d.WriteString(node + ";") nodeid := fmt.Sprintf("node%d", len(d.nodes)+1) d.nodes = append(d.nodes, nodeid) bs, _ := json.Marshal(tk) d.mapactions[nodeid] = string(bs) } d.WriteString("}") } else { d.WriteString(node + ";") } } return d } //AddDirections . func (d *Dot) AddDirections(dirs ...*net.Direction) *Dot { for _, dir := range dirs { var ( start, end string flow *net.Flow trans *net.Transition ) flow = d.flows[dir.FlowID] trans = d.trans[dir.TransitionID] if flow == nil || trans == nil { log.Error("invalid direction(%+v)", dir) continue } if dir.Direction == 1 { start, end = flow.Name, trans.Name } if dir.Direction == 2 { start, end = trans.Name, flow.Name } edge := fmt.Sprintf(_labelDirection, start, end) d.WriteString(edge + ";") edgeid := fmt.Sprintf("edge%d", len(d.edges)+1) bs, _ := json.Marshal(dir) d.mapactions[edgeid] = string(bs) d.edges = append(d.edges, edgeid) } return d } var ( flow1 = &net.Flow{ID: 1, ChName: "节点1", Name: "flow1"} flow2 = &net.Flow{ID: 2, ChName: "节点2", Name: "flow2"} flow3 = &net.Flow{ID: 3, ChName: "节点3", Name: "flow3"} flow4 = &net.Flow{ID: 4, ChName: "节点4", Name: "flow4"} flow5 = &net.Flow{ID: 5, ChName: "节点5", Name: "flow5"} flow6 = &net.Flow{ID: 6, ChName: "节点6", Name: "flow6"} flow7 = &net.Flow{ID: 7, ChName: "节点7", Name: "flow7"} tran1 = &net.Transition{ID: 1, ChName: "变迁1", Name: "tran1"} tran2 = &net.Transition{ID: 2, ChName: "变迁2", Name: "tran2"} tran3 = &net.Transition{ID: 3, ChName: "变迁3", Name: "tran3"} dir1 = &net.Direction{ID: 1, FlowID: 1, TransitionID: 1, Direction: 1} dir2 = &net.Direction{ID: 2, FlowID: 2, TransitionID: 1, Direction: 2} dir3 = &net.Direction{ID: 3, FlowID: 2, TransitionID: 2, Direction: 1} dir4 = &net.Direction{ID: 4, FlowID: 2, TransitionID: 3, Direction: 1} dir5 = &net.Direction{ID: 5, FlowID: 3, TransitionID: 2, Direction: 2} dir6 = &net.Direction{ID: 6, FlowID: 4, TransitionID: 2, Direction: 2} dir7 = &net.Direction{ID: 7, FlowID: 5, TransitionID: 3, Direction: 2} dir8 = &net.Direction{ID: 8, FlowID: 6, TransitionID: 2, Direction: 2} dir9 = &net.Direction{ID: 9, FlowID: 7, TransitionID: 3, Direction: 2} tk1 = &net.TokenBindDetail{ ID: 1, Type: 1, ElementID: 1, ChName: "待审核", } tk2 = &net.TokenBindDetail{ ID: 2, Type: 2, ElementID: 1, ChName: "通过", } ) func DebugSVG() (nv *NetView) { dot := NewDot() dot.StartDot().AddTokenBinds(tk1, tk2). AddFlow(flow1, flow2, flow3, flow4, flow5, flow6, flow7). AddTransitions(tran1, tran2, tran3). AddDirections(dir1, dir2, dir3, dir4, dir5, dir6, dir7, dir8, dir9). End() nv = NewNetView() nv.SetDot(dot) return nv }