Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"direction_test.go",
"engine_test.go",
"flow_resource_test.go",
"flow_test.go",
"log_test.go",
"message_test.go",
"monitor_test.go",
"net_cache_test.go",
"net_control_test.go",
"net_test.go",
"report_test.go",
"role_test.go",
"service_test.go",
"task_config_test.go",
"token_test.go",
"transition_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/aegis/conf:go_default_library",
"//app/admin/main/aegis/model:go_default_library",
"//app/admin/main/aegis/model/common:go_default_library",
"//app/admin/main/aegis/model/monitor:go_default_library",
"//app/admin/main/aegis/model/net:go_default_library",
"//app/admin/main/aegis/model/resource:go_default_library",
"//app/admin/main/aegis/model/task:go_default_library",
"//library/database/orm:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"business.go",
"direction.go",
"engine.go",
"extra_func.go",
"flow.go",
"flow_resource.go",
"log.go",
"message.go",
"monitor.go",
"net.go",
"net_cache.go",
"net_control.go",
"report.go",
"resource.go",
"role.go",
"service.go",
"svg.go",
"task_config.go",
"task_consumer.go",
"task_dispatch.go",
"token.go",
"token_bind.go",
"transition.go",
],
importpath = "go-common/app/admin/main/aegis/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/aegis/conf:go_default_library",
"//app/admin/main/aegis/dao/gorm:go_default_library",
"//app/admin/main/aegis/dao/http:go_default_library",
"//app/admin/main/aegis/dao/mc:go_default_library",
"//app/admin/main/aegis/dao/mysql:go_default_library",
"//app/admin/main/aegis/dao/redis:go_default_library",
"//app/admin/main/aegis/dao/rpc:go_default_library",
"//app/admin/main/aegis/model:go_default_library",
"//app/admin/main/aegis/model/business:go_default_library",
"//app/admin/main/aegis/model/common:go_default_library",
"//app/admin/main/aegis/model/databus:go_default_library",
"//app/admin/main/aegis/model/middleware:go_default_library",
"//app/admin/main/aegis/model/monitor:go_default_library",
"//app/admin/main/aegis/model/net:go_default_library",
"//app/admin/main/aegis/model/resource:go_default_library",
"//app/admin/main/aegis/model/svg:go_default_library",
"//app/admin/main/aegis/model/task:go_default_library",
"//app/service/main/up/api/v1:go_default_library",
"//library/cache/redis:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,224 @@
package service
import (
"context"
"encoding/json"
"fmt"
"go-common/app/admin/main/aegis/model/business"
"go-common/app/admin/main/aegis/model/middleware"
"go-common/library/ecode"
"go-common/library/log"
)
// AddBusiness .
func (s *Service) AddBusiness(c context.Context, b *business.Business) (id int64, err error) {
if id, err = s.gorm.AddBusiness(c, b); err != nil {
log.Error("s.gorm.AddBusiness error(%v)", err)
}
return
}
// UpdateBusiness .
func (s *Service) UpdateBusiness(c context.Context, b *business.Business) (err error) {
if err = s.gorm.UpdateBusiness(c, b); err != nil {
log.Error("s.gorm.UpdateBusiness error(%v)", err)
}
return
}
// SetBusinessState .
func (s *Service) SetBusinessState(c context.Context, b *business.Business) (err error) {
if int8(b.State) == business.StateEnable {
if err = s.gorm.EnableBusiness(c, b.ID); err != nil {
log.Error("s.gorm.EnableBusiness error(%v)", err)
}
} else {
if err = s.gorm.DisableBusiness(c, b.ID); err != nil {
log.Error("s.gorm.EnableBusiness error(%v)", err)
}
}
return
}
// Business .
func (s *Service) Business(c context.Context, b *business.Business) (res *business.Business, err error) {
if res, err = s.gorm.Business(c, b.ID); err != nil {
log.Error("s.gorm.Business error(%v)", err)
}
return
}
// BusinessList .
func (s *Service) BusinessList(c context.Context, businessID []int64, onlyEnable bool) (res []*business.Business, err error) {
if res, err = s.gorm.BusinessList(c, 0, businessID, onlyEnable); err != nil {
log.Error("s.gorm.BusinessList error(%v) ids(%+v)", err, businessID)
}
s.mulIDtoName(c, res, s.transUnames, "UID", "UserName")
return
}
func (s *Service) transUnames(c context.Context, uids []int64) (unames map[int64][]interface{}, err error) {
unames = make(map[int64][]interface{})
uns, err := s.http.GetUnames(c, uids)
if err != nil {
return
}
for uid, uname := range uns {
unames[uid] = []interface{}{uname}
}
return
}
// AddBizCFG .
func (s *Service) AddBizCFG(c context.Context, b *business.BizCFG) (lastid int64, err error) {
return s.gorm.AddBizConfig(c, b)
}
// UpdateBizCFG .
func (s *Service) UpdateBizCFG(c context.Context, b *business.BizCFG) (err error) {
if err = s.gorm.EditBizConfig(c, b); err != nil {
log.Error("s.gorm.UpdateBizCFG error(%v)", err)
return
}
delete(s.bizCfgCache, fmt.Sprintf("%d_%d", b.BusinessID, b.TP))
delete(s.bizRoleCache, b.BusinessID)
delete(s.taskRoleCache, b.BusinessID)
delete(s.bizMiddlewareCache, b.BusinessID)
return
}
// ListBizCFGs .
func (s *Service) ListBizCFGs(c context.Context, bizid int64) (res []*business.BizCFG, err error) {
if res, err = s.gorm.GetConfigs(c, bizid); err != nil {
log.Error("s.gorm.ListBizCFGs error(%v)", err)
}
return
}
// ReserveCFG 保留字配置
func (s *Service) ReserveCFG(c context.Context, bizid int64) (rsvcfg map[string]interface{}, err error) {
var rsv string
if rsv = s.getConfig(c, bizid, business.TypeReverse); len(rsv) == 0 {
return
}
rsvcfg = make(map[string]interface{})
if err = json.Unmarshal([]byte(rsv), &rsvcfg); err != nil {
log.Error("json.Unmarshal error(%v)", err)
err = ecode.AegisReservedCfgErr
}
return
}
// AttributeCFG 属性位配置
func (s *Service) AttributeCFG(c context.Context, bizid int64) (attrcfg map[string]uint, err error) {
var attr string
if attr = s.getConfig(c, bizid, business.TypeAttribute); len(attr) == 0 {
return
}
attrcfg = make(map[string]uint)
if err = json.Unmarshal([]byte(attr), &attrcfg); err != nil {
log.Error("json.Unmarshal error(%v)", err)
}
return
}
func (s *Service) syncBizCache(c context.Context) (err error) {
var (
bizcfg []*business.BizCFG
)
if bizcfg, err = s.gorm.ActiveConfigs(c); err != nil {
return
}
bizTpMap := map[string]string{}
bizRoleMap := map[int64]map[string]int64{}
taskRoleMap := map[int64]map[int64][]int64{}
bizMiddleware := map[int64][]*middleware.Aggregate{}
for _, item := range bizcfg {
bizTpMap[fmt.Sprintf("%d_%d", item.BusinessID, item.TP)] = item.Config
biz, taskRole, err := item.FormatMngBID()
if err == nil && len(taskRole) > 0 {
taskRoleMap[biz] = taskRole
}
bizID, bizRoles, err := item.FormatBizBID()
if err == nil && len(bizRoles) > 0 {
bizRoleMap[bizID] = bizRoles
}
mws, err := item.FormatAggregate()
if err == nil && len(mws) > 0 {
bizMiddleware[item.BusinessID] = mws
}
}
s.bizCfgCache = bizTpMap
s.bizRoleCache = bizRoleMap
s.taskRoleCache = taskRoleMap
s.bizMiddlewareCache = bizMiddleware
return
}
func (s *Service) getConfig(c context.Context, bizid int64, tp int8) (cfg string) {
var ok bool
if cfg, ok = s.bizCfgCache[fmt.Sprintf("%d_%d", bizid, tp)]; ok {
return cfg
}
s.syncBizCache(c)
cfg = s.bizCfgCache[fmt.Sprintf("%d_%d", bizid, tp)]
return
}
func (s *Service) getTaskRole(c context.Context, bizid int64) (cfgs map[int64][]int64) {
var exist bool
if cfgs, exist = s.taskRoleCache[bizid]; exist {
return cfgs
}
s.syncBizCache(c)
cfgs = s.taskRoleCache[bizid]
return
}
func (s *Service) getTaskBiz(c context.Context, bids []int64) (bizs []int64, flows []int64) {
bizs = []int64{}
flows = []int64{}
s.syncBizCache(c)
for _, bid := range bids {
for biz, item := range s.taskRoleCache {
if v, exist := item[bid]; exist && len(v) > 0 {
bizs = append(bizs, biz)
flows = append(flows, v...)
}
}
}
return
}
func (s *Service) getBizRole(c context.Context, mngID int64) (bizRole map[string]int64, bizID int64) {
for biz, item := range s.bizRoleCache {
if item[business.BizBIDMngID] == mngID {
bizID = biz
bizRole = item
return
}
}
s.syncBizCache(c)
for biz, item := range s.bizRoleCache {
if item[business.BizBIDMngID] == mngID {
bizID = biz
bizRole = item
break
}
}
return
}

View File

@@ -0,0 +1,556 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
)
//ShowDirection .
func (s *Service) ShowDirection(c context.Context, id int64) (r *net.ShowDirectionResult, err error) {
var (
d *net.Direction
f *net.Flow
t *net.Transition
)
if d, err = s.gorm.DirectionByID(c, id); err != nil {
return
}
if f, err = s.gorm.FlowByID(c, d.FlowID); err != nil {
return
}
if t, err = s.gorm.TransitionByID(c, d.TransitionID); err != nil {
return
}
r = &net.ShowDirectionResult{
Direction: d,
FlowName: f.ChName,
TransitionName: t.ChName,
}
return
}
//GetDirectionList .
func (s *Service) GetDirectionList(c context.Context, pm *net.ListDirectionParam) (result *net.ListDirectionRes, err error) {
var (
n *net.Net
flows map[int64]string
trans map[int64]string
unames map[int64]string
fids = []int64{}
tids = []int64{}
uid = []int64{}
)
if n, err = s.gorm.NetByID(c, pm.NetID); err != nil {
return
}
if result, err = s.gorm.DirectionList(c, pm); err != nil {
return
}
if len(result.Result) == 0 {
return
}
for _, item := range result.Result {
fids = append(fids, item.FlowID)
tids = append(tids, item.TransitionID)
uid = append(uid, item.UID)
}
if flows, err = s.gorm.ColumnMapString(c, net.TableFlow, "ch_name", fids, ""); err != nil {
return
}
if trans, err = s.gorm.ColumnMapString(c, net.TableTransition, "ch_name", tids, ""); err != nil {
return
}
if unames, err = s.http.GetUnames(c, uid); err != nil {
log.Error("GetDirectionList s.http.GetUnames error(%v)", err)
err = nil
}
for _, item := range result.Result {
item.NetName = n.ChName
item.FlowName = flows[item.FlowID]
item.TransitionName = trans[item.TransitionID]
item.UserName = unames[item.UID]
}
return
}
//SwitchDirection .
func (s *Service) SwitchDirection(c context.Context, id int64, needDisable bool) (err error, msg string) {
var (
old *net.Direction
action string
canUpdate bool
)
if old, err = s.gorm.DirectionByID(c, id); err != nil {
log.Error("SwitchDirection s.gorm.DirectionByID(%d) error(%v) needDisable(%v)", id, err, needDisable)
return
}
available := old.IsAvailable()
if available == !needDisable {
return
}
if canUpdate, err = s.beforeUpdate(c, old.NetID); err != nil {
log.Error("SwitchDirection s.beforeUpdate error(%v)", err)
return
}
if !canUpdate {
log.Error("SwitchDirection can't update id(%d) needdisable(%v)", id, needDisable)
return
}
if needDisable {
old.DisableTime = time.Now()
action = model.LogNetActionDisable
} else {
if err = s.checkDirectionBindAvailable(c, old.FlowID, old.TransitionID); err != nil {
log.Error("SwitchDirection s.checkDirectionBindAvailable(%+v) error(%v) needDisable(%v)", old, err, needDisable)
return
}
if err, msg = s.checkDirConflict(c, old); err != nil {
log.Error("SwitchDirection s.checkDirConflict(%+v) error(%v) needDisable(%v)", old, err, needDisable)
return
}
old.DisableTime = net.Recovered
action = model.LogNetActionAvailable
}
tx, err := s.gorm.BeginTx(c)
if err != nil {
log.Error("SwitchDirection s.gorm.BeginTx error(%v)", err)
return
}
if err = s.gorm.DisableNet(c, tx, old.NetID); err != nil {
tx.Rollback()
return
}
if err = s.gorm.UpdateFields(c, tx, net.TableDirection, id, map[string]interface{}{"disable_time": old.DisableTime}); err != nil {
tx.Rollback()
return
}
if err = tx.Commit().Error; err != nil {
log.Error("SwitchDirection tx.Commit error(%v)", err)
return
}
s.delDirCache(c, old)
//日志
oper := &model.NetConfOper{
OID: old.ID,
Action: action,
UID: old.UID,
NetID: old.NetID,
FlowID: old.FlowID,
TranID: old.TransitionID,
}
s.sendNetConfLog(c, model.LogTypeDirConf, oper)
return
}
func (s *Service) checkDirectionUnique(c context.Context, netID int64, FlowID int64, transitionID int64, direction int8) (err error, msg string) {
var exist *net.Direction
if exist, err = s.gorm.DirectionByUnique(c, netID, FlowID, transitionID, direction); err != nil {
return
}
if exist != nil {
err = ecode.AegisUniqueAlreadyExist
msg = fmt.Sprintf(ecode.AegisUniqueAlreadyExist.Message(), "有向线", "")
}
return
}
func (s *Service) checkDirectionBindAvailable(c context.Context, flowID, transitionID int64) (err error) {
var (
f *net.Flow
t *net.Transition
)
if flowID > 0 {
if f, err = s.gorm.FlowByID(c, flowID); err != nil {
return
}
if !f.IsAvailable() {
err = ecode.AegisFlowDisabled
return
}
}
if transitionID > 0 {
if t, err = s.gorm.TransitionByID(c, transitionID); err != nil {
return
}
if !t.IsAvailable() {
err = ecode.AegisTranDisabled
return
}
}
return
}
/**
* 不同order兼容性不同
* order=顺序flow<->tran一对一
* order=todo
*/
func (s *Service) checkDirConflict(c context.Context, d *net.Direction) (err error, msg string) {
var (
flowDir, tranDir []*net.Direction
orderDes, conflictDes string
)
if d == nil {
return
}
if flowDir, err = s.gorm.DirectionByFlowID(c, []int64{d.FlowID}, d.Direction); err != nil {
log.Error("checkDirConflict s.gorm.DirectionByFlowID error(%v) direction(%+v)", err, d)
return
}
flowDirLen := len(flowDir)
for k, item := range flowDir {
if item.ID != d.ID {
continue
}
if k < flowDirLen-1 {
flowDir = append(flowDir[:k], flowDir[k+1:]...)
} else {
flowDir = flowDir[:k]
}
flowDirLen--
}
if tranDir, err = s.gorm.DirectionByTransitionID(c, []int64{d.TransitionID}, d.Direction, true); err != nil {
log.Error("checkDirConflict s.gorm.DirectionByTransitionID error(%v) direction(%+v)", err, d)
return
}
tranDirLen := len(tranDir)
for k, item := range tranDir {
if item.ID != d.ID {
continue
}
if k < tranDirLen-1 {
tranDir = append(tranDir[:k], tranDir[k+1:]...)
} else {
tranDir = tranDir[:k]
}
tranDirLen--
}
//无任何已有线
if flowDirLen == 0 && tranDirLen == 0 {
return
}
if d.Order == net.DirOrderSequence {
conflictDes = "节点或变迁已被绑定"
} else {
//不支持的顺序报错
log.Error("checkDirConflict order(%d) is not supported! direction(%+v)", d.Order, d)
err = ecode.RequestErr
return
}
//顺序兼容性报错
log.Error("checkDirConflict direction(%+v) not allowed!", d)
err = ecode.AegisDirOrderConflict
orderDes = net.DirOrderDesc[d.Order]
msg = fmt.Sprintf(ecode.AegisDirOrderConflict.Message(), orderDes, conflictDes)
return
}
//AddDirection .
func (s *Service) AddDirection(c context.Context, uid int64, d *net.DirEditParam) (id int64, err error, msg string) {
var (
canUpdate bool
)
if canUpdate, err = s.beforeUpdate(c, d.NetID); err != nil {
log.Error("AddDirection s.beforeUpdate error(%v)", err)
return
}
if !canUpdate {
log.Error("AddDirection can't update param(%+v)", d)
return
}
if err, msg = s.checkDirectionUnique(c, d.NetID, d.FlowID, d.TransitionID, d.Direction); err != nil {
return
}
if err = s.checkDirectionBindAvailable(c, d.FlowID, d.TransitionID); err != nil {
return
}
dir := &net.Direction{
NetID: d.NetID,
FlowID: d.FlowID,
TransitionID: d.TransitionID,
Direction: d.Direction,
Order: d.Order,
Guard: d.Guard,
Output: d.Output,
UID: uid,
}
if err, msg = s.checkDirConflict(c, dir); err != nil {
return
}
tx, err := s.gorm.BeginTx(c)
if err != nil {
log.Error("AddDirection s.gorm.BeginTx error(%v)", err)
return
}
if err = s.gorm.DisableNet(c, tx, dir.NetID); err != nil {
tx.Rollback()
return
}
if err = s.gorm.AddItem(c, tx, dir); err != nil {
tx.Rollback()
return
}
if err = tx.Commit().Error; err != nil {
log.Error("AddDirection tx.Commit error(%v)", err)
return
}
id = d.ID
//日志
oper := &model.NetConfOper{
OID: dir.ID,
Action: model.LogNetActionNew,
UID: dir.UID,
NetID: dir.NetID,
FlowID: dir.FlowID,
TranID: dir.TransitionID,
Diff: []string{
model.LogFieldTemp(model.LogFieldDirection, net.DirDirectionDesc[dir.Direction], "", false),
model.LogFieldTemp(model.LogFieldOrder, net.DirOrderDesc[dir.Order], "", false),
model.LogFieldTemp(model.LogFieldGuard, dir.Guard, "", false),
model.LogFieldTemp(model.LogFieldOutput, dir.Output, "", false),
},
}
s.sendNetConfLog(c, model.LogTypeDirConf, oper)
return
}
//UpdateDirection .
func (s *Service) UpdateDirection(c context.Context, uid int64, d *net.DirEditParam) (err error, msg string) {
var (
old *net.Direction
canUpdate, checkUnique, orderChanged bool
updates = map[string]interface{}{}
diff = []string{}
)
if old, err = s.gorm.DirectionByID(c, d.ID); err != nil {
log.Error("UpdateDirection s.gorm.DirectionByID(%d) error(%v)", d.ID, err)
return
}
if canUpdate, err = s.beforeUpdate(c, old.NetID); err != nil {
log.Error("UpdateDirection s.beforeUpdate error(%v)", err)
return
}
if !canUpdate {
log.Error("UpdateDirection can't update param(%+v)", d)
return
}
cp := *old
nw := &cp
if d.FlowID != old.FlowID {
if err = s.checkDirectionBindAvailable(c, d.FlowID, 0); err != nil {
return
}
checkUnique = true
nw.FlowID = d.FlowID
updates["flow_id"] = d.FlowID
}
if d.TransitionID != old.TransitionID {
if err = s.checkDirectionBindAvailable(c, 0, d.TransitionID); err != nil {
return
}
checkUnique = true
nw.TransitionID = d.TransitionID
updates["transition_id"] = d.TransitionID
}
if d.Direction != old.Direction {
checkUnique = true
diff = append(diff, model.LogFieldTemp(model.LogFieldDirection, net.DirDirectionDesc[nw.Direction], net.DirDirectionDesc[old.Direction], true))
nw.Direction = d.Direction
updates["direction"] = d.Direction
}
if checkUnique {
if err, msg = s.checkDirectionUnique(c, nw.NetID, nw.FlowID, nw.TransitionID, nw.Direction); err != nil {
return
}
}
if d.Order != old.Order {
diff = append(diff, model.LogFieldTemp(model.LogFieldOrder, net.DirOrderDesc[nw.Order], net.DirOrderDesc[old.Order], true))
nw.Order = d.Order
updates["order"] = d.Order
orderChanged = true
}
//todo-- guard,output
if checkUnique || orderChanged {
if err, msg = s.checkDirConflict(c, nw); err != nil {
return
}
}
if len(updates) <= 0 {
return
}
tx, err := s.gorm.BeginTx(c)
if err != nil {
log.Error("UpdateDirection s.gorm.BeginTx error(%v)", err)
return
}
if err = s.gorm.DisableNet(c, tx, old.NetID); err != nil {
tx.Rollback()
return
}
if err = s.gorm.UpdateFields(c, tx, net.TableDirection, old.ID, updates); err != nil {
tx.Rollback()
return
}
if err = tx.Commit().Error; err != nil {
log.Error("UpdateDirection tx.Commit error(%v)", err)
return
}
s.delDirCache(c, old)
//日志
oper := &model.NetConfOper{
OID: nw.ID,
Action: model.LogNetActionUpdate,
UID: nw.UID,
NetID: nw.NetID,
FlowID: nw.FlowID,
TranID: nw.TransitionID,
Diff: diff,
}
s.sendNetConfLog(c, model.LogTypeDirConf, oper)
return
}
/**
* isDirEnable 检查到下一步的可能性由order&guard决定
*/
func (s *Service) isDirEnable(dir *net.Direction) (enable bool) {
if dir == nil {
return
}
if (dir.Order != net.DirOrderOrSplit && dir.Order != net.DirOrderOrResultSplit) ||
(dir.Order == net.DirOrderOrResultSplit && dir.Guard == "") {
enable = true
return
}
//todo--compute guard expression-v2
return
}
/**
* dispatchDirs 方向线是否可达下一步
* 加入资源维度:若资源没有任何可达性如何办,这是配置错误--后期加入动态检查
*/
func (s *Service) dispatchDirs(dirs []*net.Direction) (enableDir []*net.Direction) {
var (
enable bool
)
//对每个有向线, 过滤所有资源, 区分哪些资源可达
enableDir = []*net.Direction{}
for _, dir := range dirs {
enable = s.isDirEnable(dir)
if enable {
enableDir = append(enableDir, dir)
continue
}
}
return
}
/**
* fetchFlowNextEnableDirs 从flow出发找到下一步变迁
* 后期加入资源维度:若资源没有任何可达性如何办,这是配置错误--后期加入动态检查
* 被应用在:
* 1. 新节点查看下一步变迁是否需要创建分发任务;
* 2. 审核提交后通过flowid找到被触发的是哪个变迁(必须是同一个---后期加入动态检查);
* 3. 通过flowid获取可用变迁的操作项;
* todo -- 任务方存储了transitionid后可以移除2+3逻辑
*/
func (s *Service) fetchFlowNextEnableDirs(c context.Context, flowID int64) (enableDir []*net.Direction, err error) {
var (
list []*net.Direction
)
//找到以flow为起点的所有方向线
if list, err = s.dirByFlow(c, []int64{flowID}, net.DirInput); err != nil {
log.Error("fetchFlowNextEnableDirs s.dirByFlow(%d) error(%v)", flowID, err)
return
}
if len(list) == 0 { //没配置下一步,正常情况
return
}
if enableDir = s.dispatchDirs(list); len(enableDir) == 0 {
err = ecode.AegisFlowNoEnableTran
log.Error("fetchFlowNextEnableDirs s.dispatchDirs flowid(%v) no enable transition", flowID)
}
return
}
/**
* fetchTranNextEnableDirs 从变迁出发找到下一步flow
* 后期加入资源维度
*/
func (s *Service) fetchTranNextEnableDirs(c context.Context, tranID int64) (resultDir *net.Direction, err error) {
var (
list []*net.Direction
enableDir []*net.Direction
)
if list, err = s.dirByTran(c, []int64{tranID}, net.DirOutput, true); err != nil {
log.Error("fetchTranNextEnableDirs s.dirByTran(%d) error(%v)", tranID, err)
return
}
if len(list) == 0 { //变迁后面没配置flow为配置错误--后期都需要添加动态检查
err = ecode.AegisTranNoFlow
log.Error("fetchTranNextEnableDirs s.gorm.DirectionByTransitionID(%d) no flow", tranID)
return
}
enableDir = s.dispatchDirs(list)
if len(enableDir) == 0 { //变迁后面没有任何可用flow为配置错误---后期需添加动态检查
err = ecode.AegisTranNoFlow
log.Error("fetchTranNextEnableDirs transition(%d) has no enable flow", tranID)
}
//todo--允许变迁和节点的一对多对应吗?
resultDir = enableDir[0]
return
}
func (s *Service) beforeUpdate(c context.Context, netID int64) (ok bool, err error) {
var (
fr *net.FlowResource
flowID []int64
)
if flowID, err = s.flowIDByNet(c, netID); err != nil {
return
}
if len(flowID) > 0 {
if fr, err = s.gorm.FRByFlow(c, flowID); err != nil {
log.Error("beforeUpdate s.gorm.FRByFlow error(%v) netid(%d)", err, netID)
return
}
}
ok = fr == nil
return
}

View File

@@ -0,0 +1,114 @@
package service
import (
"context"
"go-common/app/admin/main/aegis/model/net"
"testing"
"encoding/json"
"github.com/smartystreets/goconvey/convey"
"go-common/app/admin/main/aegis/model"
)
func TestServiceShowDirection(t *testing.T) {
var (
c = context.TODO()
id = int64(1)
)
convey.Convey("ShowDirection", t, func(ctx convey.C) {
r, err := s.ShowDirection(c, id)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldNotBeNil)
})
d := r.Direction
t.Logf("%+v", d)
oper := &model.NetConfOper{
OID: d.ID,
Action: model.LogNetActionUpdate,
UID: d.UID,
NetID: d.NetID,
FlowID: d.FlowID,
TranID: d.TransitionID,
Diff: []string{
model.LogFieldTemp(model.LogFieldDirection, net.DirDirectionDesc[d.Direction], "", false),
model.LogFieldTemp(model.LogFieldOrder, net.DirOrderDesc[d.Order], "", false),
model.LogFieldTemp(model.LogFieldGuard, d.Guard, "", false),
model.LogFieldTemp(model.LogFieldOutput, d.Output, "", false),
},
}
i, _ := json.Marshal(oper)
t.Logf("%s", i)
s.sendNetConfLog(c, model.LogTypeDirConf, oper)
//{"diff":"[指向]为[从节点指向变化]","[顺序]为[]","[输出规则]为[]"}
})
}
func TestServiceGetDirectionList(t *testing.T) {
var (
c = context.TODO()
pm = &net.ListDirectionParam{NetID: 1}
)
convey.Convey("GetDirectionList", t, func(ctx convey.C) {
result, err := s.GetDirectionList(c, pm)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
func TestServicecheckDirectionUnique(t *testing.T) {
var (
netID = int64(0)
FlowID = int64(0)
transitionID = int64(0)
direction = int8(0)
)
convey.Convey("checkDirectionUnique", t, func(ctx convey.C) {
err, msg := s.checkDirectionUnique(cntx, netID, FlowID, transitionID, direction)
ctx.Convey("Then err should be nil.msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicecheckDirectionBindAvailable(t *testing.T) {
var (
flowID = int64(0)
transitionID = int64(0)
)
convey.Convey("checkDirectionBindAvailable", t, func(ctx convey.C) {
err := s.checkDirectionBindAvailable(cntx, flowID, transitionID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceisDirEnable(t *testing.T) {
var (
dir = &net.Direction{}
)
convey.Convey("isDirEnable", t, func(ctx convey.C) {
enable := s.isDirEnable(dir)
ctx.Convey("Then err should be nil.enable should not be nil.", func(ctx convey.C) {
ctx.So(enable, convey.ShouldNotBeNil)
})
})
}
func TestServicegetInDirTransitionID(t *testing.T) {
var (
flowID = int64(1)
)
convey.Convey("fetchFlowNextEnableDirs", t, func(ctx convey.C) {
dirs, err := s.fetchFlowNextEnableDirs(cntx, flowID)
ctx.Convey("Then err should be nil.tids,dirList should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(dirs, convey.ShouldNotBeNil)
})
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/resource"
)
func TestServiceListBizFlow(t *testing.T) {
var (
c = context.TODO()
)
convey.Convey("ListBizFlow", t, func(ctx convey.C) {
r, err := s.ListBizFlow(c, 1, []int64{1}, []int64{1, 2})
for _, item := range r {
t.Logf("bizflow(%+v)", item)
}
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
//conf = biz=1, mid="1,2",oid="oid1,oid3", oid="oid6"&extra2="3,4"
var grayTest = []struct {
AddOpt *model.AddOption
Expect bool
}{
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 1,
OID: "oid1",
}},
Expect: true,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 2,
OID: "oid2",
}},
Expect: true,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 3,
OID: "oid3",
}},
Expect: true,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 4,
OID: "oid4",
}},
Expect: false,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 200,
MID: 5,
OID: "oid5",
}},
Expect: true,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 6,
OID: "oid6",
}},
Expect: false,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 7,
OID: "oid6",
Extra2: 5,
}},
Expect: false,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 100,
MID: 7,
OID: "oid6",
Extra2: 3,
}},
Expect: true,
},
{AddOpt: &model.AddOption{Resource: resource.Resource{
BusinessID: 200, //没配置灰度
MID: 7,
OID: "oid6",
Extra2: 10,
}},
Expect: true,
},
}
func TestServiceGray(t *testing.T) {
convey.Convey("Gray", t, func(ctx convey.C) {
optid := 0
for biz, options := range s.gray {
for _, fields := range options {
optid++
t.Logf("gray biz(%d) options.id(%d), options.fields.len=%d, field(%+v)\r\n", biz, optid, len(fields), fields)
}
}
ctx.Convey("No return values", func(ctx convey.C) {
for _, item := range grayTest {
reality := s.Gray(item.AddOpt)
t.Logf("gray opt(%+v) reality(%v) expect(%v)", item.AddOpt, reality, item.Expect)
convey.So(reality, convey.ShouldEqual, item.Expect)
}
})
})
}
func TestService_Auth(t *testing.T) {
convey.Convey("Auth", t, func(ctx convey.C) {
auth, err := s.Auth(cntx, 421)
ctx.Convey("auth ok", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
t.Logf("auth(%+v)", auth)
})
})
}

View File

@@ -0,0 +1,133 @@
package service
import (
"context"
"fmt"
"hash/fnv"
"reflect"
"go-common/app/admin/main/aegis/model/common"
"go-common/library/log"
)
type multransFunc func(context.Context, []int64) (map[int64][]interface{}, error)
/* 批量查询,批量转换
* list []*struct{}
* multrans 转化器根据ID查出其他值
* ID id字段名称id字段类型必须是int64
* Names 查出来的各个字段名称
*/
func (s *Service) mulIDtoName(c context.Context, list interface{}, multrans multransFunc, ID string, Names ...string) (err error) {
var (
lV, itemI, itemIE, idFiled, nameFiled, valueField reflect.Value
id int64
ids []int64
hashIDName = make(map[int64][]interface{})
)
if lV = reflect.ValueOf(list); !lV.IsValid() || lV.IsNil() || lV.Kind() != reflect.Slice {
return fmt.Errorf("invalid list")
}
count := lV.Len()
for i := 0; i < count; i++ {
if itemI = lV.Index(i); !itemI.IsValid() || itemI.IsNil() || itemI.Kind() != reflect.Ptr {
return fmt.Errorf("invalid itemI")
}
if itemIE = itemI.Elem(); !itemIE.IsValid() || itemIE.Kind() != reflect.Struct {
return fmt.Errorf("invalid itemIE")
}
if idFiled = itemIE.FieldByName(ID); !idFiled.IsValid() || idFiled.Kind() != reflect.Int64 {
return fmt.Errorf("invalid idFiled")
}
for _, name := range Names {
if nameFiled = itemIE.FieldByName(name); !nameFiled.IsValid() || !nameFiled.CanSet() {
return fmt.Errorf("invalid nameFiled")
}
}
if id = idFiled.Int(); id != 0 {
if _, ok := hashIDName[id]; !ok {
hashIDName[id] = []interface{}{}
ids = append(ids, id)
}
}
}
if hashIDName, err = multrans(c, ids); err != nil {
log.Error("multrans error(%v)", ids)
return
}
for i := 0; i < count; i++ {
itemIE = lV.Index(i).Elem()
id = itemIE.FieldByName(ID).Int()
if names, ok := hashIDName[id]; ok && len(names) == len(Names) {
for i, name := range names {
nameFiled = itemIE.FieldByName(Names[i])
valueField = reflect.ValueOf(name)
if nameFiled.Kind() != valueField.Kind() {
log.Error("multrans(%s) return %v while need %v", name, valueField.Kind(), nameFiled.Kind())
continue
}
itemIE.FieldByName(Names[i]).Set(reflect.ValueOf(name))
}
}
}
return
}
func (s *Service) getUserGroup(c context.Context, ids []int64) (group map[int64]*common.Group) {
group = make(map[int64]*common.Group)
for _, id := range ids {
group[id] = s.groupCache[id]
}
return
}
func mergeSlice(arr1 []int64, arr2 []int64) (arr []int64) {
for _, id1 := range arr1 {
for _, id2 := range arr2 {
if id1 == id2 {
arr = append(arr, id1)
}
}
}
return
}
func fnvhash32(s string) uint32 {
h := fnv.New32a()
h.Write([]byte(s))
return h.Sum32()
}
func joinstr(maphash []string, sep string, max int) (msgs []string) {
var msg string
for _, v := range maphash {
if len(msg) == 0 {
msg = v
} else if len(msg)+len(v) < max {
msg += sep + v
} else {
msgs = append(msgs, msg)
msg = v
}
}
msgs = append(msgs, msg)
return
}
//stringset 过滤重复字符串
func stringset(arr []string) (res []string) {
mapHash := make(map[uint32]struct{})
for _, item := range arr {
hf := fnvhash32(item)
if _, ok := mapHash[hf]; ok {
continue
}
mapHash[hf] = struct{}{}
res = append(res, item)
}
return
}

View File

@@ -0,0 +1,431 @@
package service
import (
"context"
"fmt"
"sort"
"time"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
/**
* 业务下所有需要分发任务的flow,用net+flow中文名拼接
*/
func (s *Service) dispatchFlow(c context.Context, businessID []int64, limitFlow []int64) (res map[int64]map[int64]string, err error) {
var (
nets []*net.Net
tranID []int64
dirs []*net.Direction
flows []*net.Flow
)
//biz:flow:flow_name
res = map[int64]map[int64]string{}
//业务下所有可用网
if nets, err = s.gorm.NetsByBusiness(c, businessID, false); err != nil {
log.Error("dispatchFlow s.gorm.NetsByBusiness(%v) error(%v)", businessID, err)
return
}
netID := []int64{}
netMap := map[int64]*net.Net{}
for _, item := range nets {
netID = append(netID, item.ID)
netMap[item.ID] = item
}
//网下所有可用变迁
if tranID, err = s.tranIDByNet(c, netID, true, false); err != nil {
log.Error("dispatchFlow s.gorm.TransitionIDByNet(%v) error(%v) businessid(%d)", netID, err, businessID)
return
}
if len(tranID) == 0 {
return
}
//变迁所有可用的被指向的有向线
if dirs, err = s.gorm.DirectionByTransitionID(c, tranID, net.DirInput, false); err != nil {
log.Error("dispatchFlow s.gorm.DirectionByTransitionID error(%v) businessid(%d)", err, businessID)
return
}
limitFlowMap := map[int64]int{}
for _, item := range limitFlow {
limitFlowMap[item] = 1
}
accessFlow := []int64{}
for _, item := range dirs {
if len(limitFlowMap) > 0 && limitFlowMap[item.FlowID] <= 0 {
continue
}
accessFlow = append(accessFlow, item.FlowID)
}
if len(accessFlow) == 0 {
return
}
//拼接每个节点的中文名
if flows, err = s.flows(c, accessFlow, false); err != nil {
log.Error("dispatchFlow s.flows error(%v) businessid(%d)", err, businessID)
return
}
sort.Sort(net.FlowArr(flows))
for _, item := range flows {
if item == nil || netMap[item.NetID] == nil {
continue
}
nt := netMap[item.NetID]
if _, exist := res[nt.BusinessID]; !exist {
res[nt.BusinessID] = map[int64]string{}
}
res[nt.BusinessID][item.ID] = nt.ChName + item.ChName
}
return
}
//ShowFlow .
func (s *Service) ShowFlow(c context.Context, id int64) (r *net.ShowFlowResult, err error) {
var (
f *net.Flow
details map[int64][]*net.TokenBind
n *net.Net
)
if f, err = s.gorm.FlowByID(c, id); err != nil {
return
}
if details, err = s.gorm.TokenBindByElement(c, []int64{id}, []int8{net.BindTypeFlow}, true); err != nil {
return
}
if n, err = s.gorm.NetByID(c, f.NetID); err != nil {
return
}
r = &net.ShowFlowResult{
Flow: f,
Tokens: details[id],
IsStart: n.StartFlowID == id,
}
return
}
//GetFlowList .
func (s *Service) GetFlowList(c context.Context, pm *net.ListNetElementParam) (result *net.ListFlowRes, err error) {
var (
flowID []int64
tks map[int64][]*net.TokenBind
n *net.Net
uid = []int64{}
unames map[int64]string
)
if result, err = s.gorm.FlowList(c, pm); err != nil {
return
}
if len(result.Result) == 0 {
return
}
for _, item := range result.Result {
flowID = append(flowID, item.ID)
uid = append(uid, item.UID)
}
if tks, err = s.gorm.TokenBindByElement(c, flowID, []int8{net.BindTypeFlow}, true); err != nil {
return
}
if n, err = s.gorm.NetByID(c, pm.NetID); err != nil {
return
}
if unames, err = s.http.GetUnames(c, uid); err != nil {
log.Error("GetFlowList s.http.GetUnames error(%v)", err)
err = nil
}
for _, item := range result.Result {
item.IsStart = item.ID == n.StartFlowID
item.Username = unames[item.UID]
for _, bd := range tks[item.ID] {
item.Tokens = append(item.Tokens, bd.ChName)
}
}
return
}
//GetFlowByNet .
func (s *Service) GetFlowByNet(c context.Context, netID int64) (result map[int64]string, err error) {
var (
flows []*net.Flow
)
result = map[int64]string{}
if flows, err = s.gorm.FlowsByNet(c, []int64{netID}); err != nil {
log.Error("GetFlowByNet s.gorm.FlowsByNet(%d) error(%v)", netID, err)
return
}
for _, item := range flows {
result[item.ID] = item.ChName
}
return
}
func (s *Service) checkFlowUnique(c context.Context, netID int64, name string) (err error, msg string) {
var exist *net.Flow
if exist, err = s.gorm.FlowByUnique(c, netID, name); err != nil {
log.Error("checkFlowUnique s.gorm.FlowByUnique(%d,%s) error(%v)", netID, name, err)
return
}
if exist != nil {
err = ecode.AegisUniqueAlreadyExist
msg = fmt.Sprintf(ecode.AegisUniqueAlreadyExist.Message(), "节点", name)
}
return
}
func (s *Service) checkStartFlowBind(oldFlow *net.Flow, tokenIDList []int64) (err error, msg string) {
if oldFlow != nil && !oldFlow.IsAvailable() {
err = ecode.AegisFlowDisabled
msg = fmt.Sprintf("%s,不能作为初始节点", ecode.AegisFlowDisabled.Message())
return
}
//第一版动态审核初始接入状态敏感待审、非敏感待审、高频转发待审非一个确定性值而系统不提供条件判断和guard解析
//配置初始节点没有令牌而业务start时自动传入state支持(后续接入统一初始状态进而条件分状态的逻辑后去掉state字段且加上该判断)
//if len(tokenIDList) == 0 {
// err = ecode.AegisFlowNoToken
// msg = fmt.Sprintf("%s,不能作为初始节点", ecode.AegisFlowNoToken.Message())
//}
return
}
//AddFlow .
func (s *Service) AddFlow(c context.Context, uid int64, f *net.FlowEditParam) (id int64, err error, msg string) {
var (
tx *gorm.DB
diff = []string{}
diffBind string
)
if err, msg = s.checkFlowUnique(c, f.NetID, f.Name); err != nil {
return
}
if f.IsStart {
if err, msg = s.checkStartFlowBind(nil, f.TokenIDList); err != nil {
return
}
diff = append(diff, model.LogFieldTemp(model.LogFieldStartFlow, f.IsStart, false, false))
}
flow := &net.Flow{
NetID: f.NetID,
Name: f.Name,
ChName: f.ChName,
Description: f.Description,
UID: uid,
}
//db update
tx, err = s.gorm.BeginTx(c)
if err != nil {
log.Error("AddFlow s.gorm.BeginTx error(%v)", err)
return
}
if err = s.gorm.AddItem(c, tx, flow); err != nil {
tx.Rollback()
return
}
if diffBind, _, err, msg = s.compareFlowBind(c, tx, flow.ID, f.TokenIDList, false); err != nil {
log.Error("AddFlow s.compareFlowBind error(%v) params(%+v)", err, f)
tx.Rollback()
return
}
if diffBind != "" {
diff = append(diff, diffBind)
}
if f.IsStart {
if err = s.gorm.NetBindStartFlow(c, tx, flow.NetID, flow.ID); err != nil {
tx.Rollback()
return
}
}
if err = tx.Commit().Error; err != nil {
log.Error("AddFlow tx.Commit error(%v)", err)
return
}
id = flow.ID
//日志
diff = append(diff, model.LogFieldTemp(model.LogFieldChName, f.ChName, "", false))
diff = append(diff, model.LogFieldTemp(model.LogFieldName, f.Name, "", false))
oper := &model.NetConfOper{
OID: flow.ID,
Action: model.LogNetActionNew,
UID: flow.UID,
NetID: flow.NetID,
ChName: flow.ChName,
FlowID: flow.ID,
Diff: diff,
}
s.sendNetConfLog(c, model.LogTypeFlowConf, oper)
return
}
// UpdateFlow .
func (s *Service) UpdateFlow(c context.Context, uid int64, f *net.FlowEditParam) (err error, msg string) {
var (
old *net.Flow
n *net.Net
startFlowID int64 = -1
updates = map[string]interface{}{}
tx *gorm.DB
diff = []string{}
diffBind string
changedBind []int64
)
if old, err = s.gorm.FlowByID(c, f.ID); err != nil {
log.Error("UpdateFlow s.gorm.FlowByID(%d) error(%v)", f.ID, err)
return
}
if n, err = s.gorm.NetByID(c, old.NetID); err != nil {
log.Error("UpdateFlow s.gorm.NetByID(%d) error(%v) flowid(%d)", old.NetID, err, f.ID)
return
}
if f.IsStart && n.StartFlowID != f.ID {
startFlowID = f.ID
} else if !f.IsStart && n.StartFlowID == f.ID {
startFlowID = 0
}
if f.IsStart {
if err, msg = s.checkStartFlowBind(old, f.TokenIDList); err != nil {
return
}
diff = append(diff, model.LogFieldTemp(model.LogFieldStartFlow, true, false, true))
}
if f.Name != old.Name {
if err, msg = s.checkFlowUnique(c, old.NetID, f.Name); err != nil {
return
}
diff = append(diff, model.LogFieldTemp(model.LogFieldName, f.Name, old.Name, true))
old.Name = f.Name
updates["name"] = f.Name
}
if f.ChName != old.ChName {
diff = append(diff, model.LogFieldTemp(model.LogFieldChName, f.ChName, old.ChName, true))
old.ChName = f.ChName
updates["ch_name"] = f.ChName
}
if f.Description != old.Description {
old.Description = f.Description
updates["description"] = f.Description
}
//db update
tx, err = s.gorm.BeginTx(c)
if err != nil {
log.Error("UpdateFlow s.gorm.BeginTx error(%v)", err)
return
}
if len(updates) > 0 {
if err = s.gorm.UpdateFields(c, tx, net.TableFlow, old.ID, updates); err != nil {
tx.Rollback()
return
}
}
if startFlowID >= 0 {
if err = s.gorm.NetBindStartFlow(c, tx, n.ID, startFlowID); err != nil {
tx.Rollback()
return
}
}
if diffBind, changedBind, err, msg = s.compareFlowBind(c, tx, f.ID, f.TokenIDList, true); err != nil {
log.Error("updateFlow s.compareFlowBind error(%v) params(%+v)", err, f)
tx.Rollback()
return
}
if diffBind != "" {
diff = append(diff, diffBind)
}
if err = tx.Commit().Error; err != nil {
log.Error("UpdateFlow tx.Commit error(%v)", err)
return
}
s.delFlowCache(c, old, changedBind)
//日志
if len(diff) == 0 {
return
}
oper := &model.NetConfOper{
OID: old.ID,
Action: model.LogNetActionUpdate,
UID: uid,
NetID: old.NetID,
ChName: old.ChName,
FlowID: old.ID,
Diff: diff,
}
s.sendNetConfLog(c, model.LogTypeFlowConf, oper)
return
}
//SwitchFlow .
func (s *Service) SwitchFlow(c context.Context, id int64, needDisable bool) (err error) {
var (
old *net.Flow
n *net.Net
dirs []*net.Direction
action string
)
if old, err = s.gorm.FlowByID(c, id); err != nil {
log.Error("SwitchFlow s.gorm.FlowByID(%d) error(%v) needDisable(%v)", id, err, needDisable)
return
}
log.Info("SwitchFlow id(%d) needdisable(%v) old-flow(%+v)", id, needDisable, old)
available := old.IsAvailable()
if available == !needDisable {
return
}
if needDisable {
if dirs, err = s.gorm.DirectionByFlowID(c, []int64{id}, 0); err != nil {
log.Error("SwitchFlow s.gorm.DirectionByFlowID(%d) error(%v)", id, err)
return
}
if len(dirs) > 0 {
log.Error("SwitchFlow dir by flow(%d) founded", id)
err = ecode.AegisFlowBinded
return
}
if n, err = s.gorm.NetByID(c, old.NetID); err != nil {
log.Error("SwitchFlow s.gorm.NetByID(%d) error(%v) flow(%d)", old.NetID, err, id)
return
}
if n.StartFlowID == id {
log.Error("SwitchFlow net(%d).startflow=flow(%d) founded", n.ID, id)
err = ecode.AegisFlowBinded
return
}
old.DisableTime = time.Now()
action = model.LogNetActionDisable
} else {
old.DisableTime = net.Recovered
action = model.LogNetActionAvailable
}
if err = s.gorm.UpdateFields(c, nil, net.TableFlow, id, map[string]interface{}{"disable_time": old.DisableTime}); err != nil {
return
}
s.delFlowCache(c, old, nil)
//日志
oper := &model.NetConfOper{
OID: old.ID,
Action: action,
UID: old.UID,
NetID: old.NetID,
ChName: old.ChName,
FlowID: old.ID,
}
s.sendNetConfLog(c, model.LogTypeFlowConf, oper)
return
}

View File

@@ -0,0 +1,64 @@
package service
import (
"context"
"github.com/jinzhu/gorm"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/log"
)
func (s *Service) addFlowResources(tx *gorm.DB, netID int64, rids []int64, flowID int64, state int8) (err error) {
fr := &net.FlowResource{
FlowID: flowID,
State: state,
NetID: netID,
}
for _, rid := range rids {
fr.ID = 0
fr.RID = rid
if err = s.gorm.AddItem(context.TODO(), tx, fr); err != nil {
log.Error("addFlowResources s.gorm.AddItem error(%v) fr(%+v)", err, fr)
return
}
}
return
}
/**
* updateFlowResources 正常流转到新节点
* 指定网 & 指定资源 & 新节点
* 不需要指定现状flowid不同资源可能不在同一现状节点上
* 已被取消运行的资源现状,不能被更新
*/
func (s *Service) updateFlowResources(c context.Context, tx *gorm.DB, netID int64, rid int64, newFlowID int64) (err error) {
var (
frs []*net.FlowResource
)
//资源的运行现状
if frs, err = s.gorm.FRByNetRID(c, []int64{netID}, []int64{rid}, false); err != nil {
log.Error("updateFlowResources s.gorm.FRByNetRID error(%v)", err)
return
}
if len(frs) == 0 {
if err = s.addFlowResources(tx, netID, []int64{rid}, newFlowID, net.FRStateRunning); err != nil {
log.Error("updateFlowResources s.addFlowResources error(%v)", err)
}
return
}
//确定state & 记录数目:注意单线->并发和并发->单线的转折点
//todo--只有新节点,如何确定要不要并发拆分或并发合并?--并发要怎么存储(在配置时候确认并发拆分点、并发分支、并发结合点)?
//todo--需要上游transitionid确认上游dir.order吗能出现transition->flow的多对多的情况吗不能
//todo--单独通过flow去确定是在单线上还是并发线上
for _, item := range frs {
if err = s.gorm.UpdateFields(context.TODO(), tx, net.TableFlowResource, item.ID,
map[string]interface{}{"flow_id": newFlowID, "state": net.FRStateRunning}); err != nil {
log.Error("updateFlowResources s.gorm.UpdateFields error(%v)", err)
return
}
}
return
}

View File

@@ -0,0 +1,39 @@
package service
import (
"testing"
"github.com/jinzhu/gorm"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceaddFlowResource(t *testing.T) {
var (
tx = &gorm.DB{}
rid = []int64{}
flowID = int64(0)
state = int8(0)
netid = int64(0)
)
convey.Convey("addFlowResources", t, func(ctx convey.C) {
err := s.addFlowResources(tx, netid, rid, flowID, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicechangeFlowResource(t *testing.T) {
var (
tx, _ = s.gorm.BeginTx(cntx)
netid = int64(0)
rid = int64(0)
newFlowID = int64(0)
)
convey.Convey("changeFlowResource", t, func(ctx convey.C) {
err := s.updateFlowResources(cntx, tx, netid, rid, newFlowID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,67 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/aegis/model/net"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceShowFlow(t *testing.T) {
var (
c = context.TODO()
id = int64(1)
)
convey.Convey("ShowFlow", t, func(ctx convey.C) {
r, err := s.ShowFlow(c, id)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldNotBeNil)
})
})
}
func TestServiceGetFlowList(t *testing.T) {
var (
c = context.TODO()
pm = &net.ListNetElementParam{}
)
convey.Convey("GetFlowList", t, func(ctx convey.C) {
result, err := s.GetFlowList(c, pm)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
func TestServicecheckFlowUnique(t *testing.T) {
var (
netID = int64(0)
name = ""
)
convey.Convey("checkFlowUnique", t, func(ctx convey.C) {
err, msg := s.checkFlowUnique(cntx, netID, name)
ctx.Convey("Then err should be nil.msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicetaskFlowByBusiness(t *testing.T) {
var (
c = context.TODO()
)
convey.Convey("dispatchFlow", t, func(ctx convey.C) {
res, err := s.dispatchFlow(c, []int64{1, 2}, []int64{1, 2})
for biz, item := range res {
t.Logf("biz(%d) flows(%+v)", biz, item)
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,667 @@
package service
import (
"context"
"encoding/json"
"fmt"
"math"
"strconv"
"strings"
"time"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/common"
"go-common/app/admin/main/aegis/model/net"
"go-common/app/admin/main/aegis/model/resource"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/queue/databus/report"
"go-common/library/xstr"
)
// send to log service
func (s *Service) sendAuditLog(c context.Context, action string, opt *model.SubmitOptions, flowres interface{}, logtype int) (err error) {
// send
logData := &report.ManagerInfo{
Uname: opt.Uname,
UID: opt.UID,
Business: model.LogBusinessAudit,
Type: logtype,
Oid: opt.RID,
Action: action,
Ctime: time.Now(),
Index: []interface{}{opt.BusinessID, opt.NewFlowID, opt.TaskID, strconv.Itoa(opt.Result.State)},
Content: map[string]interface{}{
"opt": opt,
"flow": flowres,
},
}
if err = report.Manager(logData); err != nil {
log.Error("report.Manager(%+v) error(%v)", logData, err)
}
return
}
// send to log service
func (s *Service) sendTaskConsumerLog(c context.Context, action string, opt *common.BaseOptions) (err error) {
logData := &report.ManagerInfo{
Uname: opt.Uname,
UID: opt.UID,
Business: model.LogBusinessTask,
Type: model.LogTypeTaskConsumer,
Oid: 0,
Action: action,
Ctime: time.Now(),
Index: []interface{}{opt.BusinessID, opt.FlowID, opt.Role},
}
if err = report.Manager(logData); err != nil {
log.Error("report.Manager(%+v) error(%v)", logData, err)
}
return
}
// send to log service
func (s *Service) sendRscLog(c context.Context, acction string, opt *model.AddOption, res *net.TriggerResult, update interface{}, err error) {
var rid, flowid int64
if res != nil {
flowid = res.NewFlowID
rid = res.RID
}
// send
logData := &report.ManagerInfo{
Uname: "business",
UID: 399,
Business: model.LogBusinessResource,
Type: model.LogTypeFromAdd,
Oid: rid,
Action: acction,
Ctime: time.Now(),
Index: []interface{}{opt.BusinessID, flowid, opt.OID},
Content: map[string]interface{}{
"opt": opt,
"res": res,
"update": update,
"err": err,
},
}
if err1 := report.Manager(logData); err1 != nil {
log.Error("report.Manager(%+v) error(%v)", logData, err1)
}
}
func (s *Service) sendRscCancleLog(c context.Context, BusinessID int64, oids []string, uid int64, username string, err error) {
logData := &report.ManagerInfo{
Uname: username,
UID: uid,
Business: model.LogBusinessResource,
Type: model.LogTypeFromCancle,
Oid: 0,
Action: "cancle",
Ctime: time.Now(),
Index: []interface{}{BusinessID},
Content: map[string]interface{}{
"oids": oids,
"err": err,
},
}
if err1 := report.Manager(logData); err1 != nil {
log.Error("report.Manager(%+v) error(%v)", logData, err1)
}
}
func (s *Service) sendRscSubmitLog(c context.Context, action string, opt *model.SubmitOptions, res interface{}) {
logData := &report.ManagerInfo{
Uname: opt.Uname,
UID: opt.UID,
Business: model.LogBusinessResource,
Type: model.LogTypeFormAuditor,
Oid: opt.RID,
Action: action,
Ctime: time.Now(),
Index: []interface{}{opt.BusinessID, opt.FlowID, opt.OID},
Content: map[string]interface{}{
"opt": opt,
"res": res,
},
}
if err := report.Manager(logData); err != nil {
log.Error("report.Manager(%+v) error(%v)", logData, err)
}
}
/**
* 记录流程流转日志
* oid=rid,action=new_flow_id, index=[net_id, old_flow_id, transition_id, from], content=submit+result
* from=单个提交/批量提交/跳流程/启动/取消
*/
func (s *Service) sendNetTriggerLog(c context.Context, pm *net.TriggerResult) (err error) {
var (
submitValue, resValue []byte
tran string
content = map[string]interface{}{}
)
if len(pm.TransitionID) > 0 {
tran = xstr.JoinInts(pm.TransitionID)
}
if pm.SubmitToken != nil {
if submitValue, err = json.Marshal(pm.SubmitToken); err != nil {
log.Error("sendNetTriggerLog json.Marshal error(%v) submit(%v)", err, pm.SubmitToken)
return
}
content["submit"] = string(submitValue)
}
if pm.ResultToken != nil {
if resValue, err = json.Marshal(pm.ResultToken); err != nil {
log.Error("sendNetTriggerLog json.Marshal error(%v) result(%v)", err, pm.ResultToken)
return
}
content["result"] = string(resValue)
}
data := &report.ManagerInfo{
Business: model.LogBusinessNet,
Type: model.LogTypeNetTrigger,
Oid: pm.RID,
Action: strconv.FormatInt(pm.NewFlowID, 10),
Ctime: time.Now(),
Index: []interface{}{pm.NetID, tran, pm.From, pm.OldFlowID},
Content: content,
}
log.Info("sendNetTriggerLog start send log(%+v)", data)
report.Manager(data)
return
}
/**
* oid: 各元素id, type=level, action=禁用/创建/更新/启用, ctime=time.now, index=[net_id, ch_name, flow_id, tran_id], content=diff
* level in (net/token/token_bind_flow/token_bind_transition/flow/transition/direction)
* diff如下
* token: obj=name+compare+value(type)
* token_bind: obj=flow_chname/tran_chname:从token_obj变成token_obj, ch_name从xx变成xx
* flow: ch_name从xx变成xx, name从xx变成xx
* tran: ch_name从xx变成xx, name从xx变成xx,trigger从xx变成xx,limit从xx变成xx
* dir:direction从xx变成xx,order从xx变成xx,guard从xx变成xx,output从xx变成yy
*
*/
func (s *Service) sendNetConfLog(c context.Context, tp int, oper *model.NetConfOper) (err error) {
data := &report.ManagerInfo{
UID: oper.UID,
Uname: "",
Business: model.LogBusinessNetConf,
Type: tp,
Oid: oper.OID,
Action: oper.Action,
Ctime: time.Now(),
Index: []interface{}{oper.NetID, oper.FlowID, oper.TranID, oper.ChName},
Content: map[string]interface{}{
"diff": strings.Join(oper.Diff, "\r\n"),
},
}
log.Info("sendNetConfLog data(%+v)", data)
report.Manager(data)
return
}
//SearchAuditLogCSV 操作日志结果csv
func (s *Service) SearchAuditLogCSV(c context.Context, pm *model.SearchAuditLogParam) (csv [][]string, err error) {
var (
res []*model.SearchAuditLog
)
if res, _, err = s.SearchAuditLog(c, pm); err != nil {
return
}
csv = make([][]string, len(res)+1)
csv[0] = []string{"rid", "oid", "task id", "状态", "操作时间", "操作人", "其他信息"}
for i, item := range res {
csv[i+1] = []string{
strconv.FormatInt(item.RID, 10),
item.OID,
strconv.FormatInt(item.TaskID, 10),
item.State,
item.Stime,
fmt.Sprintf("%s(%s)", item.Uname, item.Department),
item.Extra,
}
}
return
}
//SearchAuditLog 查询审核日志
func (s *Service) SearchAuditLog(c context.Context, pm *model.SearchAuditLogParam) (res []*model.SearchAuditLog, p common.Pager, err error) {
var (
logs *model.SearchLogResult
ridoid, udepartment map[int64]string
oidrid map[string]int64
)
p = common.Pager{
Ps: pm.Ps,
Pn: pm.Pn,
}
//oid转换成rid查询
if len(pm.OID) > 0 {
if oidrid, err = s.gorm.ResIDByOID(c, pm.BusinessID, pm.OID); err != nil {
log.Error("SearchAuditLog s.gorm.ResIDByOID error(%+v) pm(%+v)", err, pm)
return
}
if len(oidrid) == 0 {
return
}
ridoid = map[int64]string{}
for oid, rid := range oidrid {
pm.RID = append(pm.RID, rid)
ridoid[rid] = oid
}
}
if logs, err = s.searchAuditLog(c, pm); err != nil {
err = ecode.AegisSearchErr
return
}
p.Total = logs.Page.Total
if len(logs.Result) == 0 {
return
}
uids := []int64{}
rids := []int64{}
unameuids := []int64{}
uidunameexist := map[int64]string{}
res = make([]*model.SearchAuditLog, len(logs.Result))
for i, item := range logs.Result {
if item.UID > 0 {
uids = append(uids, item.UID)
}
oid, exist := ridoid[item.OID]
if !exist {
rids = append(rids, item.OID)
}
if item.Uname != "" {
uidunameexist[item.UID] = item.Uname
} else {
item.Uname = uidunameexist[item.UID]
}
if item.Uname == "" && item.UID > 0 {
unameuids = append(unameuids, item.UID)
}
change := &model.Change{}
if err = json.Unmarshal([]byte(item.Extra), &change); err != nil {
log.Error("searchAuditLog json.Unmarshal error(%v) extra(%s) pm(%+v)", err, item.Extra, pm)
return
}
flowaction, submitopt := change.GetSubmitOper()
res[i] = &model.SearchAuditLog{
RID: item.OID,
OID: oid,
TaskID: item.Int2,
State: item.Str0,
Stime: item.Ctime,
UID: item.UID,
Uname: item.Uname,
Department: "",
Extra: fmt.Sprintf("操作详情:[%s]%s %s", item.Action, flowaction, submitopt),
}
}
//由搜索结果提供了rid
if len(rids) > 0 {
if ridoid, err = s.gorm.ResOIDByID(c, rids); err != nil {
return
}
}
unames, _ := s.http.GetUnames(c, unameuids)
udepartment, _ = s.http.GetUdepartment(c, uids)
for _, item := range res {
if item.OID == "" {
item.OID = ridoid[item.RID]
}
if item.Uname == "" && item.UID > 0 {
item.Uname = unames[item.UID]
}
item.Department = udepartment[item.UID]
}
return
}
func (s *Service) trackAuditLog(c context.Context, pm *model.SearchAuditLogParam) (res []*model.TrackAudit, err error) {
var (
logs *model.SearchLogResult
flowch map[int64]string
)
res = []*model.TrackAudit{}
if logs, err = s.searchAuditLog(c, pm); err != nil {
log.Error("trackAuditLog s.searchAuditLog error(%v) pm(%+v)", err, pm)
return
}
res = make([]*model.TrackAudit, len(logs.Result))
flows := []int64{}
for i, item := range logs.Result {
change := &model.Change{}
if err = json.Unmarshal([]byte(item.Extra), change); err != nil {
log.Error("trackAuditLog json.Unmarshal error(%v) pm(%+v)", err, pm)
return
}
res[i] = &model.TrackAudit{
Ctime: item.Ctime,
FlowID: []int64{},
State: "",
Uname: item.Uname,
}
if int(item.Type) == model.LogTypeAuditCancel {
res[i].State = "删除"
}
if change.Flow == nil {
continue
}
if int(item.Type) == model.LogTypeAuditCancel {
var one []int64
one, err = xstr.SplitInts(change.Flow.OldFlowID.String())
if err != nil {
log.Error("trackAuditLog xstr.SplitInts(%s) error(%v)", change.Flow.OldFlowID.String(), err)
err = nil
continue
}
if len(one) == 0 {
continue
}
res[i].FlowID = one
flows = append(flows, one...)
continue
}
flows = append(flows, change.Flow.NewFlowID)
res[i].FlowID = []int64{change.Flow.NewFlowID}
if change.Flow.ResultToken != nil {
res[i].State = change.Flow.ResultToken.ChName
}
}
//get flows names
if len(flows) == 0 {
return
}
if flowch, err = s.gorm.ColumnMapString(c, net.TableFlow, "ch_name", flows, ""); err != nil {
log.Error("trackAuditLog s.gorm.ColumnMapString error(%v) pm(%+v)", err, pm)
return
}
for _, item := range res {
fnames := make([]string, len(item.FlowID))
for i, fid := range item.FlowID {
fnames[i] = flowch[fid]
}
item.FlowName = strings.Join(fnames, ",")
}
return
}
//TrackResource 资源信息追踪, 获取资源add/update日志并分页以此为基准获取对应时间端内的资源audit日志若add/update日志只有不超过1页则获取全部audit日志超过1页最后一页会返回剩余的全部audit日志
func (s *Service) TrackResource(c context.Context, pm *model.TrackParam) (res *model.TrackInfo, p common.Pager, err error) {
var (
obj *resource.Resource
rsc []*model.TrackRsc
audit []*model.TrackAudit
rela [][]int
LogMinTime = "2018-11-01 10:00:00"
)
if obj, err = s.gorm.ResourceByOID(c, pm.OID, pm.BusinessID); err != nil || obj == nil {
log.Error("TrackResource s.gorm.ResourceByOID error(%v)/not found, pm(%+v)", err, pm)
return
}
if rsc, p, err = s.searchResourceLog(c, obj.ID, pm.Pn, pm.Ps); err != nil {
err = ecode.AegisSearchErr
return
}
//超过部分不需要查询audit
topn := int(math.Ceil(float64(p.Total) / float64(p.Ps)))
if (topn > 0 && topn < p.Pn) || (topn <= 0 && p.Pn > 1) {
return
}
//没有资源日志,则不查询审核日志--资源日志添加失败,还是需要展示审核日志啊,审核日志分页有规律
ap := &model.SearchAuditLogParam{
BusinessID: pm.BusinessID,
RID: []int64{obj.ID},
CtimeFrom: LogMinTime,
CtimeTo: "",
Ps: 1000, //一次性拿出来所有的日志
}
//对于audit日志当add日志各种情况下会返回如下:no data(p.Total <= 0)---全量, 1页(p.Total <= p.Ps)---全量, 2或多页(p1=最新->p1.lasttime, p2=p1.lasttime-p2.lasttime,...pn=pn-1.lasttime-mintime)
if p.Total > p.Ps { //有多页
llen := len(rsc)
if llen > 0 && topn > p.Pn {
ap.CtimeFrom = rsc[llen-1].Ctime
}
if p.Pn > 1 {
ap.CtimeTo = pm.LastPageTime
}
}
if audit, err = s.trackAuditLog(c, ap); err != nil {
err = ecode.AegisSearchErr
return
}
//根据ctime聚合以资源日志为基准
llen := len(rsc) + 2
rscctime := make([]string, llen)
rscctime[0] = time.Now().Format("2006-01-02 15:04:05") //max
for i, item := range rsc {
rscctime[i+1] = item.Ctime
}
rscctime[llen-1] = time.Time{}.Format("2006-01-02 15:04:05") //min
index := 0
for i := 1; i < llen; i++ {
rel := []int{}
for ; index < len(audit); index++ {
t := audit[index].Ctime
if t >= rscctime[i] && t < rscctime[i-1] {
rel = append(rel, index)
continue
}
break
}
if i == llen-1 && len(rel) == 0 {
continue
}
rela = append(rela, rel)
}
res = &model.TrackInfo{
Add: rsc,
Audit: audit,
Relation: rela,
}
return
}
func (s *Service) searchAuditLog(c context.Context, pm *model.SearchAuditLogParam) (resp *model.SearchLogResult, err error) {
args := &model.ParamsQueryLog{
Business: model.LogBusinessAudit,
Oid: pm.RID,
CtimeFrom: pm.CtimeFrom,
CtimeTo: pm.CtimeTo,
Int2: pm.TaskID,
Uname: pm.Username,
}
if pm.State != "" {
args.Str0 = []string{pm.State}
}
if pm.BusinessID > 0 {
args.Int0 = []int64{pm.BusinessID}
}
escm := model.EsCommon{
Ps: pm.Ps,
Pn: pm.Pn,
Order: "ctime",
Sort: "desc",
}
return s.http.QueryLogSearch(c, args, escm)
}
func (s *Service) auditLogByRID(c context.Context, rid int64) (ls []string, err error) {
resp, err := s.searchAuditLog(c, &model.SearchAuditLogParam{
RID: []int64{rid},
Ps: 1000,
Pn: 1})
if err != nil || resp == nil {
return
}
for _, result := range resp.Result {
change := &model.Change{}
if err = json.Unmarshal([]byte(result.Extra), &change); err != nil {
log.Error("json.Unmarshal error(%v)", err)
return
}
flowaction, submitopt := change.GetSubmitOper()
// 时间 + 操作人 + 操作/state + 操作内容
l := fmt.Sprintf("%s %s[%s] %s %s", result.Ctime, result.Uname, result.Action, flowaction, submitopt)
ls = append(ls, l)
}
return
}
func (s *Service) searchWeightLog(c context.Context, taskid int64, pn, ps int) (ls []*model.WeightLog, count int, err error) {
args := &model.ParamsQueryLog{
Business: model.LogBusinessTask,
Type: model.LogTYpeTaskWeight,
Oid: []int64{taskid},
Action: []string{"weight"},
}
escm := model.EsCommon{
Pn: pn,
Ps: ps,
Order: "ctime",
Sort: "desc",
}
resp, err := s.http.QueryLogSearch(c, args, escm)
if err != nil || resp == nil {
return
}
count = resp.Page.Total
for _, result := range resp.Result {
logitem := make(map[string]*model.WeightLog)
if err = json.Unmarshal([]byte(result.Extra), &logitem); err != nil {
log.Error("json.Unmarshal error(%v)", err)
return
}
ls = append(ls, logitem["weightlog"])
}
return
}
func (s *Service) searchConsumerLog(c context.Context, bizid, flowid int64, action []string, uids []int64, ps int) (at map[int64]string, err error) {
args := &model.ParamsQueryLog{
Business: model.LogBusinessTask,
Type: model.LogTypeTaskConsumer,
Action: action,
UID: uids,
CtimeFrom: time.Now().Add(-24 * time.Hour * 7).Format("2006-01-02 15:04:05"),
}
if bizid > 0 {
args.Int0 = []int64{bizid}
}
if flowid > 0 {
args.Int1 = []int64{flowid}
}
escm := model.EsCommon{
Order: "ctime",
Sort: "desc",
Pn: 1,
Ps: ps,
Group: "uid",
}
resp, err := s.http.QueryLogSearch(c, args, escm)
if err != nil || resp == nil {
return
}
at = make(map[int64]string)
for _, item := range resp.Result {
if ct, ok := at[item.UID]; ok {
if item.Ctime > ct {
at[item.UID] = item.Ctime
}
} else {
at[item.UID] = item.Ctime
}
}
return
}
func (s *Service) searchResourceLog(c context.Context, rid int64, pn, ps int) (result []*model.TrackRsc, p common.Pager, err error) {
//根据ctime降序排列
args := &model.ParamsQueryLog{
Business: model.LogBusinessResource,
Type: model.LogTypeFromAdd,
Oid: []int64{rid},
}
p = common.Pager{
Pn: pn,
Ps: ps,
}
escm := model.EsCommon{
Order: "ctime",
Sort: "desc",
Pn: pn,
Ps: ps,
}
resp, err := s.http.QueryLogSearch(c, args, escm)
if err != nil || resp == nil {
return
}
p.Total = resp.Page.Total
result = make([]*model.TrackRsc, len(resp.Result))
for i, item := range resp.Result {
extra := struct {
Opt map[string]interface{} `json:"opt"`
}{}
if err = json.Unmarshal([]byte(item.Extra), &extra); err != nil {
log.Error("ResourceLog json.Unmarshal error(%v) extra(%s)", err, item.Extra)
return
}
result[i] = &model.TrackRsc{
Ctime: item.Ctime,
Content: extra.Opt["content"].(string),
Detail: extra.Opt,
}
}
//content变化由于result是根据创建时间降序排列的以result的最后一个为基础 向result[0]判断
content := ""
for i := len(result) - 1; i >= 0; i-- {
item := result[i]
//固定content字段比较变化
if item.Content != content {
content = item.Content
continue
}
item.Content = ""
}
return
}

View File

@@ -0,0 +1,159 @@
package service
import (
"context"
"encoding/json"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/common"
"go-common/app/admin/main/aegis/model/net"
"go-common/app/admin/main/aegis/model/resource"
"go-common/app/admin/main/aegis/model/task"
)
func Test_AuditLog(t *testing.T) {
opt := &model.SubmitOptions{
Binds: []int64{1, 2},
EngineOption: model.EngineOption{
BaseOptions: common.BaseOptions{
RID: 1,
},
Result: &resource.Result{},
},
}
res := &net.TriggerResult{
SubmitToken: &net.TokenPackage{
Values: map[string]interface{}{
"state": 1,
"forbid": 2,
},
TokenIDList: []int64{3, 4},
},
ResultToken: &net.TokenPackage{
Values: map[string]interface{}{
"state": 1,
"forbid": 2,
},
TokenIDList: []int64{3, 4},
},
}
err := s.sendAuditLog(context.TODO(), "submit", opt, res, model.LogTypeAuditSubmit)
fmt.Printf("err(%v)\n", err)
t.Fail()
}
func Test_TaskLog(t *testing.T) {
Content := map[string]interface{}{
"task": &task.Task{},
}
bs, _ := json.Marshal(Content)
fmt.Printf("bs(%s)", string(bs))
t.Fail()
}
func Test_ResourceLog(t *testing.T) {
Content := map[string]interface{}{
"opt": &model.AddOption{},
"res": &net.TriggerResult{
SubmitToken: &net.TokenPackage{
Values: map[string]interface{}{
"state": 1,
"forbid": 2,
},
TokenIDList: []int64{3, 4},
},
ResultToken: &net.TokenPackage{
Values: map[string]interface{}{
"state": 1,
"forbid": 2,
},
TokenIDList: []int64{3, 4},
},
},
"oids": []int64{1, 2, 3},
"err": "dqfgug",
}
bs, _ := json.Marshal(Content)
fmt.Printf("bs(%s)", string(bs))
t.Fail()
}
func TestService_SearchAuditLog1(t *testing.T) {
convey.Convey("SearchAuditLog", t, func(ctx convey.C) {
pm := &model.SearchAuditLogParam{
OID: []string{"196962673299031503"},
BusinessID: 1,
Ps: 20,
Pn: 1,
Username: []string{"业务方"},
CtimeFrom: "2018-11-01 00:00:00",
CtimeTo: "",
TaskID: []int64{0, 660},
State: "0",
}
data, pager, err := s.SearchAuditLog(cntx, pm)
t.Logf("pager(%+v)", pager)
for i, item := range data {
t.Logf("data i=%d, %+v", i, item)
}
ctx.So(err, convey.ShouldBeNil)
})
}
func TestService_SearchAuditLogCSV(t *testing.T) {
convey.Convey("SearchAuditLogCSV", t, func(ctx convey.C) {
pm := &model.SearchAuditLogParam{
OID: []string{"196962673299031503"},
BusinessID: 1,
Ps: 20,
Pn: 1,
//Username: "chenxuefeng",
CtimeFrom: "2018-11-01 00:00:00",
CtimeTo: "",
//TaskID: []int64{0, 55},
//State: "1",
}
data, err := s.SearchAuditLogCSV(cntx, pm)
for i, item := range data {
t.Logf("data i=%d, %+v", i, item)
}
ctx.So(err, convey.ShouldBeNil)
})
}
func TestService_TrackResource(t *testing.T) {
convey.Convey("TrackResource", t, func(ctx convey.C) {
pm := &model.TrackParam{
OID: "186464454672655532112",
BusinessID: 1,
Pn: 2,
Ps: 2,
LastPageTime: "2018-12-06 14:01:15",
}
data, p, err := s.TrackResource(cntx, pm)
ctx.So(err, convey.ShouldBeNil)
t.Logf("data(%+v) pager(%+v), params(%+v)", data, p, pm)
for i, item := range data.Add {
t.Logf("data.add i=%d, %+v", i, item)
}
for i, item := range data.Audit {
t.Logf("data.audit i=%d, %+v", i, item)
}
})
}
func TestService_searchConsumerLog(t *testing.T) {
convey.Convey("searchConsumerLog", t, func(ctx convey.C) {
res, err := s.searchConsumerLog(cntx, 1, 1, []string{"on"}, []int64{1148}, 10)
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
t.Logf("res(%+v)", res)
})
}

View File

@@ -0,0 +1,30 @@
package service
import (
"context"
"strconv"
"go-common/app/admin/main/aegis/model/databus"
"go-common/library/log"
)
func (s *Service) sendCreateTaskMsg(c context.Context, rid, flowID, dispatchLimit, bizid int64) (err error) {
msg := &databus.CreateTaskMsg{
BizID: bizid,
RID: rid,
FlowID: flowID,
DispatchLimit: dispatchLimit,
}
return s.async.Do(c, func(c context.Context) {
log.Info("start to send msg(%+v)", msg)
for retry := 0; retry < 3; retry++ {
if err = s.aegisPub.Send(c, strconv.Itoa(int(msg.RID)), msg); err == nil {
break
}
}
if err != nil {
log.Error("s.aegisPub.Send error(%v) msg(%+v) ", err, msg)
}
})
}

View File

@@ -0,0 +1,24 @@
package service
import (
"testing"
"context"
"github.com/smartystreets/goconvey/convey"
)
func TestServicesendCreateTaskMsg(t *testing.T) {
var (
rid = int64(1)
flowID = int64(1)
dispatchLimit = int64(1)
bizid = int64(1)
)
convey.Convey("sendCreateTaskMsg", t, func(ctx convey.C) {
err := s.sendCreateTaskMsg(context.TODO(), rid, flowID, dispatchLimit, bizid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,106 @@
package service
import (
"context"
"errors"
"go-common/app/admin/main/aegis/model/monitor"
"go-common/library/log"
"time"
)
// MonitorBuzResult 获取业务的监控结果
func (s *Service) MonitorBuzResult(c context.Context, bid int64) (res []*monitor.RuleResultData, err error) {
var (
rules []*monitor.Rule
uids []int64
uNames map[int64]string
statsMap map[int64]*monitor.Stats
min, max int64
)
statsMap = make(map[int64]*monitor.Stats)
if rules, err = s.mysql.MoniBizRules(c, bid); err != nil {
log.Error("s.MonitorResult(%d) error:%v", bid, err)
return
}
for _, rule := range rules {
uids = append(uids, rule.UID)
if min, max, err = s.monitorNotifyTime(rule.RuleConf); err != nil {
log.Error("s.MonitorBuzResult(%d) s.monitorNotifyTime(%+v) error:%v", bid, rule.RuleConf, err)
continue
}
if statsMap[rule.ID], err = s.redis.MoniRuleStats(c, rule.ID, min, max); err != nil {
log.Error("s.redis.MoniRuleStats(%d,%+v) error:%v", rule.ID, rule.RuleConf, err)
err = nil
statsMap[rule.ID] = &monitor.Stats{}
}
}
if uNames, err = s.http.GetUnames(c, uids); err != nil {
log.Error("s.MonitorResult(%d) s.http.ManagerUNames(%v) error:%v", bid, uids, err)
err = nil
}
for _, rule := range rules {
var (
uName string
stats *monitor.Stats
)
if _, ok := uNames[rule.UID]; ok {
uName = uNames[rule.UID]
}
if _, ok := statsMap[rule.ID]; ok {
stats = statsMap[rule.ID]
}
data := &monitor.RuleResultData{
Rule: rule,
User: &monitor.User{
ID: rule.UID,
UserName: uName,
NickName: uName,
},
Stats: stats,
}
res = append(res, data)
}
return
}
// MonitorResultOids 获取
func (s *Service) MonitorResultOids(c context.Context, rid int64) (res map[int64]int, err error) {
var (
min, max int64
rule *monitor.Rule
)
if rule, err = s.mysql.MoniRule(c, rid); err != nil {
log.Error("s.MonitorResultOids(%d) error:%v", rid, err)
return
}
if min, max, err = s.monitorNotifyTime(rule.RuleConf); err != nil {
log.Error("s.MonitorResultOids(%d) s.monitorNotifyTime() error:%v", rid, err)
return
}
return s.redis.MoniRuleOids(c, rid, min, max)
}
// monitorNotifyTime 计算监控报警的score区间
func (s *Service) monitorNotifyTime(conf *monitor.RuleConf) (tFrom, tTo int64, err error) {
now := time.Now().Unix()
if _, ok := conf.NotifyCdt["time"]; !ok {
err = errors.New("配置的 NotifyCdt 中不存在 time")
return
}
timeCdt := conf.NotifyCdt["time"].Value
compCdt := conf.NotifyCdt["time"].Comp
switch compCdt {
case monitor.CompGT:
tFrom = 0
tTo = now - timeCdt
case monitor.CompLT:
tFrom = now - timeCdt
tTo = now
default:
err = errors.New("配置的 NotifyCdt 中 comparison 不合法: " + compCdt)
return
}
return
}

View File

@@ -0,0 +1,62 @@
package service
import (
"context"
"go-common/app/admin/main/aegis/model/monitor"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceMonitorBuzResult(t *testing.T) {
convey.Convey("MonitorBuzResult", t, func(convCtx convey.C) {
var (
c = context.Background()
bid = int64(2)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
_, err := s.MonitorBuzResult(c, bid)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestServiceMonitorResultOids(t *testing.T) {
convey.Convey("MonitorBuzResult", t, func(convCtx convey.C) {
var (
c = context.Background()
id = int64(1)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
_, err := s.MonitorResultOids(c, id)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestServiceMonitorNotifyTime(t *testing.T) {
convey.Convey("MonitorNotifyTime", t, func(convCtx convey.C) {
var (
conf = &monitor.RuleConf{
NotifyCdt: map[string]struct {
Comp string `json:"comparison"`
Value int64 `json:"value"`
}{
"time": {
Comp: ">",
Value: 1,
},
},
}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
_, _, err := s.monitorNotifyTime(conf)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,273 @@
package service
import (
"context"
"fmt"
"time"
"github.com/jinzhu/gorm"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
)
//GetNetList .
func (s *Service) GetNetList(c context.Context, pm *net.ListNetParam) (result *net.ListNetRes, err error) {
var (
unames map[int64]string
)
if result, err = s.gorm.NetList(c, pm); err != nil {
return
}
if len(result.Result) == 0 {
return
}
//get username
uid := []int64{}
for _, item := range result.Result {
if item.UID > 0 {
uid = append(uid, item.UID)
}
}
if len(uid) == 0 {
return
}
if unames, err = s.http.GetUnames(c, uid); err != nil || len(unames) == 0 {
log.Error("GetNetList s.http.GetUnames error(%v) or empty uid(%+v)", err, uid)
err = nil
}
for _, item := range result.Result {
item.Username = unames[item.UID]
}
return
}
//GetNetByBusiness .
func (s *Service) GetNetByBusiness(c context.Context, businessID int64) (res map[int64]string, err error) {
var (
list []*net.Net
)
res = map[int64]string{}
if list, err = s.gorm.NetsByBusiness(c, []int64{businessID}, true); err != nil {
log.Error("GetNetByBusiness s.gorm.NetsByBusiness(%d) error(%v)", businessID, err)
return
}
for _, item := range list {
res[item.ID] = item.ChName
}
return
}
//ShowNet .
func (s *Service) ShowNet(c context.Context, id int64) (r *net.Net, err error) {
if r, err = s.gorm.NetByID(c, id); err != nil {
log.Error("ShowNet s.gorm.NetByID(%d) error(%v)", id, err)
}
return
}
func (s *Service) netCheckUnique(c context.Context, chName string) (err error, msg string) {
var exist *net.Net
if exist, err = s.gorm.NetByUnique(c, chName); err != nil && err != gorm.ErrRecordNotFound {
log.Error("netCheckUnique s.gorm.NetByID(%s) error(%v)", chName, err)
return
}
if exist != nil {
err = ecode.AegisUniqueAlreadyExist
msg = fmt.Sprintf(ecode.AegisUniqueAlreadyExist.Message(), "网", chName)
}
return
}
//AddNet .
func (s *Service) AddNet(c context.Context, n *net.Net) (id int64, err error, msg string) {
if err, msg = s.netCheckUnique(c, n.ChName); err != nil {
return
}
//网初建为禁用状态
n.DisableTime = time.Now()
if err = s.gorm.AddItem(c, nil, n); err != nil {
return
}
id = n.ID
//日志
oper := &model.NetConfOper{
OID: n.ID,
Action: model.LogNetActionNew,
UID: n.UID,
NetID: n.ID,
ChName: n.ChName,
FlowID: n.StartFlowID,
Diff: []string{model.LogFieldTemp(model.LogFieldPID, n.PID, 0, false)},
}
s.sendNetConfLog(c, model.LogTypeNetConf, oper)
return
}
//UpdateNet .
func (s *Service) UpdateNet(c context.Context, uid int64, n *net.NetEditParam) (err error, msg string) {
var (
old *net.Net
updates = map[string]interface{}{}
diff = []string{}
)
if old, err = s.gorm.NetByID(c, n.ID); err != nil {
log.Error("UpdateNet s.gorm.NetByID(%d) error(%v)", n.ID, err)
return
}
if n.ChName != old.ChName {
if err, msg = s.netCheckUnique(c, n.ChName); err != nil {
return
}
diff = append(diff, model.LogFieldTemp(model.LogFieldChName, n.ChName, old.ChName, true))
old.ChName = n.ChName
updates["ch_name"] = n.ChName
}
if n.Description != old.Description {
old.Description = n.Description
updates["description"] = n.Description
}
if err = s.gorm.UpdateFields(c, nil, net.TableNet, old.ID, updates); err != nil {
return
}
s.delNetCache(c, old)
//日志
if len(diff) == 0 {
return
}
oper := &model.NetConfOper{
OID: old.ID,
Action: model.LogNetActionUpdate,
UID: uid,
NetID: old.ID,
ChName: old.ChName,
FlowID: old.StartFlowID,
Diff: diff,
}
s.sendNetConfLog(c, model.LogTypeNetConf, oper)
return
}
//SwitchNet .
func (s *Service) SwitchNet(c context.Context, id int64, needDisable bool) (err error) {
var (
n *net.Net
action string
valid bool
)
if n, err = s.gorm.NetByID(c, id); err != nil {
log.Error("SwitchNet s.gorm.NetByID(%d) error(%v) needDisable(%v)", id, err, needDisable)
return
}
available := n.IsAvailable()
if available == !needDisable {
return
}
if needDisable {
n.DisableTime = time.Now()
action = model.LogNetActionDisable
} else {
if valid, err = s.checkNetValid(c, id); err != nil {
log.Error("SwitchNet s.checkNetValid error(%v) id(%d)", err, id)
return
}
if !valid {
log.Error("SwitchNet id(%d) isn't valid, can't be available", id)
err = ecode.AegisNetErr
return
}
n.DisableTime = net.Recovered
action = model.LogNetActionAvailable
}
if err = s.gorm.UpdateFields(c, nil, net.TableNet, id, map[string]interface{}{"disable_time": n.DisableTime}); err != nil {
return
}
s.delNetCache(c, n)
//日志
oper := &model.NetConfOper{
OID: n.ID,
Action: action,
UID: n.UID,
NetID: n.ID,
ChName: n.ChName,
FlowID: n.StartFlowID,
}
s.sendNetConfLog(c, model.LogTypeNetConf, oper)
return
}
//初步检查流程网的可用性:流转完整性
func (s *Service) checkNetValid(c context.Context, netID int64) (valid bool, err error) {
var (
n *net.Net
flow *net.Flow
dirs []*net.Direction
)
if n, err = s.netByID(c, netID); err != nil || n == nil || n.StartFlowID <= 0 {
log.Error("checkNetValid s.netByID(%d) error(%v)/not found/start_flow_id=0, net(%+v)", netID, err, n)
return
}
if flow, err = s.flowByID(c, n.StartFlowID); err != nil || flow == nil || !flow.IsAvailable() {
log.Error("checkNetValid s.flowByID(%d) error(%v)/flow not found/disabled, netid(%d), flow(%+v)", n.StartFlowID, err, netID, flow)
return
}
if dirs, err = s.gorm.DirectionByNet(c, netID); err != nil {
log.Error("checkNetValid s.gorm.DirectionByNet(%d) error(%v)", netID, err)
return
}
tranPrevMap := map[int64][]int64{}
tranNextMap := map[int64][]int64{}
flowPrevMap := map[int64][]int64{}
for _, item := range dirs {
if item.Direction == net.DirInput {
tranPrevMap[item.TransitionID] = append(tranPrevMap[item.TransitionID], item.FlowID)
continue
}
flowPrevMap[item.FlowID] = append(flowPrevMap[item.FlowID], item.TransitionID)
tranNextMap[item.TransitionID] = append(tranNextMap[item.TransitionID], item.FlowID)
}
/**
flow next empty/trans---dir=input
flow prev empty(start)/trans---dir=output
tran next flows---dir=output
tran prev flows---dir=input
*/
for flowID, trans := range flowPrevMap {
if len(trans) == 0 && flowID != n.StartFlowID {
log.Error("checkNetValid flow(%d) no previous transition", flowID)
return
}
}
for tranID, flows := range tranPrevMap {
prv := len(flows)
nxt := len(tranNextMap[tranID])
if prv == 0 || nxt == 0 {
log.Error("checkNetValid transition(%d) no prev(%d)/next(%d) flow", tranID, prv, nxt)
return
}
}
for tranID, flows := range tranNextMap {
prv := len(tranPrevMap[tranID])
nxt := len(flows)
if prv == 0 || nxt == 0 {
log.Error("checkNetValid transition(%d) no prev(%d)/next(%d) flow", tranID, prv, nxt)
return
}
}
valid = true
return
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,899 @@
package service
/**
* 需要缓存的对象
******************************************
* one2many
* key type val
* flow int64+avail []*tokenbind,err---ok
* flow int64+dir []*dir,err
* netid int64 []*flow,err --- ok
* netid []int64,avail,disp []int64,err--transitionid
* tranid []int64,isbatch []*tokenbind,err---ok
* tranid int64,avail []*dir.err
* biz int64 []int64,err---netid ---ok
*
* one2one
* ids []int64,xxx,yy []*struct,err --- net/bind/flow/=ok
******************************************
* testing:
* 1. get objs by ids
* 2. get objs by obj.aggregate_field
* 3. get obj ids by obj.aggregate_field
*/
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"testing"
"github.com/smartystreets/goconvey/convey"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/database/orm"
)
var (
testaggrmap = map[int64]struct {
Rela []int64
JSON string
}{
1: {Rela: []int64{1, 2}, JSON: "1,2"},
2: {Rela: []int64{3, 4}, JSON: "3,4"},
}
testaggrv = []int64{1, 2}
testaggrcache = map[string]string{
"test_aggr:1": "1,2",
"test_aggr:2": "3,4",
}
testaggrdest = &AggrCacheDest{
GetKey: func(id int64, opt ...interface{}) (k []string) {
k = []string{fmt.Sprintf("test_aggr:%d", id)}
return
},
AggrRelaRaw: func(c context.Context, miss []int64, kopt []interface{}, opt ...interface{}) (rela map[int64][]int64, err error) {
rela = map[int64][]int64{}
for _, i := range miss {
rela[i] = testaggrmap[i].Rela
}
return
},
}
testflows = []*net.Flow{
{ID: 50, Name: "init", NetID: 99},
{ID: 51, Name: "first", NetID: 99},
{ID: 52, Name: "init", NetID: 100},
}
testnets = []*net.Net{
{ID: 8, ChName: "测试网1", BusinessID: 10},
{ID: 9, ChName: "测试网1", BusinessID: 10},
{ID: 10, ChName: "测试网1", BusinessID: 11},
}
testtokens = []*net.Token{
{ID: 50, NetID: 10, Name: "test1", Compare: 0, Value: "0"},
{ID: 51, NetID: 10, Name: "test2", Compare: 0, Value: "0"},
{ID: 52, NetID: 11, Name: "test2", Compare: 0, Value: "0"},
}
testbinds = []*net.TokenBind{
{ID: 50, Type: 1, ElementID: 10, TokenID: "8"},
{ID: 51, Type: 1, ElementID: 10, TokenID: "9"},
{ID: 52, Type: 2, ElementID: 11, TokenID: "8"},
}
testtrans = []*net.Transition{
{ID: 50, NetID: 10, Trigger: 1, Limit: 1, Name: "test1"},
{ID: 51, NetID: 10, Trigger: 1, Limit: 1, Name: "test2"},
{ID: 52, NetID: 11, Trigger: 1, Limit: 1, Name: "test3"},
}
testdirs = []*net.Direction{
{ID: 50, NetID: 10, FlowID: 10, TransitionID: 20, Direction: 1},
{ID: 51, NetID: 10, FlowID: 11, TransitionID: 20, Direction: 2},
{ID: 52, NetID: 10, FlowID: 11, TransitionID: 21, Direction: 1},
{ID: 53, NetID: 10, FlowID: 12, TransitionID: 21, Direction: 2},
}
testdata = map[string]struct {
RowObj interface{}
RowObjLen int
RowRela map[int64][]int64
Caches map[string]string
AggrID, ObjID []int64
}{
"flow": {
RowObj: testflows,
RowObjLen: 3,
RowRela: map[int64][]int64{99: {50, 51}, 100: {52}},
Caches: map[string]string{
"net_flow:99": "50,51",
"net_flow:100": "52",
"flow:50": getjsonobj(testflows[0]),
"flow:51": getjsonobj(testflows[1]),
"flow:52": getjsonobj(testflows[2]),
},
AggrID: []int64{99, 100, 101},
ObjID: []int64{50, 51, 52},
},
"net": {
RowObj: testnets,
RowObjLen: 3,
RowRela: map[int64][]int64{10: {8, 9}, 11: {10}},
Caches: map[string]string{
"business_net:10": "8,9",
"business_net:11": "10",
"net:8": getjsonobj(testnets[0]),
"net:9": getjsonobj(testnets[1]),
"net:10": getjsonobj(testnets[2]),
},
AggrID: []int64{10, 11, 1200},
ObjID: []int64{8, 9, 10},
},
"token": {
RowObj: testtokens,
RowObjLen: 3,
Caches: map[string]string{
"token:50": getjsonobj(testtokens[0]),
"token:51": getjsonobj(testtokens[1]),
"token:52": getjsonobj(testtokens[2]),
},
ObjID: []int64{50, 51, 52},
},
"bind": {
RowObj: testbinds,
RowObjLen: 3,
RowRela: map[int64][]int64{10: {50, 51}, 11: {52}},
Caches: map[string]string{
"flow_bind:10": "50,51",
"transition_bind:11": "52",
"bind:50": getjsonobj(testbinds[0]),
"bind:51": getjsonobj(testbinds[1]),
"bind:52": getjsonobj(testbinds[2]),
},
AggrID: []int64{10, 11, 1200},
ObjID: []int64{50, 51, 52},
},
"tran": {
RowObj: testtrans,
RowObjLen: 3,
RowRela: map[int64][]int64{10: {50, 51}, 11: {52}},
Caches: map[string]string{
"net_transition:10": "50,51",
"net_transition:11": "52",
"transition:50": getjsonobj(testtrans[0]),
"transition:51": getjsonobj(testtrans[1]),
"transition:52": getjsonobj(testtrans[2]),
},
AggrID: []int64{10, 11, 12000},
ObjID: []int64{50, 51, 52},
},
}
testdata2 = map[string]struct {
RowObj interface{}
RowObjLen int
RowRela map[int64]map[int8][]int64
Caches map[string]string
ObjID []int64
AggrID []int64
}{
"dir": {
RowObj: testdirs,
RowObjLen: 4,
RowRela: map[int64]map[int8][]int64{
10: {1: {50}},
11: {1: {52}, 2: {51}},
12: {2: {53}},
20: {1: {50}, 2: {51}},
21: {1: {52}, 2: {53}},
},
Caches: map[string]string{
"flow_direction_1:10": "50",
"flow_direction_1:11": "52",
"flow_direction_2:11": "51",
"flow_direction_2:12": "53",
"transition_direction_1:20": "50",
"transition_direction_2:20": "51",
"transition_direction_1:21": "52",
"transition_direction_2:21": "53",
"direction:50": getjsonobj(testdirs[0]),
"direction:51": getjsonobj(testdirs[1]),
"direction:52": getjsonobj(testdirs[2]),
"direction:53": getjsonobj(testdirs[3]),
},
AggrID: []int64{10, 11, 12, 20, 21, 12000},
ObjID: []int64{50, 51, 52, 53},
},
}
getjsonobj = func(v interface{}) string {
bs, _ := json.Marshal(v)
return string(bs)
}
)
func TestServiceAppendString(t *testing.T) {
convey.Convey("AppendString", t, func(ctx convey.C) {
testaggrdest.Dest = map[int64][]int64{}
ctx.Convey("err=nil, ok=true, aggrdest expected", func(ctx convey.C) {
for id, item := range testaggrmap {
ok, err := testaggrdest.AppendString(id, item.JSON)
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(fmt.Sprintf("%v", testaggrdest.Dest[id]), convey.ShouldEqual, fmt.Sprintf("%v", item.Rela))
}
})
})
}
func TestServiceAppendRelationRaw(t *testing.T) {
convey.Convey("AppendRelationRaw", t, func(ctx convey.C) {
missCache, err := testaggrdest.AppendRelationRaw(cntx, testaggrv, nil)
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missCache, convey.ShouldNotBeNil)
for k, v := range missCache {
ctx.So(v, convey.ShouldEqual, testaggrcache[k])
}
})
})
}
func testdataprepare(k string, v interface{}) {
testdao := orm.NewMySQL(s.c.ORM)
objs := reflect.ValueOf(v)
for i := 0; i < objs.Len(); i++ {
testdao.Save(objs.Index(i).Interface())
}
}
func TestServiceAppendAggregateRaw(t *testing.T) {
convey.Convey("AppendAggregateRaw", t, func(ctx convey.C) {
objdest := &flowArr{}
w := &CacheWrap{
ObjDest: objdest,
AggrObjRaw: s.netFlowAppendRaw,
AggregateDest: &AggrCacheDest{
GetKey: s.netFlowKey,
AggrRelaRaw: s.netFlowRelation,
},
}
k := "flow"
testdataprepare(k, testdata[k].RowObj)
missCache, err := w.AppendAggregateRaw(cntx, testdata[k].AggrID, nil)
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missCache, convey.ShouldNotBeNil)
//对象存在 & 聚合关系存在 & 待缓存值存在
ctx.So(len(objdest.dest), convey.ShouldEqual, testdata[k].RowObjLen)
for ck, v := range testdata[k].RowRela {
ctx.So(fmt.Sprintf("%v", w.AggregateDest.Dest[ck]), convey.ShouldEqual, fmt.Sprintf("%v", v))
}
for ck, v := range testdata[k].Caches {
pos := strings.Index(missCache[ck], "ctime") //json化的
if pos == -1 {
ctx.So(missCache[ck], convey.ShouldEqual, v)
} else {
ctx.So(missCache[ck][:pos], convey.ShouldEqual, v[:pos])
}
}
})
})
}
func TestServiceGetFromCache(t *testing.T) {
convey.Convey("getFromCache", t, func(ctx convey.C) {
k := "flow"
//delete cache
keypro := s.netFlowKey
llen := len(testdata[k].AggrID) + 2
keys := []string{}
ids := append(testdata[k].AggrID, 1000, 2000)
for _, id := range ids {
keys = append(keys, keypro(id)...)
}
s.redis.DelMulti(cntx, keys...)
//miss all
miss := s.getFromCache(cntx, ids, keypro, &flowArr{}, nil)
//set last 2 caches & get from cache
s.redis.SetMulti(cntx, map[string]string{keys[llen-1]: "10000", keys[llen-2]: "20000"})
miss2 := s.getFromCache(cntx, ids, keypro, &AggrCacheDest{}, nil)
ctx.Convey("Then miss should not be nil.", func(ctx convey.C) {
ctx.So(miss, convey.ShouldNotBeNil)
ctx.So(fmt.Sprintf("%v", miss), convey.ShouldEqual, fmt.Sprintf("%v", ids))
ctx.So(fmt.Sprintf("%v", miss2), convey.ShouldEqual, fmt.Sprintf("%v", testdata[k].AggrID))
})
})
}
func TestServiceobjCache(t *testing.T) {
convey.Convey("objCache", t, func(ctx convey.C) {
k := "flow"
ids := testdata[k].ObjID
objdest := &flowArr{}
err := s.objCache(cntx, ids, objdest, nil)
objm := map[int64]*net.Flow{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for _, id := range ids {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestServiceaggregateRelationCache(t *testing.T) {
convey.Convey("aggregateRelationCache", t, func(ctx convey.C) {
k := "flow"
aggrdest := &AggrCacheDest{
GetKey: s.netFlowKey,
AggrRelaRaw: s.netFlowRelation,
}
err := s.aggregateRelationCache(cntx, testdata[k].AggrID, aggrdest, nil)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for nid, v := range testdata[k].RowRela {
ctx.So(len(aggrdest.Dest[nid]), convey.ShouldEqual, len(v))
mmp := map[int64]int64{}
for _, i := range v {
mmp[i] = i
}
for _, i := range aggrdest.Dest[nid] {
ctx.So(i, convey.ShouldEqual, mmp[i])
}
}
})
})
}
func TestServiceaggregateCache(t *testing.T) {
convey.Convey("aggregateCache", t, func(ctx convey.C) {
k := "flow"
objdest := &flowArr{}
w := &CacheWrap{
ObjDest: objdest,
AggrObjRaw: s.netFlowAppendRaw,
AggregateDest: &AggrCacheDest{
GetKey: s.netFlowKey,
},
}
err := s.aggregateCache(cntx, testdata[k].AggrID, w, nil, nil, nil, nil)
objm := map[int64]*net.Flow{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for id, v := range testdata[k].RowRela {
ctx.So(len(w.AggregateDest.Dest[id]), convey.ShouldEqual, len(v))
mmp := map[int64]int64{}
for _, i := range v {
mmp[i] = i
}
for _, i := range w.AggregateDest.Dest[id] {
ctx.So(i, convey.ShouldEqual, mmp[i])
}
}
for _, id := range testdata[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestServicedelCache(t *testing.T) {
convey.Convey("delCache", t, func(ctx convey.C) {
err := s.delCache(cntx, []string{})
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestNetArrGetKey(t *testing.T) {
convey.Convey("netArrGetKey", t, func(ctx convey.C) {
objdest := &netArr{}
p1 := objdest.GetKey(0)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestNetArrAppendString(t *testing.T) {
convey.Convey("netArrAppendString", t, func(ctx convey.C) {
objdest := &netArr{}
k := "net"
testdataprepare(k, testdata[k].RowObj)
v := testdata[k].Caches[objdest.GetKey(testdata[k].ObjID[0])[0]]
ok, err := objdest.AppendString(testdata[k].ObjID[0], v)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(objdest.dest[0].ID, convey.ShouldEqual, testdata[k].ObjID[0])
})
})
}
func TestNetArrAppendRaw(t *testing.T) {
convey.Convey("netArrAppendRaw", t, func(ctx convey.C) {
objdest := &netArr{}
k := "net"
missCache, err := objdest.AppendRaw(cntx, s, testdata[k].ObjID, nil)
objm := map[int64]*net.Net{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for ck, v := range testdata[k].Caches {
if strings.Contains(ck, "business_net") {
continue
}
pos := strings.Index(v, "ctime")
if pos == -1 {
ctx.So(fmt.Sprintf("%v", missCache[ck]), convey.ShouldEqual, fmt.Sprintf("%v", v))
} else {
ctx.So(fmt.Sprintf("%v", missCache[ck][:pos]), convey.ShouldEqual, fmt.Sprintf("%v", v[:pos]))
}
}
for _, id := range testdata[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestNetCache(t *testing.T) {
convey.Convey("NetCache", t, func(ctx convey.C) {
k := "net"
biz := testdata[k].AggrID[0]
res, err := s.netIDByBusiness(cntx, biz)
ctx.So(err, convey.ShouldBeNil)
ctx.So(fmt.Sprintf("%v", res), convey.ShouldEqual, fmt.Sprintf("%v", testdata[k].RowRela[biz]))
id := testdata[k].ObjID[0]
res2, err := s.netByID(cntx, id)
ctx.So(err, convey.ShouldBeNil)
ctx.So(res2.ID, convey.ShouldEqual, id)
err = s.delNetCache(cntx, res2)
ctx.So(err, convey.ShouldBeNil)
res3, err := s.netIDByBusiness(cntx, biz)
ctx.So(err, convey.ShouldBeNil)
ctx.So(fmt.Sprintf("%v", res3), convey.ShouldEqual, fmt.Sprintf("%v", testdata[k].RowRela[biz]))
res4, err := s.netByID(cntx, id)
ctx.So(err, convey.ShouldBeNil)
ctx.So(res4.ID, convey.ShouldEqual, id)
})
}
func TestTokenArrGetKey(t *testing.T) {
convey.Convey("TokenArrGetKey", t, func(ctx convey.C) {
objdest := &tokenArr{}
p1 := objdest.GetKey(0)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestTokenArrAppendString(t *testing.T) {
convey.Convey("TokenArrAppendString", t, func(ctx convey.C) {
objdest := &tokenArr{}
k := "token"
testdataprepare(k, testdata[k].RowObj)
v := testdata[k].Caches[objdest.GetKey(testdata[k].ObjID[0])[0]]
ok, err := objdest.AppendString(testdata[k].ObjID[0], v)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(objdest.dest[0].ID, convey.ShouldEqual, testdata[k].ObjID[0])
})
})
}
func TestTokenArrAppendRaw(t *testing.T) {
convey.Convey("TokenArrAppendRaw", t, func(ctx convey.C) {
objdest := &tokenArr{}
k := "token"
missCache, err := objdest.AppendRaw(cntx, s, testdata[k].ObjID, nil)
objm := map[int64]*net.Token{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for ck, v := range testdata[k].Caches {
pos := strings.Index(v, "ctime")
if pos == -1 {
ctx.So(fmt.Sprintf("%v", missCache[ck]), convey.ShouldEqual, fmt.Sprintf("%v", v))
} else {
ctx.So(fmt.Sprintf("%v", missCache[ck][:pos]), convey.ShouldEqual, fmt.Sprintf("%v", v[:pos]))
}
}
for _, id := range testdata[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestTokenCache(t *testing.T) {
convey.Convey("TokenCache", t, func(ctx convey.C) {
k := "token"
biz := testdata[k].ObjID
res, err := s.tokens(cntx, biz)
ctx.So(err, convey.ShouldBeNil)
resm := map[int64]*net.Token{}
for _, item := range res {
resm[item.ID] = item
}
for _, i := range biz {
ctx.So(resm[i], convey.ShouldNotBeNil)
}
keys := []string{}
for ck := range testdata[k].Caches {
keys = append(keys, ck)
}
err = s.redis.DelMulti(cntx, keys...)
ctx.So(err, convey.ShouldBeNil)
res2, err := s.tokens(cntx, biz)
ctx.So(err, convey.ShouldBeNil)
resm2 := map[int64]*net.Token{}
for _, item := range res2 {
resm2[item.ID] = item
}
for _, i := range biz {
ctx.So(resm2[i], convey.ShouldNotBeNil)
}
})
}
func TestBindArrGetKey(t *testing.T) {
convey.Convey("BindArrGetKey", t, func(ctx convey.C) {
objdest := &bindArr{}
p1 := objdest.GetKey(0)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestBindArrAppendString(t *testing.T) {
convey.Convey("BindArrAppendString", t, func(ctx convey.C) {
objdest := &bindArr{}
k := "bind"
testdataprepare(k, testdata[k].RowObj)
v := testdata[k].Caches[objdest.GetKey(testdata[k].ObjID[0])[0]]
ok, err := objdest.AppendString(testdata[k].ObjID[0], v)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(objdest.dest[0].ID, convey.ShouldEqual, testdata[k].ObjID[0])
})
})
}
func TestBindArrAppendRaw(t *testing.T) {
convey.Convey("BindArrAppendRaw", t, func(ctx convey.C) {
objdest := &bindArr{}
k := "bind"
missCache, err := objdest.AppendRaw(cntx, s, testdata[k].ObjID, nil)
objm := map[int64]*net.TokenBind{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for ck, v := range testdata[k].Caches {
if strings.Contains(ck, "flow_bind") || strings.Contains(ck, "transition_bind") {
continue
}
pos := strings.Index(v, "ctime")
if pos == -1 {
ctx.So(fmt.Sprintf("%v", missCache[ck]), convey.ShouldEqual, fmt.Sprintf("%v", v))
} else {
ctx.So(fmt.Sprintf("%v", missCache[ck][:pos]), convey.ShouldEqual, fmt.Sprintf("%v", v[:pos]))
}
}
for _, id := range testdata[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestBindCache(t *testing.T) {
convey.Convey("BindCache", t, func(ctx convey.C) {
k := "bind"
biz := testdata[k].AggrID
res, err := s.tokenBindByElement(cntx, biz, []int8{net.BindTypeFlow})
resm := map[int64]*net.TokenBind{}
for _, item := range res {
t.Logf("flow bind tokens in biz(%+v) item(%+v)", biz, item)
resm[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
for _, i := range testdata[k].ObjID[:2] {
ctx.So(resm[i], convey.ShouldNotBeNil)
}
res, err = s.tokenBindByElement(cntx, biz, net.BindTranType)
for _, item := range res {
resm[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
res2, err := s.tokenBinds(cntx, testdata[k].ObjID, true)
resm2 := map[int64]*net.TokenBind{}
for _, item := range res2 {
resm2[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
for _, i := range testdata[k].ObjID {
ctx.So(resm2[i], convey.ShouldNotBeNil)
}
})
}
func TestFlowArrGetKey(t *testing.T) {
convey.Convey("FlowArrGetKey", t, func(ctx convey.C) {
objdest := &flowArr{}
p1 := objdest.GetKey(0)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestFlowArrAppendString(t *testing.T) {
convey.Convey("FlowArrAppendString", t, func(ctx convey.C) {
objdest := &flowArr{}
k := "flow"
testdataprepare(k, testdata[k].RowObj)
v := testdata[k].Caches[objdest.GetKey(testdata[k].ObjID[0])[0]]
ok, err := objdest.AppendString(testdata[k].ObjID[0], v)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(objdest.dest[0].ID, convey.ShouldEqual, testdata[k].ObjID[0])
})
})
}
func TestFlowArrAppendRaw(t *testing.T) {
convey.Convey("FlowArrAppendRaw", t, func(ctx convey.C) {
objdest := &flowArr{}
k := "flow"
missCache, err := objdest.AppendRaw(cntx, s, testdata[k].ObjID, nil)
objm := map[int64]*net.Flow{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for ck, v := range testdata[k].Caches {
if strings.Contains(ck, "net_flow") {
continue
}
pos := strings.Index(v, "ctime")
if pos == -1 {
ctx.So(fmt.Sprintf("%v", missCache[ck]), convey.ShouldEqual, fmt.Sprintf("%v", v))
} else {
ctx.So(fmt.Sprintf("%v", missCache[ck][:pos]), convey.ShouldEqual, fmt.Sprintf("%v", v[:pos]))
}
}
for _, id := range testdata[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestFlowCache(t *testing.T) {
convey.Convey("FlowCache", t, func(ctx convey.C) {
k := "flow"
biz := testdata[k].AggrID[0]
res, err := s.flowsByNet(cntx, biz)
resm := map[int64]*net.Flow{}
for _, item := range res {
resm[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
for _, i := range testdata[k].RowRela[biz] {
ctx.So(resm[i], convey.ShouldNotBeNil)
}
res2, err := s.flowIDByNet(cntx, biz)
ctx.So(err, convey.ShouldBeNil)
mmp := map[int64]int64{}
for _, i := range testdata[k].RowRela[biz] {
mmp[i] = i
}
ctx.So(len(res2), convey.ShouldEqual, len(testdata[k].RowRela[biz]))
for _, i := range res2 {
ctx.So(i, convey.ShouldEqual, mmp[i])
}
res3, err := s.flows(cntx, testdata[k].ObjID, true)
resm2 := map[int64]*net.Flow{}
for _, item := range res3 {
resm2[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
for _, i := range testdata[k].ObjID {
ctx.So(resm2[i], convey.ShouldNotBeNil)
}
})
}
func TestTranArrGetKey(t *testing.T) {
convey.Convey("TranArrGetKey", t, func(ctx convey.C) {
objdest := &tranArr{}
p1 := objdest.GetKey(0)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestTranArrAppendString(t *testing.T) {
convey.Convey("TranArrAppendString", t, func(ctx convey.C) {
objdest := &tranArr{}
k := "tran"
testdataprepare(k, testdata[k].RowObj)
v := testdata[k].Caches[objdest.GetKey(testdata[k].ObjID[0])[0]]
ok, err := objdest.AppendString(testdata[k].ObjID[0], v)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(objdest.dest[0].ID, convey.ShouldEqual, testdata[k].ObjID[0])
})
})
}
func TestTranArrAppendRaw(t *testing.T) {
convey.Convey("TranArrAppendRaw", t, func(ctx convey.C) {
objdest := &tranArr{}
k := "tran"
_, err := objdest.AppendRaw(cntx, s, testdata[k].ObjID, nil)
objm := map[int64]*net.Transition{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for _, id := range testdata[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestTranCache(t *testing.T) {
convey.Convey("TranCache", t, func(ctx convey.C) {
k := "tran"
biz := testdata[k].AggrID[0]
res2, err := s.tranIDByNet(cntx, []int64{biz}, true, true)
ctx.So(err, convey.ShouldBeNil)
ctx.So(fmt.Sprintf("%v", res2), convey.ShouldEqual, fmt.Sprintf("%v", testdata[k].RowRela[biz]))
res3, err := s.transitions(cntx, testdata[k].ObjID, true)
resm2 := map[int64]*net.Transition{}
for _, item := range res3 {
resm2[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
for _, i := range testdata[k].ObjID {
ctx.So(resm2[i], convey.ShouldNotBeNil)
}
})
}
func TestDirArrGetKey(t *testing.T) {
convey.Convey("dirArrGetKey", t, func(ctx convey.C) {
objdest := &dirArr{}
p1 := objdest.GetKey(0)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDirArrAppendString(t *testing.T) {
convey.Convey("dirArrAppendString", t, func(ctx convey.C) {
objdest := &dirArr{}
k := "dir"
testdataprepare(k, testdata2[k].RowObj)
v := testdata2[k].Caches[objdest.GetKey(testdata2[k].ObjID[0])[0]]
ok, err := objdest.AppendString(testdata2[k].ObjID[0], v)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldEqual, true)
ctx.So(objdest.dest[0].ID, convey.ShouldEqual, testdata2[k].ObjID[0])
})
})
}
func TestDirArrAppendRaw(t *testing.T) {
convey.Convey("dirArrAppendRaw", t, func(ctx convey.C) {
objdest := &dirArr{}
k := "dir"
missCache, err := objdest.AppendRaw(cntx, s, testdata2[k].ObjID, nil)
objm := map[int64]*net.Direction{}
for _, item := range objdest.dest {
objm[item.ID] = item
}
ctx.Convey("Then err should be nil.missCache should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
for ck, v := range testdata2[k].Caches {
if strings.Contains(ck, "flow_direction") || strings.Contains(ck, "transition_direction") {
continue
}
pos := strings.Index(v, "ctime")
if pos == -1 {
ctx.So(fmt.Sprintf("%v", missCache[ck]), convey.ShouldEqual, fmt.Sprintf("%v", v))
} else {
ctx.So(fmt.Sprintf("%v", missCache[ck][:pos]), convey.ShouldEqual, fmt.Sprintf("%v", v[:pos]))
}
}
for _, id := range testdata2[k].ObjID {
ctx.So(objm[id], convey.ShouldNotBeNil)
}
})
})
}
func TestDirCache(t *testing.T) {
convey.Convey("DirCache", t, func(ctx convey.C) {
k := "dir"
biz := testdata2[k].AggrID
tp := net.DirInput
res, err := s.dirByFlow(cntx, biz, tp)
resm := map[int64]*net.Direction{}
for _, item := range res {
resm[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
expected := []int64{}
for _, i := range biz {
expected = append(expected, testdata2[k].RowRela[i][tp]...)
}
for _, i := range expected {
ctx.So(resm[i], convey.ShouldNotBeNil)
}
res2, err := s.dirByTran(cntx, biz, net.DirOutput, true)
for _, item := range res2 {
resm[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
expected2 := []int64{}
for _, i := range biz {
expected2 = append(expected2, testdata2[k].RowRela[i][tp]...)
}
for _, i := range expected2 {
ctx.So(resm[i], convey.ShouldNotBeNil)
}
res3, err := s.directions(cntx, testdata2[k].ObjID)
resm2 := map[int64]*net.Direction{}
for _, item := range res3 {
resm2[item.ID] = item
}
ctx.So(err, convey.ShouldBeNil)
for _, i := range testdata2[k].ObjID {
ctx.So(resm2[i], convey.ShouldNotBeNil)
}
})
}

View File

@@ -0,0 +1,891 @@
package service
import (
"context"
"fmt"
"sort"
"strconv"
"strings"
"github.com/jinzhu/gorm"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
//启动流程网之前的检查
func (s *Service) startNet(c context.Context, businessID int64, netID int64) (result *net.TriggerResult, err error) {
var (
n *net.Net
resultTokenPackage *net.TokenPackage
)
//网可用性
if n, err = s.netByID(c, netID); err != nil {
log.Error("startNet s.netByID(%d) error(%v)", netID, err)
return
}
if n == nil || !n.IsAvailable() || n.StartFlowID <= 0 {
log.Error("startNet s.netByID(%d) not found/disabled/start_flow_id=0", netID)
err = ecode.RequestErr
return
}
if n.BusinessID != businessID {
log.Error("startNet s.netByID(%d) business(%d) != param business(%d)", netID, n.BusinessID, businessID)
err = ecode.RequestErr
return
}
//初始节点的token集合
if resultTokenPackage, err = s.computeNewFlowPackage(c, n.StartFlowID, nil, nil, true); err != nil {
log.Error("startNet s.computeNewFlowPackage(%d) error(%+v) netid(%d)", n.StartFlowID, err, netID)
return
}
result = &net.TriggerResult{
NetID: netID,
ResultToken: resultTokenPackage,
NewFlowID: n.StartFlowID,
From: model.LogFromStart,
}
return
}
/*FetchJumpFlowInfo ..
* 跳流程详情页, 跳流程是单独db层面的更新
* 参数flowid(可选)
* 获取网下所有flow和变迁的所有操作项
*/
func (s *Service) FetchJumpFlowInfo(c context.Context, flowID int64) (res *net.JumpInfo, err error) {
var (
flow *net.Flow
runningNet int64
flowList []*net.Flow
)
res = &net.JumpInfo{
Flows: []*net.SimpleInfo{},
Operations: []*net.TranOperation{},
}
if flow, err = s.flowByID(c, flowID); err != nil {
log.Error("FetchJumpFlowInfo s.flowByID(%d) error(%v)", flowID, err)
return
}
if flow == nil {
log.Error("FetchJumpFlowInfo flow(%d) not found", flowID)
err = ecode.AegisFlowNotFound
return
}
runningNet = flow.NetID
if flowList, err = s.flowsByNet(c, runningNet); err != nil {
log.Error("FetchJumpFlowInfo s.flowsByNet(%d) error(%v) flowid(%d)", runningNet, err, flowID)
return
}
flowInfo := []*net.SimpleInfo{}
for _, item := range flowList {
flowInfo = append(flowInfo, &net.SimpleInfo{
ID: item.ID,
ChName: item.ChName,
})
}
//网下的所有变迁可操作项
if res.Operations, err = s.tranOpersByNet(c, []int64{runningNet}, []int8{net.BindTypeTransition}); err != nil {
log.Error("FetchJumpFlowInfo s.tranOpersByNet(%d) error(%v) flowid(%d)", runningNet, err, flowID)
return
}
res.Flows = flowInfo
return
}
/**
* 跳流程提交rid, oldflowid, newflowid, binds
* 可以单独提供flowid,单独提供binds
* 单独提供flowid时不做流程流转
*/
func (s *Service) jumpFlow(c context.Context, tx *gorm.DB, rid int64, oldFlowID int64, newFlowID int64, binds []int64) (result *net.JumpFlowResult, err error) {
var (
frs []*net.FlowResource
oldFlow, newFlow *net.Flow
triggerResult *net.TriggerResult
bindList []*net.TokenBind
submitPackage *net.TokenPackage
)
log.Info("jumpFlow before rid(%d) oldflowid(%d) newflowid(%d) binds(%v)", rid, oldFlowID, newFlowID, binds)
if rid <= 0 || oldFlowID <= 0 || (len(binds) == 0 && newFlowID <= 0) {
err = ecode.RequestErr
return
}
//检查资源是否运行在该节点
if frs, err = s.gorm.FRByUniques(c, []int64{rid}, []int64{oldFlowID}, true); err != nil {
log.Error("jumpFlow s.gorm.FRByUniques(%d,%d) error(%v)", rid, oldFlowID, err)
return
}
if len(frs) == 0 {
log.Error("jumpFlow rid(%d) not running at oldflowid(%d)", rid, oldFlowID)
err = ecode.AegisNotRunInFlow
return
}
if oldFlow, err = s.flowByID(c, oldFlowID); err != nil {
log.Error("jumpFlow s.flowByID(%d) error(%v) rid(%d)", oldFlowID, err, rid)
return
}
if oldFlow == nil {
log.Error("jumpFlow oldflowid(%d) not found, rid(%d)", oldFlowID, rid)
err = ecode.AegisFlowNotFound
return
}
/**
* jumpFlow 指定一批资源,更新为相同现状
* 不需要前端指定现状flowid
* 资源现状从单线跳到多线程的某个节点时----由运营决定是到sync-split节点还是并发分支上的某个节点------fr数目从1变成1/还是1变成n
* 资源现状从多线程跳到某个节点时----节点在并发分支上如何确定现状flow和新节点都在并发分支上----fr数目从1变成1
* ----节点在单线上,结束所有运行中都并发现状,加一个单线分支---fr数目从n变成1
*/
if len(binds) > 0 {
if bindList, err = s.tokenBinds(c, binds, true); err != nil {
log.Error("jumpFlow s.tokenBinds(%v) error(%v) rid(%d)", binds, err, rid)
return
}
if len(bindList) == 0 {
log.Error("jumpFlow binds(%v) not found, rid(%d)", binds, rid)
err = ecode.RequestErr
return
}
if submitPackage, err = s.newTokenPackage(c, bindList, true); err != nil {
log.Error("jumpFlow s.newTokenPackage(%v) error(%v) rid(%d)", binds, err, rid)
return
}
}
if newFlowID > 0 && newFlowID != oldFlowID {
if newFlow, err = s.flowByID(c, newFlowID); err != nil {
log.Error("jumpFlow newflow(%d) error(%v) rid(%d)", newFlowID, err, rid)
return
}
if newFlow == nil {
log.Error("jumpFlow newflow(%d) not found, rid(%d)", newFlowID, rid)
err = ecode.AegisFlowNotFound
return
}
if oldFlow.NetID != newFlow.NetID {
log.Error("jumpFlow rid(%d) run at oldflowid(%d)/net(%d), can't jump to newflowid(%d)/net(%d)", rid, oldFlowID, oldFlow.NetID, newFlowID, newFlow.NetID)
err = ecode.RequestErr
return
}
triggerResult = &net.TriggerResult{
RID: rid,
NetID: oldFlow.NetID,
NewFlowID: newFlowID,
OldFlowID: strconv.FormatInt(oldFlowID, 10),
SubmitToken: submitPackage,
ResultToken: submitPackage,
}
if err = s.reachNewFlowDB(c, tx, triggerResult); err != nil {
log.Error("jumpFlow s.reachNewFlowDB error(%v) triggerresult(%+v)", err, triggerResult)
return
}
} else {
newFlowID = oldFlowID
}
result = &net.JumpFlowResult{
RID: rid,
NetID: oldFlow.NetID,
SubmitToken: submitPackage,
ResultToken: submitPackage,
NewFlowID: newFlowID,
OldFlowID: strconv.FormatInt(oldFlowID, 10),
}
return
}
func (s *Service) afterJumpFlow(c context.Context, res *net.JumpFlowResult, bizid int64) (err error) {
trigger := &net.TriggerResult{
RID: res.RID,
NetID: res.NetID,
SubmitToken: res.SubmitToken,
ResultToken: res.ResultToken,
NewFlowID: res.NewFlowID,
OldFlowID: res.OldFlowID,
From: model.LogFromJump,
}
s.afterReachNewFlow(c, trigger, bizid)
return
}
/**
* 批量审核提交--单个遍历处理
* 参数business, rid, binds
* flowid(由rid反查现状)
* transition_id由flow_id查到的下游transition & token_bind集合 与 token_id的交集决定非叶子节点若找不到则报错
* 找到了,则计算新flow & result_token
* 计算result_token规则
* 变迁trigger=人工必须提供binds;
* 变迁trigger=其他binds可以为空 & 新flow要么有绑定token要么有向线上output不为空
*/
func (s *Service) computeBatchTriggerResult(c context.Context, businessID int64, rid int64, bindID []int64) (result *net.TriggerResult, err error) {
var (
oldFlow *net.Flow
)
log.Info("computeBatchTriggerResult start business(%d) rid(%d) bindID(%+v)", businessID, rid, bindID)
//反查指定资源所运行的节点
if oldFlow, _, err = s.flowsByRunning(c, rid, businessID, 0, true); err != nil {
return
}
if result, err = s.computeResult(c, rid, oldFlow, bindID, true); err != nil {
log.Error("computeBatchTriggerResult s.computeResult error(%v) rid(%d) businessid(%d) bindID(%v) oldflow(%+v)", err, rid, businessID, bindID, oldFlow)
return
}
result.From = model.LogFromBatch
log.Info("computeBatchTriggerResult end business(%d) rid(%d) bindID(%+v) result(%+v)", businessID, rid, bindID, result)
return
}
/**
* 详情页审核提交
* 参数rid, flowid, binds
* 当flowid=最后一个,不流转--binds可能为多个; 其余情况均正常流转到下一个flow--binds只有一个
* 计算result_token规则
* 变迁trigger=人工必须提供binds;
* 变迁trigger=其他binds可以为空 & 新flow要么有绑定token要么有向线上output不为空
*/
func (s *Service) computeTriggerResult(c context.Context, rid int64, oldFlowID int64, binds []int64) (result *net.TriggerResult, err error) {
var (
oldFlow *net.Flow
frs []*net.FlowResource
)
log.Info("computeTriggerResult start rid(%d) oldflow(%d) binds(%+v)", rid, oldFlowID, binds)
if rid <= 0 || oldFlowID <= 0 || len(binds) == 0 {
err = ecode.RequestErr
return
}
if frs, err = s.gorm.FRByUniques(c, []int64{rid}, []int64{oldFlowID}, true); err != nil {
log.Error("computeTriggerResult s.gorm.FRByUniques(%d) error(%v) rid(%d) binds(%v)", oldFlowID, err, rid, binds)
return
}
if len(frs) == 0 {
log.Error("computeTriggerResult rid(%d) not running at oldflow(%d) binds(%v)", rid, oldFlowID, binds)
err = ecode.AegisNotRunInFlow
return
}
if oldFlow, err = s.flowByID(c, oldFlowID); err != nil {
log.Error("computeTriggerResult s.flowByID(%d) error(%v) rid(%d) binds(%v)", oldFlowID, err, rid, binds)
return
}
if oldFlow == nil {
err = ecode.AegisFlowNotFound
return
}
if result, err = s.computeResult(c, rid, oldFlow, binds, false); err != nil {
log.Error("computeTriggerResult s.computeResult error(%v) binds(%v) flow(%+v) rid(%d)", err, binds, oldFlow, rid)
return
}
result.From = model.LogFromSingle
log.Info("computeTriggerResult end rid(%d) oldflow(%d) binds(%+v) result(%+v)", rid, oldFlowID, binds, result)
return
}
func (s *Service) computeResult(c context.Context, rid int64, flow *net.Flow, bindID []int64, fromBatch bool) (result *net.TriggerResult, err error) {
var (
hitAudit, hitHelper bool //审核流转和非流转操作是互斥的,不能同时提交
dirs []*net.Direction
isLast bool
triggerBind = []*net.TokenBind{}
binds []*net.TokenBind
trans []*net.Transition
triggerTranID, newFlowID int64
submitTokenPackage, resultTokenPackage *net.TokenPackage
resultDir *net.Direction
)
//非叶子节点只能有一个bind
if rid == 0 || flow == nil {
log.Error("computeResult rid(%d)=0/flow(%+v)=ni", rid, flow)
err = ecode.RequestErr
return
}
if binds, err = s.tokenBinds(c, bindID, true); err != nil {
log.Error("computeBatchTriggerResult s.tokenBinds(%+v) error(%v)", bindID, err)
return
}
tranID := []int64{}
for _, item := range binds {
if item.IsBatch() != fromBatch {
continue
}
tranID = append(tranID, item.ElementID)
if (fromBatch && item.Type == net.BindTypeTranBatch) || (!fromBatch && item.Type == net.BindTypeTransition) {
hitAudit = true
}
if (fromBatch && item.Type == net.BindTypeTranHelpBatch) || (!fromBatch && item.Type == net.BindTypeTranHelp) {
hitHelper = true
}
}
if len(binds) == 0 || (hitAudit && hitHelper) {
log.Error("computeBatchTriggerResult binds(%v) not found/both hit audit && helper are not allowed", binds)
err = ecode.RequestErr
return
}
if hitAudit {
//同网下、同flow下游的变迁过滤, 非叶子节点必须命中一个变迁
if dirs, err = s.dirByFlow(c, []int64{flow.ID}, net.DirInput); err != nil {
log.Error("computeResult s.dirByFlow(%d) error(%v)", flow.ID, err)
return
}
isLast = len(dirs) == 0 //是否为叶子节点
if !isLast {
tranID = []int64{}
for _, item := range dirs {
tranID = append(tranID, item.TransitionID)
}
}
}
if trans, err = s.transitions(c, tranID, true); err != nil {
log.Error("computeResult s.transitions(%v) error(%v) rid(%d) flow(%v)", tranID, err, rid, flow)
return
}
tranMap := map[int64]*net.Transition{}
for _, item := range trans {
tranMap[item.ID] = item
}
triggerBindID := []int64{}
triggerTrans := []int64{}
for _, item := range binds {
if tranMap[item.ElementID] == nil || tranMap[item.ElementID].Trigger != net.TriggerManual {
continue
}
//叶子节点=同网过滤|非叶子节点=同flow下游变迁过滤
if (isLast && tranMap[item.ElementID].NetID == flow.NetID) || !isLast {
triggerBind = append(triggerBind, item)
triggerTrans = append(triggerTrans, item.ElementID)
triggerBindID = append(triggerBindID, item.ID)
}
}
if len(triggerBind) == 0 || (!isLast && len(triggerBind) > 1) {
log.Error("computeResult no triggered(%+v)/non-leaf-flow triggered >2, rid(%d) flow(%+v)", triggerBindID, rid, flow)
err = ecode.AegisNotTriggerFlow
return
}
//计算提交的变迁 & 提交令牌集合
if submitTokenPackage, err = s.newTokenPackage(c, triggerBind, true); err != nil {
log.Error("computeResult s.newTokenPackage error(%v)", err)
return
}
if hitHelper || isLast {
//不流转的情况
newFlowID = flow.ID
resultTokenPackage = submitTokenPackage
} else {
//中间节点找到下游新节点
triggerTranID = triggerTrans[0]
if resultDir, err = s.fetchTranNextEnableDirs(c, triggerTranID); err != nil {
log.Error("computeResult s.fetchTranNextEnableDirs(%d) error(%v)", triggerTranID, err)
return
}
newFlowID = resultDir.FlowID
if resultTokenPackage, err = s.computeNewFlowPackage(c, newFlowID, resultDir, submitTokenPackage, false); err != nil {
log.Error("computeResult s.computeNewFlowPackage error(%v)", err)
return
}
}
log.Info("submitpk(%+v) result(%+v)", submitTokenPackage, resultTokenPackage)
result = &net.TriggerResult{
RID: rid,
NetID: flow.NetID,
SubmitToken: submitTokenPackage,
ResultToken: resultTokenPackage,
NewFlowID: newFlowID,
OldFlowID: strconv.FormatInt(flow.ID, 10),
TransitionID: triggerTrans,
}
log.Info("computeResult end islast(%v) frombatch(%v) result(%+v)", isLast, fromBatch, result)
return
}
/**
* 资源审核详情页,获取运行资源所在网的节点下的所有可允许下游可操作项,去掉批量可操作项:
* 若为网中的最后一个flow, 获取网中所有变迁的可操作项(bind级别去重)--提交时只改token不改flow
* 若为中间节点:只有一个(非并发),直接获取变迁的可操作项; 并发,需指定哪个变迁(先抛错)
* 前提同一rid只在同一业务下的一个net下运行
* 任务列表进入详情页参数rid,businessid,netid(可选)
*/
func (s *Service) fetchResourceTranInfo(c context.Context, rid int64, businessID int64, netID int64) (result *net.TransitionInfo, err error) {
var (
runningFlow *net.Flow
runningNet int64
)
if rid <= 0 || businessID <= 0 {
err = ecode.RequestErr
return
}
if runningFlow, runningNet, err = s.flowsByRunning(c, rid, businessID, netID, false); err != nil {
return
}
flowID := runningFlow.ID
if result, err = s.fetchTaskTranInfo(c, rid, flowID, runningNet); err != nil {
log.Error("fetchResourceTranInfo s.fetchTaskTranInfo error(%v) rid(%d) businessid(%d) netid(%d) runningnet(%d)", err, rid, businessID, netID, runningNet)
}
return
}
/**
* 任务详情页获取flowid下的所有可允许变迁的可操作项去掉批量可操作项:
* 若为网中的最后一个flow, 获取网中所有变迁的可操作项(bind级别去重)--提交时只改token不改flow
* 若为中间节点:只有一个(非并发),直接获取变迁的可操作项;并发,需指定哪个变迁(先抛错)
* 前提同一rid只在同一业务下的一个net下运行
* 资源列表进入详情页参数rid, flowid, netid(可选)
*/
func (s *Service) fetchTaskTranInfo(c context.Context, rid int64, flowID int64, netID int64) (result *net.TransitionInfo, err error) {
var (
transitionID []int64
flow *net.Flow
enableDir []*net.Direction
trans []*net.Transition
)
if rid <= 0 || flowID <= 0 {
err = ecode.RequestErr
return
}
if enableDir, err = s.fetchFlowNextEnableDirs(c, flowID); err != nil {
log.Error("fetchTaskTranInfo s.fetchFlowNextEnableDirs(%d) error(%v)", flowID, err)
return
}
//作为分支的叶子节点,获取整个网的所有可操作项
if len(enableDir) == 0 {
if netID <= 0 {
if flow, err = s.flowByID(c, flowID); err != nil {
log.Error("fetchTaskTranInfo s.flowByID error(%v) rid(%d) flowid(%d)", err, rid, flowID)
return
}
if flow == nil {
log.Error("fetchTaskTranInfo flow(%d) not found rid(%d) ", flowID, rid)
err = ecode.AegisFlowNotFound
return
}
netID = flow.NetID
}
if transitionID, err = s.tranIDByNet(c, []int64{netID}, true, true); err != nil {
log.Error("fetchTaskTranInfo s.tranIDByNet(%d) error(%v) rid(%d) flowid(%d)", netID, err, rid, flowID)
return
}
} else {
transitionID = []int64{}
for _, item := range enableDir {
transitionID = append(transitionID, item.TransitionID)
}
if trans, err = s.transitions(c, transitionID, false); err != nil {
log.Error("fetchTaskTranInfo s.transitions(%v) error(%v) rid(%d) flowid(%d)", transitionID, err, rid, flowID)
return
}
transitionID = []int64{}
for _, item := range trans {
if item.Trigger == net.TriggerManual {
transitionID = append(transitionID, item.ID)
}
}
}
result = &net.TransitionInfo{
RID: rid,
FlowID: flowID,
}
if len(transitionID) == 0 {
return
}
if result.Operations, err = s.tranOpers(c, transitionID, []int8{net.BindTypeTransition, net.BindTypeTranHelp}); err != nil {
log.Error("fetchTaskTranInfo s.tranOpers(%v) error(%v) rid(%d) flowid(%d)", transitionID, err, rid, flowID)
return
}
return
}
/**
* 全部资源列表页,获取所有变迁的所有批量操作项
* 参数businessid,netid(选填)
*/
func (s *Service) fetchBatchOperations(c context.Context, businessID int64, netID int64) (operations []*net.TranOperation, err error) {
var (
netIDList []int64
)
operations = []*net.TranOperation{}
if netID > 0 {
netIDList = []int64{netID}
} else {
//查询business_id下的所有net
if netIDList, err = s.netIDByBusiness(c, businessID); err != nil {
log.Error("fetchBatchOperations s.netIDByBusiness error(%v) businessid(%d) netid(%d)", err, businessID, netID)
return
}
if len(netIDList) == 0 {
log.Error("fetchBatchOperations business(%d) no net", businessID)
return
}
}
//查询所有net下的所有变迁可操作项
if operations, err = s.tranOpersByNet(c, netIDList, []int8{net.BindTypeTranBatch, net.BindTypeTranHelpBatch}); err != nil {
log.Error("fetchBatchOperations s.tranOpersByNet(%v) error(%v) businessid(%d) netid(%d)", netIDList, err, businessID, netID)
}
return
}
func (s *Service) tranOpersByNet(c context.Context, netIDs []int64, bindTp []int8) (opers []*net.TranOperation, err error) {
var (
transitionID = []int64{}
)
opers = []*net.TranOperation{}
if len(netIDs) == 0 {
return
}
if transitionID, err = s.tranIDByNet(c, netIDs, true, true); err != nil {
log.Error("tranOpersByNet s.tranIDByNet(%v) error(%v) isBatch(%v)", netIDs, err, bindTp)
return
}
if len(transitionID) == 0 {
return
}
if opers, err = s.tranOpers(c, transitionID, bindTp); err != nil {
log.Error("tranOpersByNet s.tranOpers(%v) error(%v) netid(%v) bindtp(%v)", transitionID, err, netIDs, bindTp)
}
return
}
func (s *Service) tranOpers(c context.Context, tranID []int64, bindTp []int8) (opers []*net.TranOperation, err error) {
var (
binds = []*net.TokenBind{}
)
opers = []*net.TranOperation{}
if binds, err = s.tokenBindByElement(c, tranID, bindTp); err != nil {
log.Error("tranOpers s.tokenBindByElement error(%v) tranid(%v) bindtp(%v)", err, tranID, bindTp)
return
}
//bind.token_id维度去重 + bind.id维度排序
tokenIDBind := map[string][]int64{}
unique := map[string]*net.TranOperation{}
for _, item := range binds {
if _, exist := tokenIDBind[item.TokenID]; !exist {
tokenIDBind[item.TokenID] = []int64{item.ID}
unique[item.TokenID] = &net.TranOperation{
ChName: item.ChName,
}
continue
}
tokenIDBind[item.TokenID] = append(tokenIDBind[item.TokenID], item.ID)
}
for uniqueTokenID, item := range unique {
item.BindIDList = xstr.JoinInts(tokenIDBind[uniqueTokenID])
opers = append(opers, item)
}
sort.Sort(net.TranOperationArr(opers))
return
}
/**
* 计算新节点的token集合
* 启动流程节点只提供newflowID, 找绑定的token
* 流转过程中根据bindIDs + 触发变迁的可允许有向线(变迁id + newflowid)计算新flow的结果
* 1. 若有静态绑定token,返回绑定的
* 2. 若没静态绑定且output="",返回bindID
* 3. 若没静态绑定且output!="",解析output计算---计算可能涉及到触发变迁/上一个flowid
* 4. 其他情况,为配置错误,应该在配置时避免---todo
* 5. start临时支持无令牌绑定情况
*/
func (s *Service) computeNewFlowPackage(c context.Context, newFlowID int64, fromDir *net.Direction, submitPackage *net.TokenPackage, start bool) (resultTokens *net.TokenPackage, err error) {
var (
binds []*net.TokenBind
)
//flow绑定了tokens,直接返回
if binds, err = s.tokenBindByElement(c, []int64{newFlowID}, []int8{net.BindTypeFlow}); err != nil {
log.Error("computeNewFlowPackage s.tokenBindByElement error(%v) newflowid(%d) fromdir(%v) submit(%+v)", err, newFlowID, fromDir, submitPackage)
return
}
if len(binds) > 0 {
if resultTokens, err = s.newTokenPackage(c, binds, false); err != nil {
log.Error("computeNewFlowPackage s.newTokenPackage error(%v) newflowid(%d) fromdir(%v) submit(%+v)", err, newFlowID, fromDir, submitPackage)
}
return
}
if start {
return
}
//没绑定的通过有向线的output计算
if fromDir == nil {
err = ecode.AegisFlowNoFromDir
log.Error("computeNewFlowPackage newflowid(%d) has no tokens & fromdir, submit(%+v)", newFlowID, submitPackage)
return
}
//output为空根据提交内容
if fromDir.Output == "" {
resultTokens = submitPackage
return
}
//todo--compute output解析与prevFlowID + rid + submitTokens相关,return {{"rids":[],"tokens":[]}} --version2
return
}
/**
* 计算指定令牌关联的打包形式,包括:各类型的值+关联中文名+关联对应的令牌
* sametokenid=true需检查binds对应的tokenid是否一致
*/
func (s *Service) newTokenPackage(c context.Context, binds []*net.TokenBind, sameTokenID bool) (res *net.TokenPackage, err error) {
var (
tokenIDList []int64
tokens []*net.Token
value interface{}
sameTokenName string
hitAudit bool
)
tokenIDStr := ""
tokenID := ""
if sameTokenID {
tokenID = binds[0].TokenID
sameTokenName = binds[0].ChName
}
for _, item := range binds {
if sameTokenID && tokenID != item.TokenID {
err = ecode.RequestErr
log.Error("newTokenPackage binds diff token(%s!=%s) sameTokenID(%v)", tokenID, item.TokenID, sameTokenID)
return
}
if item.Type == net.BindTypeTransition || item.Type == net.BindTypeTranBatch {
hitAudit = true
}
tokenIDStr = tokenIDStr + "," + item.TokenID
}
tokenIDStr = strings.TrimLeft(tokenIDStr, ",")
if tokenIDList, err = xstr.SplitInts(tokenIDStr); err != nil {
log.Error("newTokenPackage xstr.SplitInts(%s) error(%v) sameTokenID(%v)", tokenIDStr, err, sameTokenID)
return
}
if tokens, err = s.tokens(c, tokenIDList); err != nil {
log.Error("newTokenPackage s.tokens(%v) error(%v) sameTokenID(%v)", tokenIDStr, err, sameTokenID)
return
}
if len(tokens) == 0 {
log.Error("newTokenPackage tokens(%s) not found sameTokenID(%v)", tokenIDStr, sameTokenID)
err = ecode.AegisTokenNotFound
return
}
values := map[string]interface{}{}
chName := sameTokenName
for _, item := range tokens {
if value, err = item.FormatValue(); err != nil {
log.Error("NewTokenPackage item.FormatValue(%+v) error(%v) sameTokenID(%v)", item, err, sameTokenID)
return
}
values[item.Name] = value
if sameTokenName == "" {
chName = chName + item.ChName
}
}
res = &net.TokenPackage{
Values: values,
TokenIDList: tokenIDList,
ChName: chName,
HitAudit: hitAudit,
}
return
}
/**
* flowsByRunning 指定业务或netid获取资源的运行节点, 只在一个节点上运行
* rid反查现状得到flows,netid或businessid做过滤
* businessID可选netID可选2者必须提供一个
* onlyRunning 只过滤正常运行的资源现状
*/
func (s *Service) flowsByRunning(c context.Context, rid int64, businessID int64, netID int64, onlyRunning bool) (runningFlow *net.Flow, runningNetID int64, err error) {
var (
n *net.Net
nets []int64
frs []*net.FlowResource
)
if rid <= 0 || (businessID <= 0 && netID == 0) {
err = ecode.RequestErr
log.Error("flowsByRunning rid(%d)/businessid(%d)+netid(%d) are empty, onlyrunning(%v)", rid, businessID, netID, onlyRunning)
return
}
if netID > 0 {
if n, err = s.netByID(c, netID); err != nil {
log.Error("flowsByRunning s.netByID(%d) error(%v) rid(%d) businessid(%d) onlyrunning(%v)", netID, err, rid, businessID, onlyRunning)
return
}
if n == nil || n.BusinessID != businessID {
log.Error("flowsByRunning net(%d) in business(%d) not found, rid(%d) onlyrunning(%v)", netID, businessID, rid, onlyRunning)
err = ecode.RequestErr
return
}
nets = []int64{netID}
} else {
if nets, err = s.netIDByBusiness(c, businessID); err != nil {
log.Error("flowsByRunning s.netIDByBusiness(%d) error(%v) rid(%d) onlyrunning(%v)", businessID, err, rid, onlyRunning)
return
}
}
if frs, err = s.gorm.FRByNetRID(c, nets, []int64{rid}, onlyRunning); err != nil {
log.Error("flowsByRunning s.gorm.FRByNetRID(%d) error(%v) businessid(%d) netid(%d)", rid, err, businessID, netID)
return
}
if len(frs) == 0 {
log.Error("flowsByRunning rid(%d) not running in business(%d)/netid(%d) onlyrunning(%v)", rid, businessID, netID, onlyRunning)
err = ecode.AegisNotRunInRange
return
}
flowID := []int64{}
for _, item := range frs {
if runningNetID == 0 {
runningNetID = item.NetID
} else if item.NetID != runningNetID {
log.Error("flowsByRunning rid(%d) running is both net(%d) & net(%d), business(%d), onlyrunning(%v)", rid, runningNetID, item.NetID, businessID, onlyRunning)
err = ecode.AegisRunInDiffNet
return
}
flowID = append(flowID, item.FlowID)
}
if len(flowID) > 1 {
log.Error("flowsByRunning rid(%d) running in flows(%+v)>=2, businessid(%d)/netid(%d) runningnet(%d), onlyrunning(%v)", rid, flowID, businessID, netID, runningNetID, onlyRunning)
err = ecode.AegisNotRunInFlow
return
}
if runningFlow, err = s.flowByID(c, flowID[0]); err != nil {
log.Error("flowsByRunning s.flowByID(%d) error(%v), rid(%d) businessid(%d) netid(%d), onlyrunning(%v)", flowID[0], err, rid, businessID, netID, onlyRunning)
return
}
if runningFlow == nil {
log.Error("flowsByRunning rid(%d) running in flows(%d) not found, businessid(%d) netid(%d) onlyrunnning(%v)", rid, flowID[0], businessID, netID, onlyRunning)
err = ecode.AegisFlowNotFound
return
}
return
}
/**
* 流转到新节点且已更新现状表后,所需做的处理
* 1. 现状流转日志
* 2. 下游变迁处理,比如:人工变迁是否需要分发
*/
func (s *Service) afterReachNewFlow(c context.Context, pm *net.TriggerResult, bizid int64) (err error) {
s.sendNetTriggerLog(c, pm)
err = s.prepareBeforeTrigger(c, []int64{pm.RID}, pm.NewFlowID, bizid)
return
}
/**
* 流转到达新节点更新db操作需在新节点计算token之后执行
* 1. 现状表更新
*/
func (s *Service) reachNewFlowDB(c context.Context, tx *gorm.DB, pm *net.TriggerResult) (err error) {
var (
flow *net.Flow
)
if pm.NetID <= 0 || pm.RID <= 0 || pm.NewFlowID <= 0 {
log.Error("reachNewFlowDB params error(%+v)", pm)
err = ecode.RequestErr
return
}
//新节点与netid的同网检查
if flow, err = s.flowByID(c, pm.NewFlowID); err != nil {
log.Error("reachNewFlowDB s.flowByID error(%v) params(%+v)", err, pm)
return
}
if flow == nil || flow.NetID != pm.NetID {
log.Error("reachNewFlowDB s.flowByID not found/not same net, params(%+v), flow(%+v)", pm, flow)
err = ecode.RequestErr
return
}
if err = s.updateFlowResources(c, tx, pm.NetID, pm.RID, pm.NewFlowID); err != nil {
log.Error("reachNewFlowDB s.changeFlowResource error(%v) params(%+v)", err, pm)
return
}
//todo--更新现状token情况
return
}
//取消指定资源的所有运行中流程
func (s *Service) cancelNet(c context.Context, tx *gorm.DB, rids []int64) (res map[int64]string, err error) {
var (
frs []*net.FlowResource
)
if len(rids) == 0 {
return
}
res = map[int64]string{}
if frs, err = s.gorm.FRByUniques(c, rids, nil, true); err != nil {
log.Error("cancelNet s.gorm.FRByUniques error(%v) rids(%+v)", err, rids)
return
}
if len(frs) == 0 {
return
}
if err = s.gorm.CancelFlowResource(c, tx, rids); err != nil {
log.Error("cancelNet s.gorm.CancelFlowResource error(%+v) rids(%+v)", err, rids)
return
}
//添加日志
trigger := &net.TriggerResult{
From: model.LogFromCancle,
}
for _, item := range frs {
pref := res[item.RID]
if pref != "" {
pref = "," + pref
}
res[item.RID] = fmt.Sprintf("%s%d", pref, item.FlowID)
trigger.RID = item.RID
trigger.NetID = item.NetID
trigger.OldFlowID = strconv.FormatInt(item.FlowID, 10)
trigger.NewFlowID = item.FlowID
s.sendNetTriggerLog(c, trigger)
}
return
}

View File

@@ -0,0 +1,177 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServicestartNet(t *testing.T) {
var (
rid = int64(1)
biz = int64(1)
nid = int64(1)
tx, _ = s.gorm.BeginTx(context.TODO())
)
convey.Convey("startNet", t, func(ctx convey.C) {
result, err := s.startNet(context.TODO(), biz, nid)
if err == nil {
t.Logf("result(%+v) result.resulttoken(%+v)", result, result.ResultToken)
} else {
return
}
result.RID = rid
err = s.reachNewFlowDB(context.TODO(), tx, result)
if err == nil {
tx.Commit()
}
s.afterReachNewFlow(context.TODO(), result, biz)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicecancelNet(t *testing.T) {
var (
rid = []int64{1, 3, 4}
tx, _ = s.gorm.BeginTx(context.TODO())
)
convey.Convey("cancelNet", t, func(ctx convey.C) {
_, err := s.cancelNet(context.TODO(), tx, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
/**
批量
*/
func TestServicefetchBatchOperations(t *testing.T) {
convey.Convey("fetchBatchOperations", t, func(ctx convey.C) {
result, err := s.fetchBatchOperations(cntx, 1, 0)
for _, item := range result {
t.Logf("operations(%+v)", item)
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicecomputeBatchTriggerResult(t *testing.T) {
var (
rid = int64(1) //running at flow(1), flow(1)->t1->flow(3)
binds = []int64{1} //bind by transition 3 & 1
)
convey.Convey("computeBatchTriggerResult", t, func(ctx convey.C) {
result, err := s.computeBatchTriggerResult(cntx, 1, rid, binds)
if err != nil {
return
}
tx, _ := s.gorm.BeginTx(cntx)
s.reachNewFlowDB(cntx, tx, result)
err = tx.Commit().Error
t.Logf("result(%+v) submittoken(%+v) resulttoken(%+v)", result, result.SubmitToken, result.ResultToken)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
/**
*资源单个处理
*/
func TestServicefetchResourceTranInfo(t *testing.T) {
convey.Convey("fetchResourceTranInfo", t, func(ctx convey.C) {
result, err := s.fetchResourceTranInfo(cntx, 1, 1, 0)
t.Logf("result(%+v)", result)
for _, item := range result.Operations {
t.Logf("operations(%+v)", item)
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicecomputeTriggerResult(t *testing.T) {
var (
rid = int64(1)
flowID = int64(1)
binds = []int64{23}
)
convey.Convey("computeTriggerResult", t, func(ctx convey.C) {
result, err := s.computeTriggerResult(cntx, rid, flowID, binds)
if err == nil {
t.Logf("result(%+v) submittoken(%+v) resulttoken(%+v)", result, result.SubmitToken, result.ResultToken)
s.sendNetTriggerLog(context.TODO(), result)
}
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
/**
* 任务单个处理
*/
func TestServicefetchTaskTransitionInfo(t *testing.T) {
convey.Convey("fetchTaskTranInfo", t, func(ctx convey.C) {
result, err := s.fetchTaskTranInfo(cntx, 1, 1, 1)
t.Logf("result(%+v)", result)
for _, item := range result.Operations {
t.Logf("operations(%+v)", item)
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
/**
* 跳流程
*/
func TestServicejumpFlow(t *testing.T) {
var (
tx, _ = s.gorm.BeginTx(context.TODO())
)
convey.Convey("jumpFlow", t, func(ctx convey.C) {
result, err := s.jumpFlow(cntx, tx, 1, 1, 2, []int64{10})
if err != nil {
return
}
t.Logf("result(%+v) resulttoken(%+v) submittoken(%+v)", result, result.ResultToken, result.SubmitToken)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicefetchJumpFlowInfo(t *testing.T) {
convey.Convey("FetchJumpFlowInfo", t, func(ctx convey.C) {
res, err := s.FetchJumpFlowInfo(cntx, 1)
for _, item := range res.Operations {
t.Logf("operations(%+v)", item)
}
for _, item := range res.Flows {
t.Logf("flowArr(%+v)", item)
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,51 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/aegis/model/net"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceGetNetList(t *testing.T) {
var (
c = context.TODO()
pm = &net.ListNetParam{}
)
convey.Convey("GetNetList", t, func(ctx convey.C) {
result, err := s.GetNetList(c, pm)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
func TestServiceShowNet(t *testing.T) {
var (
c = context.TODO()
id = int64(1)
)
convey.Convey("ShowNet", t, func(ctx convey.C) {
r, err := s.ShowNet(c, id)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldNotBeNil)
})
})
}
func TestServicenetCheckUnique(t *testing.T) {
var (
chName = ""
)
convey.Convey("netCheckUnique", t, func(ctx convey.C) {
err, msg := s.netCheckUnique(cntx, chName)
ctx.Convey("Then err should be nil.msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,274 @@
package service
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"go-common/app/admin/main/aegis/model"
"go-common/library/ecode"
"go-common/library/log"
)
//ReportTaskflow .
func (s *Service) ReportTaskflow(c context.Context, opt *model.OptReport) (res map[string][]*model.ReportFlowItem, form map[string][24]*model.ReportFlowItem, err error) {
// TODO 从redis 补充实时增量
var (
mnames map[int64]string
metas []*model.ReportMeta
)
// 取出统计元数据
mnames, metas, err = s.reportMeta(c, opt)
if err != nil {
return
}
tempres := model.Gentempres(opt, mnames, metas)
res = model.Genres(opt, tempres, mnames)
form = model.Genform(res)
return
}
//MemberStats . 统计个人24h 处理量 处理率 通过率 平均耗时
func (s *Service) MemberStats(c context.Context, bizid, flowid int64, uids []int64) (res map[int64][]interface{}, err error) {
var (
metas []*model.ReportMeta
opt = &model.OptReport{
BizID: bizid,
FlowID: flowid,
Type: model.TypeMeta,
}
passState = s.passState(bizid, flowid)
)
if _, metas, err = s.reportMeta(c, opt); err != nil {
return
}
return model.GenMemberStat(metas, passState)
}
//MemberStats . 统计个人24h 处理量 处理率 通过率 平均耗时
func (s *Service) parseOption(c context.Context, opt *model.OptReport) (uids []int64, mnames map[int64]string, err error) {
var muids map[string]int64
mnames = make(map[int64]string)
bTime, _ := time.ParseInLocation("2006-01-02", opt.Bt, time.Local)
eTime, _ := time.ParseInLocation("2006-01-02", opt.Et, time.Local)
switch {
case bTime.IsZero() && eTime.IsZero():
bTime = time.Now().Add(-24 * time.Hour)
eTime = time.Now()
case bTime.IsZero() && !eTime.IsZero():
bTime = eTime.Add(-24 * time.Hour)
case !bTime.IsZero() && eTime.IsZero():
eTime = bTime.Add(24 * time.Hour)
}
eTime = eTime.Add(+1 * time.Hour)
opt.Btime, opt.Etime = bTime, eTime
opt.Bt, opt.Et = bTime.Format("2006-01-02 15"), eTime.Format("2006-01-02 15")
if bTime.After(eTime) {
err = ecode.RequestErr
return
}
if len(opt.UName) > 0 {
if muids, err = s.http.GetUIDs(c, opt.UName); err != nil {
return
}
for name, uid := range muids {
uids = append(uids, uid)
mnames[uid] = name
}
}
return
}
func (s *Service) reportMeta(c context.Context, opt *model.OptReport) (mnames map[int64]string, metas []*model.ReportMeta, err error) {
// TODO 从redis 补充实时增量
var (
uids []int64
)
uids, mnames, err = s.parseOption(c, opt)
if err != nil {
return
}
// 取出统计元数据
metas, missuids, err := s.gorm.ReportTaskMetas(c, opt.Bt, opt.Et, opt.BizID, opt.FlowID, uids, mnames, opt.Type)
if err != nil {
return
}
if len(missuids) > 0 && opt.Type == model.TypeMeta {
var tunames map[int64]string
if tunames, err = s.http.GetUnames(c, missuids); err != nil {
return
}
for uid, uname := range tunames {
mnames[uid] = uname
}
}
return
}
//TODO 根据业务配置,获取通过状态号
func (s *Service) passState(bizid, flowid int64) int64 {
return 1
}
//ReportTaskSubmit 业务节点下的任务提交统计信息, 按天分页
func (s *Service) ReportTaskSubmit(c context.Context, pm *model.OptReportSubmit) (res *model.ReportSubmitRes, err error) {
var (
datas []*model.TaskReport
states map[string]string
unames map[int64]string
uids = []int64{}
dayUID = int64(-1)
dayUname = "ALL"
groups = [][]*model.ReportSubmitItem{} //{stat_date:[]obj}
creates = map[string]string{} //{bizid_flowid_statdate:cnt}
stateOrderUnique = map[string]string{}
)
res = &model.ReportSubmitRes{}
//取出元数据: 状态名 + 操作提交数据
if datas, err = s.gorm.TaskReports(c, pm.BizID, pm.FlowID,
[]int8{model.TypeCreate, model.TypeDaySubmit, model.TypeDayUserSubmit},
pm.Bt, pm.Et); err != nil || len(datas) == 0 {
return
}
if states, err = s.TokenByName(c, pm.BizID, "state"); err != nil {
return
}
prevDate := ""
groupItems := []*model.ReportSubmitItem{}
for _, item := range datas {
statdate := item.StatDate.Format("2006-01-02")
//每日任务增量
if item.Type == model.TypeCreate {
creates[fmt.Sprintf("%d_%s", item.FlowID, statdate)] = item.Content
continue
}
//每日uid维度的提交数据
if item.Type == model.TypeDaySubmit {
item.UID = dayUID
} else {
uids = append(uids, item.UID)
}
content := &model.ReportSubmitContent{}
if err = json.Unmarshal([]byte(item.Content), content); err != nil {
log.Error("ReportTaskSubmit json.Unmarshal(%s) error(%v), for id(%d)", item.Content, err, item.ID)
continue
}
submitCnt := int64(0)
statecnt := map[string]*model.ReportSubmitState{}
for state, cnt := range content.RscStates {
submitCnt = submitCnt + cnt
statecnt[state] = &model.ReportSubmitState{
Cnt: cnt,
}
if _, exist := stateOrderUnique[state]; !exist {
stateOrderUnique[state] = states[state]
}
}
//各状态的提交量所占比率
for _, one := range statecnt {
one.Ratio = fmt.Sprintf("%.2f", float64(one.Cnt)/float64(submitCnt))
}
if prevDate == "" {
prevDate = statdate
}
if prevDate != statdate {
groups = append(groups, groupItems)
groupItems = []*model.ReportSubmitItem{}
prevDate = statdate
}
groupItems = append(groupItems, &model.ReportSubmitItem{
StatDate: statdate,
UID: item.UID,
AvgDur: time.Unix(content.AvgDur, 0).In(time.UTC).Format("15:04:05"),
AvgUtime: time.Unix(content.AvgUtime, 0).In(time.UTC).Format("15:04:05"),
FlowID: item.FlowID,
SubmitCnt: submitCnt,
StateCnt: statecnt,
})
}
if len(groupItems) > 0 {
groups = append(groups, groupItems)
}
//get usernames
unames, _ = s.http.GetUnames(c, uids)
res.Order = []string{
"state_date",
"create_cnt",
"username",
"avg_dur",
"avg_utime",
"submit_cnt",
}
res.Header = map[string]string{
"state_date": "日期",
"create_cnt": "新增量",
"username": "操作人",
"avg_dur": "平均耗时",
"avg_utime": "平均停留时间",
"submit_cnt": "操作总量",
}
res.Rows = make([][]map[string]string, len(groups))
for state, name := range stateOrderUnique {
cnt := fmt.Sprintf("state_%s_cnt", state)
ratio := fmt.Sprintf("state_%s_ratio", state)
res.Header[cnt] = fmt.Sprintf("%s量", name)
res.Header[ratio] = fmt.Sprintf("%s率", name)
res.Order = append(res.Order, cnt, ratio)
}
for i, list := range groups {
res.Rows[i] = make([]map[string]string, len(list))
for j, item := range list {
uname := strconv.FormatInt(item.UID, 10)
create := "0"
if item.UID == dayUID {
create = creates[fmt.Sprintf("%d_%s", item.FlowID, item.StatDate)]
uname = dayUname
} else if v, exist := unames[item.UID]; exist {
uname = v
}
one := map[string]string{
"state_date": item.StatDate,
"create_cnt": create,
"username": uname,
"avg_dur": item.AvgDur,
"avg_utime": item.AvgUtime,
"submit_cnt": strconv.FormatInt(item.SubmitCnt, 10),
}
for state := range stateOrderUnique {
cnt := "0"
ration := "0.00"
if v, exist := item.StateCnt[state]; exist {
cnt = strconv.FormatInt(v.Cnt, 10)
ration = v.Ratio
}
one[fmt.Sprintf("state_%s_cnt", state)] = cnt
one[fmt.Sprintf("state_%s_ratio", state)] = ration
}
res.Rows[i][j] = one
}
}
return
}

View File

@@ -0,0 +1,25 @@
package service
import (
"testing"
"github.com/smartystreets/goconvey/convey"
"go-common/app/admin/main/aegis/model"
)
func TestService_ReportTaskSubmit(t *testing.T) {
convey.Convey("ReportTaskSubmit", t, func(ctx convey.C) {
pm := &model.OptReportSubmit{
BizID: 2,
}
res, err := s.ReportTaskSubmit(cntx, pm)
ctx.So(err, convey.ShouldBeNil)
t.Logf("res.header(%+v)", res.Header)
for _, list := range res.Rows {
for _, item := range list {
t.Logf("item(%+v)", item)
}
}
})
}

View File

@@ -0,0 +1,154 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/business"
"go-common/app/admin/main/aegis/model/net"
"go-common/app/admin/main/aegis/model/resource"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
// TxAddResource .
func (s *Service) TxAddResource(ormTx *gorm.DB, b *resource.Resource, r *resource.Result) (rid int64, err error) {
if rid, err = s.gorm.TxAddResource(ormTx, b, r); err != nil {
log.Error("s.dao.TxAddResource error(%v)", err)
}
return
}
// TxUpdateResource .
func (s *Service) TxUpdateResource(tx *gorm.DB, rid int64, res map[string]interface{}, rscRes *resource.Result) (err error) {
if err = s.gorm.TxUpdateResult(tx, rid, res, rscRes); err != nil {
return
}
return s.gorm.TxUpdateResource(tx, rid, res)
}
// CheckResource .
func (s *Service) CheckResource(c context.Context, opt *model.AddOption) (rid int64, err error) {
r, err := s.gorm.ResByOID(c, opt.BusinessID, opt.OID)
if err != nil || r == nil {
return
}
if r != nil {
rid = r.ID
}
return
}
// syncResource 同步到业务方
func (s *Service) syncResource(c context.Context, opt *model.SubmitOptions, mid int64, restoken *net.TokenPackage) (err error) {
if opt.ExtraData == nil {
opt.ExtraData = make(map[string]interface{})
}
opt.ExtraData["mid"] = mid
if opt.Forbid != nil {
if opt.Forbid.Duration > 0 {
opt.Forbid.Duration = opt.Forbid.Duration * 60 * 60 * 24
}
}
act, ropt, err := s.formatSubmitRequest(c, opt, restoken)
if err != nil || act == nil {
log.Error("syncResource formatSubmitRequest opt(%+v) restoken(%+v) error(%v)", opt, restoken, err)
return
}
// TODO: 偶发错误(接口超时,业务方对应错误码等)重试3次
var codes = s.getConfig(c, opt.BusinessID, business.TypeTempCodes)
for i := 0; i < 3; i++ {
var code int
code, err = s.http.SyncResource(c, act, ropt)
if err == nil {
break
}
if err != nil && code == 0 { //http错误不重试了免得错误日志太多
break
}
if len(codes) > 0 && !strings.Contains(","+codes+",", fmt.Sprintf(",%d,", code)) {
return
}
}
return err
}
// ResourceRes .
func (s *Service) ResourceRes(c context.Context, args *resource.Args) (res *resource.Res, err error) {
if res, err = s.gorm.ResourceRes(c, args.RID); err != nil {
log.Error("s.gorm.ResourceRes error(%v)", err)
}
return
}
// TxDelResource .
func (s *Service) TxDelResource(c context.Context, ormTx *gorm.DB, bizid int64, rids []int64) (delState int, err error) {
delState = -10010
var (
ststr string
)
if ststr = s.getConfig(c, bizid, business.TypeDeleteState); len(ststr) != 0 {
if delState, err = strconv.Atoi(ststr); err != nil {
log.Error("TxDelResource strconv.Atoi error(%v)", err)
return
}
}
err = s.gorm.TxUpdateState(ormTx, rids, delState)
return
}
func (s *Service) formatSubmitRequest(c context.Context, opt *model.SubmitOptions, restoken *net.TokenPackage) (act *model.Action, ropt map[string]interface{}, err error) {
//1. 读取业务回调配置
cfg := s.getConfig(c, opt.BusinessID, business.TypeCallback)
if len(cfg) == 0 {
err = errors.New("empty submit config")
log.Error("FormatSubmitRequest(%d) error(%v)", opt.BusinessID, cfg)
return
}
mapcfg := make(map[string]*model.Action)
if err = json.Unmarshal([]byte(cfg), &mapcfg); err != nil {
log.Error("FormatSubmitRequest(%d) json.Unmarshal error(%v)", opt.BusinessID, cfg)
return
}
var ok bool
//2. 根据流程结果判断,调用哪个回调
if restoken.HitAudit {
act, ok = mapcfg["audit"]
} else {
act, ok = mapcfg["noaudit"]
}
if !ok || act == nil {
err = errors.New("empty submit config[noaudit]")
log.Error("FormatSubmitRequest(%d) error(%v)", opt.BusinessID, cfg)
return
}
ropt = make(map[string]interface{})
ot := reflect.TypeOf(*opt)
ov := reflect.ValueOf(*opt)
for k, v := range act.Params {
model.SubReflect(ot, ov, k, strings.Split(v.Value, "."), v.Default, ropt)
}
//3. 根据回调配置,读取需要的参数进行拼接
for k, v := range restoken.Values {
ropt[k] = v
}
return
}

View File

@@ -0,0 +1,143 @@
package service
import (
"context"
"go-common/app/admin/main/aegis/model/business"
"go-common/app/admin/main/aegis/model/common"
"go-common/app/admin/main/aegis/model/task"
"go-common/library/log"
)
// GetRole .
func (s *Service) GetRole(c context.Context, opt *common.BaseOptions) (uname string, role int8, err error) {
role, uname, err = s.mc.GetRole(c, opt)
if err == nil && role > 0 {
return
}
var (
cfgs map[int64][]int64 //bid:flowids
mngid int64
)
if cfgs = s.getTaskRole(c, opt.BusinessID); err != nil {
return
}
for bid, flows := range cfgs {
exist := false
for _, flowID := range flows {
if flowID == opt.FlowID {
exist = true
break
}
}
if exist {
mngid = bid
break
}
}
log.Info("GetRole %d %d", mngid, opt.UID)
if mngid <= 0 {
return
}
roles, err := s.http.GetRole(c, mngid, opt.UID)
if err != nil {
return
}
for _, item := range roles {
log.Info("GetRole %d %d %+v", mngid, opt.UID, item)
if int8(item.RID) == task.TaskRoleLeader {
role = task.TaskRoleLeader
break
}
if int8(item.RID) == task.TaskRoleMember {
role = task.TaskRoleMember
}
}
log.Info("GetRole %d %d %d", mngid, opt.UID, role)
if opt.Uname == "" {
unames, _ := s.http.GetUnames(c, []int64{opt.UID})
opt.Uname = unames[opt.UID]
}
uname = opt.Uname
s.mc.SetRole(c, opt, role)
return
}
//GetTaskBizFlows uid能查看哪些任务节点
func (s *Service) GetTaskBizFlows(c context.Context, uid int64) (businessID []int64, flows []int64, err error) {
var (
uroles []*task.Role
)
businessID = []int64{}
flows = []int64{}
//用户角色
if uroles, err = s.http.GetUserRoles(c, uid); err != nil {
log.Error("GetTaskBizFlows s.http.GetUserRoles(%d) error(%v)", uid, err)
return
}
//业务与用户角色的映射关系
bizMap := map[int64]int{}
bizs := []int64{}
for _, item := range uroles {
if item == nil || item.BID <= 0 {
continue
}
bizMap[item.BID] = 1
bizs = append(bizs, item.BID)
}
businessID, flows = s.getTaskBiz(c, bizs)
log.Info("checkAccessTask uid(%d) can see business(%+v)", uid, businessID)
return
}
//GetRoleBiz uid能查看哪些业务
func (s *Service) GetRoleBiz(c context.Context, uid int64, role string, noAdmin bool) (businessID []int64, err error) {
var (
uroles []*task.Role
)
businessID = []int64{}
//用户角色
if uroles, err = s.http.GetUserRoles(c, uid); err != nil {
log.Error("GetRoleBiz s.http.GetUserRoles(%d) error(%v)", uid, err)
return
}
//业务与用户角色的映射关系
bizMap := map[int64]int{}
for _, item := range uroles {
if item == nil || item.BID <= 0 || item.RID <= 0 {
continue
}
roles, bizID := s.getBizRole(c, item.BID)
log.Info("GetRoleBiz s.getBizRole roles(%+v) bizID(%d)", roles, bizID)
if len(roles) == 0 || bizID <= 0 {
continue
}
//没有role, 走bid, 有role走role过滤; 然后在走noadmin过滤
if (role != "" && roles[role] != item.RID) || (noAdmin && roles[business.BizBIDAdmin] == item.RID) || bizMap[bizID] > 0 {
continue
}
businessID = append(businessID, bizID)
bizMap[bizID] = 1
}
log.Info("uid(%d) can see biz(%v) as role(%s) noadmin(%v)", uid, businessID, role, noAdmin)
return
}
//FlushRole .
func (s *Service) FlushRole(c context.Context, BizID, flowID int64, uids []int64) (err error) {
return s.mc.DelRole(c, BizID, flowID, uids)
}

View File

@@ -0,0 +1,17 @@
package service
import (
"github.com/smartystreets/goconvey/convey"
"testing"
)
func TestServiceGetRoleBiz(t *testing.T) {
convey.Convey("GetRoleBiz", t, func(ctx convey.C) {
result, err := s.GetRoleBiz(cntx, 421, "leader", true)
t.Logf("result(%+v)", result)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,166 @@
package service
import (
"context"
"fmt"
"strings"
"time"
"go-common/app/admin/main/aegis/conf"
daoOrm "go-common/app/admin/main/aegis/dao/gorm"
daoHttp "go-common/app/admin/main/aegis/dao/http"
daoMc "go-common/app/admin/main/aegis/dao/mc"
daoMysql "go-common/app/admin/main/aegis/dao/mysql"
daoRedis "go-common/app/admin/main/aegis/dao/redis"
daoRpc "go-common/app/admin/main/aegis/dao/rpc"
"go-common/app/admin/main/aegis/model/common"
"go-common/app/admin/main/aegis/model/middleware"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/queue/databus"
"go-common/library/sync/pipeline/fanout"
)
// Service struct
type Service struct {
c *conf.Config
gorm *daoOrm.Dao
http *daoHttp.Dao
mc *daoMc.Dao
mysql *daoMysql.Dao
redis *daoRedis.Dao
rpc *daoRpc.Dao
aegisPub *databus.Databus
// cache
groupCache map[int64]*common.Group
bizCfgCache map[string]string
bizRoleCache map[int64]map[string]int64 //business:cfg_name:role_id/manager_id映射
taskRoleCache map[int64]map[int64][]int64 //business:manager_id:flow_id映射
netCacheCh chan map[string]string
bizMiddlewareCache map[int64][]*middleware.Aggregate
netCache map[int64]*net.Net
tokenCache map[int64]*net.Token
flowCache map[int64]*net.Flow
transitionCache map[int64]*net.Transition
bindCache map[int64]*net.TokenBind
//async
async *fanout.Fanout
//gray
gray map[int64][][]common.GrayField
}
// Cache .
func (s *Service) Cache() map[string]interface{} {
return map[string]interface{}{
"groupCache": s.groupCache,
"bizCfgCache": s.bizCfgCache,
"bizRoleCache": s.bizRoleCache,
"taskRoleCache": s.taskRoleCache,
"bizMiddlewareCache": s.bizMiddlewareCache,
}
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
// dao
gorm: daoOrm.New(c),
http: daoHttp.New(c),
mc: daoMc.New(c),
mysql: daoMysql.New(c),
redis: daoRedis.New(c),
rpc: daoRpc.New(c),
aegisPub: databus.New(c.AegisPub),
// cache
netCache: map[int64]*net.Net{},
tokenCache: map[int64]*net.Token{},
flowCache: map[int64]*net.Flow{},
transitionCache: map[int64]*net.Transition{},
bindCache: map[int64]*net.TokenBind{},
//gray
gray: loadGray(c),
netCacheCh: make(chan map[string]string, 10240),
//async
async: fanout.New("async", fanout.Worker(10), fanout.Buffer(10240)),
}
go s.cacheProc()
go s.setNetCache()
return s
}
// Debug .
func (s *Service) Debug() string {
return s.c.Debug
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.mysql.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.async.Close()
s.gorm.Close()
s.mysql.Close()
s.mc.Close()
s.redis.Close()
s.aegisPub.Close()
}
func (s *Service) cacheProc() {
for {
s.syncUpCache(context.Background())
s.syncBizCache(context.Background())
time.Sleep(10 * time.Minute)
}
}
//IsAdmin uid是否为管理员
func (s *Service) IsAdmin(uid int64) bool {
return len(s.c.Admin) > 0 && strings.Contains(","+s.c.Admin+",", fmt.Sprintf(",%d,", uid))
}
func loadGray(c *conf.Config) (gray map[int64][][]common.GrayField) {
if c.Gray == nil {
gray = make(map[int64][][]common.GrayField)
return
}
gray = make(map[int64][][]common.GrayField)
for _, biz := range c.Gray.Biz {
ones := [][]common.GrayField{}
for _, opts := range biz.Options {
options := []common.GrayField{}
for _, field := range opts.Fields {
field.Name = strings.TrimSpace(field.Name)
field.Value = strings.TrimSpace(field.Value)
if field.Value == "" || field.Name == "" {
continue
}
options = append(options, common.GrayField{
Name: field.Name,
Value: fmt.Sprintf(",%s,", field.Value),
})
}
ones = append(ones, options)
}
gray[biz.BusinessID] = ones
}
return
}
//GetMiddlewareCache get cache by bizid
func (s *Service) GetMiddlewareCache(bizid int64) []*middleware.Aggregate {
return s.bizMiddlewareCache[bizid]
}

View File

@@ -0,0 +1,94 @@
package service
import (
"context"
"flag"
"testing"
"go-common/app/admin/main/aegis/conf"
"github.com/smartystreets/goconvey/convey"
)
var (
s *Service
cntx context.Context
)
func init() {
flag.Set("conf", "../cmd/aegis-admin.toml")
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
s = New(conf.Conf)
cntx = context.Background()
}
/*
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.archive.aegis-admin")
flag.Set("conf_token", "cad913269be022e1eb8c45a8d5408d78")
flag.Set("tree_id", "60977")
flag.Set("conf_version", "1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/local.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
conf.Conf.AegisPub = &databus.Config{
// Key: "0PvKGhAqDvsK7zitmS8t",
// Secret: "0PvKGhAqDvsK7zitmS8u",
// Group: "databus_test_group",
// Topic: "databus_test_topic",
Key: "dbe67e6a4c36f877",
Secret: "8c775ea242caa367ba5c876c04576571",
Group: "Test1-MainCommonArch-P",
Topic: "test1",
Action: "pub",
Name: "databus",
Proto: "tcp",
// Addr: "172.16.33.158:6205",
Addr: "172.18.33.50:6205",
Active: 10,
Idle: 5,
DialTimeout: xtime.Duration(time.Second),
WriteTimeout: xtime.Duration(time.Second),
ReadTimeout: xtime.Duration(time.Second),
IdleTimeout: xtime.Duration(time.Minute),
}
s = New(conf.Conf)
cntx = context.Background()
m.Run()
os.Exit(0)
}
*/
func TestServicePing(t *testing.T) {
var (
c = context.TODO()
)
convey.Convey("Ping", t, func(ctx convey.C) {
err := s.Ping(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceClose(t *testing.T) {
convey.Convey("Close", t, func(ctx convey.C) {
//s.Close()
//ctx.Convey("No return values", func(ctx convey.C) {
//})
})
}

View File

@@ -0,0 +1,55 @@
package service
import (
"context"
"go-common/app/admin/main/aegis/model/net"
"go-common/app/admin/main/aegis/model/svg"
)
//NetSVG 绘制网的svg图
func (s *Service) NetSVG(c context.Context, netid int64) (nv *svg.NetView, err error) {
flows, err := s.gorm.FlowsByNet(c, []int64{netid})
if err != nil {
return
}
dirs, err := s.gorm.DirectionByNet(c, netid)
if err != nil {
return
}
trans, err := s.gorm.TranByNet(c, netid, true)
if err != nil {
return
}
var binds []*net.TokenBind
for _, flow := range flows {
abs, _, _ := s.fetchOldBindAndLog(c, flow.ID, []int8{1})
for _, bind := range abs {
binds = append(binds, bind)
}
}
for _, tran := range trans {
abs, _, _ := s.fetchOldBindAndLog(c, tran.ID, []int8{2})
for _, bind := range abs {
binds = append(binds, bind)
}
}
tbds, err := s.tokenBindDetail(c, binds)
if err != nil {
return
}
dot := svg.NewDot()
dot.StartDot().AddTokenBinds(tbds...).
AddFlow(flows...).
AddTransitions(trans...).
AddDirections(dirs...).
End()
nv = svg.NewNetView()
nv.SetDot(dot)
return
}

View File

@@ -0,0 +1,89 @@
package service
import (
"context"
"encoding/json"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/task"
"go-common/library/xstr"
)
// QueryConfigs .
func (s *Service) QueryConfigs(c context.Context, queryParams *task.QueryParams) (ls []*task.Config, count int64, err error) {
ls, count, err = s.gorm.QueryConfigs(c, queryParams)
if err != nil || count == 0 {
return
}
switch queryParams.ConfType {
case task.TaskConfigAssign:
case task.TaskConfigRangeWeight:
case task.TaskConfigEqualWeight:
if len(queryParams.IDFilter) > 0 || len(queryParams.TypeFilter) > 0 {
confItem := &task.EqualWeightConfig{}
lsres := []*task.Config{}
for _, item := range ls {
if err = json.Unmarshal([]byte(item.ConfJSON), confItem); err != nil {
continue
}
if len(queryParams.IDFilter) > 0 {
ids1, _ := xstr.SplitInts(confItem.IDs)
ids2, _ := xstr.SplitInts(queryParams.IDFilter)
if len(mergeSlice(ids1, ids2)) == 0 {
continue
}
}
if len(queryParams.TypeFilter) > 0 {
ids, _ := xstr.SplitInts(queryParams.TypeFilter)
if len(mergeSlice(ids, []int64{int64(confItem.Type)})) == 0 {
continue
}
}
lsres = append(lsres, item)
}
return lsres, count, err
}
}
return ls, count, err
}
// UpdateConfig .
func (s *Service) UpdateConfig(c context.Context, config *task.Config) (err error) {
return s.gorm.UpdateConfig(c, config)
}
// SetStateConfig .
func (s *Service) SetStateConfig(c context.Context, id int64, state int8) (err error) {
return s.gorm.SetStateConfig(c, id, state)
}
// AddConfig .
func (s *Service) AddConfig(c context.Context, config *task.Config, confJSON interface{}) (err error) {
return s.gorm.AddConfig(c, config, confJSON)
}
// DeleteConfig .
func (s *Service) DeleteConfig(c context.Context, id int64) (err error) {
return s.gorm.DeleteConfig(c, id)
}
// WeightLog .
func (s *Service) WeightLog(c context.Context, taskid int64, pn, ps int) (ls []*model.WeightLog, count int, err error) {
ls, count, err = s.searchWeightLog(c, taskid, pn, ps)
if err != nil || len(ls) == 0 {
return
}
var Name string
if mid := ls[0].Mid; mid > 0 {
if info, _ := s.rpc.Info3(c, mid); info != nil {
Name = info.Name
}
}
for _, item := range ls {
item.MemName = Name
}
return
}

View File

@@ -0,0 +1,17 @@
package service
import (
"github.com/smartystreets/goconvey/convey"
"testing"
)
func TestServiceWeightLog(t *testing.T) {
convey.Convey("WeightLog", t, func(ctx convey.C) {
result, cnt, err := s.WeightLog(cntx, 49, 1, 4)
t.Logf("cnt(%d) error(%v)", cnt, err)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,135 @@
package service
import (
"context"
"go-common/app/admin/main/aegis/model/common"
"go-common/app/admin/main/aegis/model/task"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
// On 上线
func (s *Service) On(c context.Context, opt *common.BaseOptions) (err error) {
if err = s.mc.ConsumerOn(c, opt); err != nil {
log.Error("s.mc.ConsumerOn mc错误(%v)", err)
err = nil
}
if err = s.mysql.ConsumerOn(c, opt); err != nil {
return
}
go s.sendTaskConsumerLog(context.TODO(), "on", opt)
return
}
// Off 离线
func (s *Service) Off(c context.Context, opt *common.BaseOptions) (err error) {
if err = s.mc.ConsumerOff(c, opt); err != nil {
log.Error("s.mc.ConsumerOff mc错误(%v)", err)
err = nil
}
if err = s.mysql.ConsumerOff(c, opt); err != nil {
return
}
go s.sendTaskConsumerLog(context.TODO(), "off", opt)
return
}
// KickOut 踢出
func (s *Service) KickOut(c context.Context, opt *common.BaseOptions, kickuid int64) (err error) {
opt.UID = kickuid
unames, _ := s.http.GetUnames(c, []int64{kickuid})
opt.Uname = unames[kickuid]
return s.Off(c, opt)
}
// Watcher 监控管理
func (s *Service) Watcher(c context.Context, bizid, flowid int64, role int8) (watchers []*task.WatchItem, err error) {
// 从数据库拿出24小时内有变化的或者依然在线的
var wis []*task.WatchItem
if wis, err = s.mysql.ConsumerStat(c, bizid, flowid); err != nil {
return
}
bopt := &common.BaseOptions{
BusinessID: bizid,
FlowID: flowid,
}
var (
onuids, offuids []int64
inxmap = make(map[int64]int)
)
for _, item := range wis {
bopt.UID = item.UID
bopt.Uname = ""
uname, urole, err := s.GetRole(c, bopt)
// 组员列表展示 组员 + 角色获取失败的 + 非任务角色(例如平台管理员)
if err != nil || role != urole || len(uname) == 0 {
if role == task.TaskRoleLeader || urole == task.TaskRoleLeader {
continue
}
}
isOn, _ := s.mc.IsConsumerOn(c, bopt)
item.Role = urole
item.IsOnLine = isOn
item.Uname = uname
if item.IsOnLine {
onuids = append(onuids, item.UID)
item.LastOn = item.Mtime.Format("2006-01-02 15:04:05")
} else {
offuids = append(offuids, item.UID)
item.LastOff = item.Mtime.Format("2006-01-02 15:04:05")
}
inxmap[item.UID] = len(watchers)
watchers = append(watchers, item)
}
// 补充laston 或者 lastoff
wg, ctx := errgroup.WithContext(c)
if len(onuids) > 0 {
wg.Go(func() error {
at, err := s.searchConsumerLog(ctx, bizid, flowid, []string{"off", "kickout"}, onuids, len(onuids))
if err == nil {
for uid, ctime := range at {
watchers[inxmap[uid]].LastOff = ctime
}
}
return err
})
}
if len(offuids) > 0 {
wg.Go(func() error {
at, err := s.searchConsumerLog(ctx, bizid, flowid, []string{"on"}, offuids, len(offuids))
if err == nil {
for uid, ctime := range at {
watchers[inxmap[uid]].LastOn = ctime
}
}
return err
})
}
wg.Go(func() error {
trans := func(c context.Context, ids []int64) (map[int64][]interface{}, error) {
return s.MemberStats(c, bizid, flowid, ids)
}
return s.mulIDtoName(ctx, watchers, trans, "UID", "Count", "CompleteRate", "PassRate", "AvgUT")
})
wg.Wait()
return
}
// IsOn .
func (s *Service) IsOn(c context.Context, opt *common.BaseOptions) bool {
on, err := s.mc.IsConsumerOn(c, opt)
if err != nil {
log.Error("s.mc.ConsumerOff mc错误(%v)", err)
if on, err = s.mysql.IsConsumerOn(c, opt); err != nil {
log.Error("s.mysql.ConsumerOff mysql错误(%v)", err)
}
}
return on
}

View File

@@ -0,0 +1,438 @@
package service
import (
"context"
"errors"
"time"
"go-common/app/admin/main/aegis/model/common"
taskmod "go-common/app/admin/main/aegis/model/task"
"go-common/library/cache/redis"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
//ERROR
var (
ErrTaskMiss = errors.New("task miss")
)
// NextTask 下一个任务
func (s *Service) NextTask(c context.Context, opt *taskmod.NextOptions) (tasks []*taskmod.Task, count int64, err error) {
log.Info("task-NextTask opt(%+v)", opt)
if count, err = s.countPersonalTask(c, &opt.BaseOptions, opt.NoCache); err != nil {
return
}
if count < opt.DispatchCount {
if count, err = s.syncSeize(c, opt); err != nil {
return
}
}
/* 去掉异步抢占
else if count < opt.SeizeCount {
s.asyncSeize(opt)
}
*/
if count == 0 {
return
}
return s.dispatch(c, opt)
}
// ListTasks 实时列表,停滞列表,延迟列表
func (s *Service) ListTasks(c context.Context, opt *taskmod.ListOptions) (tasks []*taskmod.Task, count int64, err error) {
switch opt.State {
case 1: // 实时任务,从redis取出,在数据库校验
tasks, count, err = s.listUnseized(c, opt)
case 2: // 停滞任务,组员的直接从redis取。组长的从数据库取idredis取任务
tasks, count, err = s.listMyTasks(c, "seized", opt)
case 3: // 延迟任务,组员的直接从redis取。组长的从数据库取idredis取任务
tasks, count, err = s.listMyTasks(c, "delayd", opt)
case 4: // 指派停滞任务,从数据库取idredis取任务
tasks, count, err = s.listMyTasks(c, "assignd", opt)
default: // 所有未完成任务
}
if err != nil {
tasks, count, err = s.mysql.ListTasks(c, opt)
}
opt.Total = int(count)
return
}
// Task 直接读取某个任务
func (s *Service) Task(c context.Context, tid int64) (task *taskmod.Task, err error) {
return s.mysql.TaskFromDB(c, tid)
}
// TxSubmitTask 提交任务
func (s *Service) TxSubmitTask(c context.Context, ormTx *gorm.DB, opt *common.BaseOptions, state int8) (ostate int8, otaskid, ouid int64, err error) {
var (
t *taskmod.Task
rows int64
)
// 根据rid,flowid检索最新的未完成任务
if t, err = s.gorm.TaskByRID(c, opt.RID, opt.FlowID); err != nil || t == nil || t.ID == 0 {
log.Warn("TaskByRID(%d,%d) miss(%v)", opt.RID, opt.FlowID, err)
t, err = s.gorm.TaskByRID(c, opt.RID, 0)
}
// TODO 先默认一个资源只在一个flow下分发解决目前存在flow状态与task状态不同步
if err != nil || t == nil || t.ID == 0 {
log.Warn("s.gorm.TaskByRID(%d,%d) miss(%v)", opt.RID, 0, err)
err = nil
return
}
ostate = t.State
ouid = t.UID
otaskid = t.ID
var utime uint64
if !t.Gtime.Time().IsZero() {
utime = uint64(time.Since(t.Gtime.Time()).Seconds())
}
subopt := &taskmod.SubmitOptions{
BaseOptions: *opt,
TaskID: t.ID,
OldUID: t.UID,
Utime: utime,
OldState: t.State,
}
// 1. 改数据库
if rows, err = s.gorm.TxSubmit(ormTx, subopt, state); err != nil {
return
}
if rows != 1 {
err = ecode.NothingFound
log.Error("Submit (%v) error(%v)", opt, err)
return
}
return
}
func (s *Service) submitTaskCache(c context.Context, opt *common.BaseOptions, ostate int8, taskid, ouid int64) {
log.Info("SubmitTaskCache opt(%+v) ostate(%d) taskid(%d) ouid(%d)", opt, ostate, taskid, ouid)
optc := &common.BaseOptions{
BusinessID: opt.BusinessID,
FlowID: opt.FlowID,
UID: ouid,
}
if ostate == taskmod.TaskStateDelay {
s.redis.RemoveDelayTask(c, optc, taskid)
return
}
s.redis.RemovePersonalTask(c, optc, taskid)
}
// Delay 延迟任务
func (s *Service) Delay(c context.Context, opt *taskmod.DelayOptions) (err error) {
var (
taskmod *taskmod.Task
rows int64
)
if taskmod, err = s.mysql.TaskFromDB(c, opt.TaskID); err != nil || taskmod == nil {
return
}
if !s.checkDelayOption(c, opt, taskmod) {
log.Error("checkDelayOption error opt(%+v) taskmod(%+v)", opt, taskmod)
return ecode.AegisTaskFinish
}
if rows, err = s.mysql.Delay(c, opt); err != nil {
return
}
if rows != 1 {
err = ecode.AegisTaskFinish
log.Error("Submit (%v) error(%v)", opt, err)
return
}
if err = s.redis.RemovePersonalTask(c, &opt.BaseOptions, opt.TaskID); err != nil {
return
}
s.redis.PushDelayTask(c, &opt.BaseOptions, opt.TaskID)
return
}
// Release 释放任务
func (s *Service) Release(c context.Context, opt *common.BaseOptions, delay bool) (rows int64, err error) {
if rows, err = s.mysql.Release(c, opt, delay); err != nil {
return
}
//err = s.redis.Release(c, opt, delay)
return
}
// MaxWeight 当前最高权重
func (s *Service) MaxWeight(c context.Context, opt *common.BaseOptions) (max int64, err error) {
return s.gorm.MaxWeight(c, opt.BusinessID, opt.FlowID)
}
// UnDoStat undo stat
func (s *Service) UnDoStat(c context.Context, opt *common.BaseOptions) (stat *taskmod.UnDOStat, err error) {
return s.gorm.UndoStat(c, opt.BusinessID, opt.FlowID, opt.UID)
}
// TaskStat task stat
func (s *Service) TaskStat(c context.Context, opt *common.BaseOptions) (stat *taskmod.Stat, err error) {
return s.gorm.TaskStat(c, opt.BusinessID, opt.FlowID, opt.UID)
}
func (s *Service) countPersonalTask(c context.Context, opt *common.BaseOptions, nocache bool) (count int64, err error) {
log.Info("task-countPersonalTask opt(%+v) nocache(%v)", opt, nocache)
defer func() { log.Info("task-countPersonalTask count(%d) err(%v)", count, err) }()
if nocache {
return s.mysql.CountPersonal(c, opt)
}
if count, err = s.redis.CountPersonalTask(c, opt); err != nil {
// redis 挂了
if count, err = s.mysql.CountPersonal(c, opt); err != nil {
return
}
}
return
}
func (s *Service) syncSeize(c context.Context, opt *taskmod.NextOptions) (count int64, err error) {
return s.seize(c, opt)
}
func (s *Service) seize(c context.Context, opt *taskmod.NextOptions) (count int64, err error) {
log.Info("task-seize opt(%+v)", opt)
defer func() { log.Info("task-seize count(%d) err(%v)", count, err) }()
var (
hitids, missids []int64
others map[int64]int64
)
// TODO: 抢占任务要根据用户是否在线,处理任务指派
if opt.NoCache {
hitids, err = s.mysql.QueryForSeize(c, opt.BusinessID, opt.FlowID, opt.UID, opt.SeizeCount)
} else {
hitids, missids, others, err = s.redis.SeizeTask(c, opt.BusinessID, opt.FlowID, opt.UID, opt.SeizeCount)
if err != nil {
hitids, err = s.mysql.QueryForSeize(c, opt.BusinessID, opt.FlowID, opt.UID, opt.SeizeCount)
}
}
if err != nil {
return
}
log.Info("seize uid(%d) hitids(%v), missids(%v), others(%v)", opt.UID, hitids, missids, others)
if !opt.NoCache && len(missids) > 0 {
log.Error("seize uid(%d) missids(%v)", opt.UID, missids)
for _, id := range missids {
if err = s.syncTask(c, id); err != nil {
s.redis.RemovePublicTask(c, &opt.BaseOptions, id)
}
}
}
if len(hitids) > 0 {
log.Info("seize uid(%d) hitids(%v)", opt.UID, hitids)
mhits := make(map[int64]int64)
for _, id := range hitids {
mhits[id] = opt.UID
}
if count, err = s.mysql.Seize(c, mhits); err != nil || count == 0 {
return
}
return
}
return
}
func (s *Service) dispatch(c context.Context, opt *taskmod.NextOptions) (tasks []*taskmod.Task, count int64, err error) {
log.Info("task-dispatch opt(%+v)", opt)
defer func() { log.Info("task-dispatch tasks(%+v) count(%d) err(%v)", tasks, count, err) }()
listopt := &taskmod.ListOptions{
BaseOptions: opt.BaseOptions,
Pager: common.Pager{
Pn: 1,
Ps: int(opt.DispatchCount),
}}
tasks, count, err = s.calibur(c, listopt, s.redis.RangePersonalTask, s.mysql.DispatchByID, s.redis.RemovePersonalTask)
if err != nil {
tasks, count, err = s.mysql.DBDispatch(c, opt)
}
return
}
func (s *Service) syncTask(c context.Context, taskID int64) (err error) {
var task *taskmod.Task
if task, err = s.mysql.TaskFromDB(c, taskID); err != nil || task == nil {
return ErrTaskMiss
}
var option = &common.BaseOptions{
BusinessID: task.BusinessID,
FlowID: task.FlowID,
UID: task.UID,
}
s.redis.SetTask(c, task)
switch task.State {
case taskmod.TaskStateInit:
s.redis.PushPublicTask(c, task)
case taskmod.TaskStateDispatch:
s.redis.RemovePublicTask(c, option, task.ID)
s.redis.PushPersonalTask(c, option, task.ID)
case taskmod.TaskStateDelay:
s.redis.RemovePublicTask(c, option, task.ID)
s.redis.PushDelayTask(c, option, task.ID)
default:
s.redis.RemovePublicTask(c, option, task.ID)
}
return
}
func (s *Service) listUnseized(c context.Context, opt *taskmod.ListOptions) (tasks []*taskmod.Task, count int64, err error) {
return s.calibur(c, opt, s.redis.RangePublicTask, s.mysql.ListCheckUnSeized, s.redis.RemovePublicTask)
}
func (s *Service) listMyTasks(c context.Context, ltp string, opt *taskmod.ListOptions) (tasks []*taskmod.Task, count int64, err error) {
if !opt.BisLeader {
if ltp == "delayd" {
return s.calibur(c, opt, s.redis.RangeDealyTask, s.mysql.ListCheckDelay, s.redis.RemoveDelayTask)
}
if ltp == "seized" {
return s.calibur(c, opt, s.redis.RangePersonalTask, s.mysql.ListCheckSeized, s.redis.RemovePersonalTask)
}
}
if opt.BisLeader {
opt.UID = 0
}
var ids []int64
switch ltp {
case "delayd":
ids, count, err = s.gorm.TaskListDelayd(c, opt)
case "seized":
ids, count, err = s.gorm.TaskListSeized(c, opt)
case "assignd":
ids, count, err = s.gorm.TaskListAssignd(c, opt)
}
if err != nil || len(ids) == 0 {
return
}
if tasks, err = s.redis.GetTask(c, ids); err != nil {
err = redis.ErrNil
}
return
}
func (s *Service) calibur(c context.Context, opt *taskmod.ListOptions, rfunc taskmod.RangeFunc, lfunc taskmod.ListFuncDB, remove taskmod.RemoveFunc) (taskmods []*taskmod.Task, count int64, err error) {
var (
hitids, missids []int64
missmap map[int64]struct{}
mtaskmods map[int64]*taskmod.Task
)
mtaskmods, count, hitids, missids, err = rfunc(c, opt)
log.Info("calibur(%+v) rfunc count(%d) hitids(%v) missids(%v)", opt, count, hitids, missids)
if err != nil {
return
}
if len(missids) > 0 {
for _, id := range missids {
if err = s.syncTask(c, id); err != nil {
log.Error("syncTask error(%v)", err)
remove(c, &opt.BaseOptions, id)
}
}
}
if len(hitids) > 0 {
if missmap, err = lfunc(c, mtaskmods, hitids, opt.UID); err != nil {
log.Error("calibur lfunc error(%v)", err)
return
}
if len(missmap) > 0 {
log.Info("calibur personal任务移除%v", missmap)
for id := range missmap {
remove(c, &opt.BaseOptions, id)
}
}
}
for _, id := range hitids {
if _, ok := missmap[id]; ok && opt.Action != "release" {
delete(mtaskmods, id)
} else {
taskmods = append(taskmods, mtaskmods[id])
}
}
return
}
/*
func (s *Service) checkSubmitOption(c context.Context, opt *taskmod.SubmitOptions, task *taskmod.Task) bool {
opt.OldState = task.State
opt.OldUID = task.UID
// 1. 组员只能处理自己的延迟任务
if task.State == taskmod.TaskStateDelay {
if opt.BisLeader {
return true
}
if task.UID != opt.UID {
return false
}
}
if task.State == taskmod.TaskStateDispatch && opt.UID == task.UID {
opt.Utime = uint64(time.Since(task.Gtime.Time()).Seconds())
return true
}
return false
}
*/
func (s *Service) checkDelayOption(c context.Context, opt *taskmod.DelayOptions, task *taskmod.Task) bool {
if task.State == taskmod.TaskStateDispatch && task.UID == opt.UID {
return true
}
return false
}
func (s *Service) syncUpCache(c context.Context) (err error) {
if s.Debug() == "local" {
return
}
upGroup := make(map[int64]*common.Group)
upgs, err := s.rpc.UpGroups(c)
if err != nil || upgs == nil {
return
}
for gid, upg := range upgs {
if _, ok := upGroup[gid]; !ok {
upGroup[gid] = &common.Group{
ID: gid,
Name: upg.Name,
Note: upg.Note,
Tag: upg.Tag,
FontColor: upg.FontColor,
BgColor: upg.BgColor,
}
log.Info("groupCache upg(%+v) upGroup(%+v)", upg, upGroup[gid])
}
}
s.groupCache = upGroup
log.Info("groupCache(%+v)", s.groupCache)
return
}

View File

@@ -0,0 +1,133 @@
package service
import (
"context"
"fmt"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
)
//GetTokenList .
func (s *Service) GetTokenList(c context.Context, pm *net.ListTokenParam) (result *net.ListTokenRes, err error) {
var (
unames map[int64]string
)
if result, err = s.gorm.TokenListWithPager(c, pm); err != nil {
return
}
if len(result.Result) == 0 {
return
}
//get username
uid := []int64{}
for _, item := range result.Result {
if item.UID > 0 {
uid = append(uid, item.UID)
}
}
if len(uid) == 0 {
return
}
if unames, err = s.http.GetUnames(c, uid); err != nil || len(unames) == 0 {
log.Error("GetTokenList s.http.GetUnames error(%v) or empty uid(%+v)", err, uid)
err = nil
}
for _, item := range result.Result {
item.Username = unames[item.UID]
}
return
}
//TokenGroupByType .
func (s *Service) TokenGroupByType(c context.Context, netID int64) (result map[string][]*net.Token, err error) {
var (
list []*net.Token
)
result = make(map[string][]*net.Token)
if list, err = s.gorm.TokenList(c, []int64{netID}, nil, "", true); err != nil || len(list) == 0 {
return
}
for _, item := range list {
typeDesc := net.TokenValueTypeDesc[item.Type]
if _, exist := result[typeDesc]; !exist {
result[typeDesc] = []*net.Token{item}
continue
}
result[typeDesc] = append(result[typeDesc], item)
}
return
}
//TokenByName .
func (s *Service) TokenByName(c context.Context, businessID int64, name string) (result map[string]string, err error) {
var (
netID []int64
tokenList []*net.Token
)
result = map[string]string{}
if netID, err = s.netIDByBusiness(c, businessID); err != nil {
log.Error("TokenByName s.netIDByBusiness(%d) error(%v)", businessID, err)
return
}
if len(netID) == 0 {
return
}
if tokenList, err = s.gorm.TokenList(c, netID, nil, name, true); err != nil {
log.Error("TokenByName s.gorm.TokenList(%v, %s) error(%v) businessid(%d)", netID, name, err, businessID)
return
}
for _, item := range tokenList {
result[item.Value] = item.ChName
}
return
}
//ShowToken .
func (s *Service) ShowToken(c context.Context, id int64) (n *net.Token, err error) {
return s.gorm.TokenByID(c, id)
}
func (s *Service) checkTokenUnique(c context.Context, netID int64, name string, compare int8, value string) (err error, msg string) {
var exist *net.Token
if exist, err = s.gorm.TokenByUnique(c, netID, name, compare, value); err != nil {
log.Error("checkTokenUnique s.gorm.TokenByUnique(%d,%s,%d,%s) error(%v)", netID, name, compare, value, err)
return
}
if exist != nil {
err = ecode.AegisUniqueAlreadyExist
msg = fmt.Sprintf(ecode.AegisUniqueAlreadyExist.Message(), "令牌",
fmt.Sprintf("%s %s %s", name, net.GetTokenCompare(compare), value))
}
return
}
//AddToken .
func (s *Service) AddToken(c context.Context, t *net.Token) (id int64, err error, msg string) {
if err, msg = s.checkTokenUnique(c, t.NetID, t.Name, t.Compare, t.Value); err != nil {
return
}
if err = s.gorm.AddItem(c, nil, t); err != nil {
return
}
id = t.ID
//日志
oper := &model.NetConfOper{
OID: t.ID,
Action: model.LogNetActionNew,
UID: t.UID,
NetID: t.NetID,
ChName: t.ChName,
Diff: []string{t.FormatLog()},
}
s.sendNetConfLog(c, model.LogTypeTokenConf, oper)
return
}

View File

@@ -0,0 +1,431 @@
package service
import (
"context"
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/jinzhu/gorm"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
func (s *Service) checkBindToken(c context.Context, tokenID []int64) (tokenMap map[string]*net.Token, err error, msg string) {
var (
tokens []*net.Token
)
tokenMap = map[string]*net.Token{}
if len(tokenID) == 0 {
return
}
if tokens, err = s.gorm.Tokens(c, tokenID); err != nil {
log.Error("checkBindToken s.gorm.Tokens error(%v)", err)
return
}
for _, tk := range tokens {
if !tk.IsAssign() {
err = ecode.AegisTokenNotAssign
msg = fmt.Sprintf(ecode.AegisTokenNotAssign.Message(), tk.ChName)
return
}
id := strconv.FormatInt(tk.ID, 10)
tokenMap[id] = tk
}
if len(tokenID) == len(tokens) {
return
}
for _, id := range tokenID {
if _, exist := tokenMap[strconv.FormatInt(id, 10)]; !exist {
err = ecode.AegisTokenNotFound
msg = fmt.Sprintf("id=%d的令牌 %s", id, ecode.AegisTokenNotAssign.Message())
return
}
}
return
}
func (s *Service) fetchOldBindAndLog(c context.Context, elementID int64, tp []int8) (all map[int64]*net.TokenBind, availableLog []string, err error) {
var (
oldBindMap map[int64][]*net.TokenBind
oldBindToken map[int64][]*net.Token
)
all = map[int64]*net.TokenBind{}
availableLog = []string{}
if oldBindMap, err = s.gorm.TokenBindByElement(c, []int64{elementID}, tp, false); err != nil {
log.Error("fetchOldBindAndLog s.gorm.TokenBindByElement error(%v)", err)
return
}
oldBindAvailable := []*net.TokenBind{}
allBindMap := map[int64]*net.TokenBind{}
for _, item := range oldBindMap[elementID] {
allBindMap[item.ID] = item
oldBindAvailable = append(oldBindAvailable, item)
}
if oldBindToken, err = s.bindTokens(c, oldBindAvailable); err != nil {
log.Error("fetchOldBindAndLog s.bindTokens error(%v)", err)
return
}
for bindID, items := range oldBindToken {
sub := []string{}
for _, item := range items {
sub = append(sub, item.FormatLog())
}
chname := ""
tp := int8(0)
if allBindMap[bindID] != nil {
chname = allBindMap[bindID].ChName
tp = allBindMap[bindID].Type
}
availableLog = append(availableLog, fmt.Sprintf(net.BindLogTemp, chname, tp, strings.Join(sub, ",")))
}
all = allBindMap
return
}
func (s *Service) compareFlowBind(c context.Context, tx *gorm.DB, flowID int64, tokenID []int64, isUpdate bool) (diff string, changed []int64, err error, msg string) {
var (
tokenMap map[string]*net.Token
oldBindAll = map[int64]*net.TokenBind{}
newFormatLog = []string{}
oldFormatLog = []string{}
disable = time.Now()
recovered = net.Recovered
existTokenID = map[string]int{}
)
log.Info("compareFlowBind start flow(%d) tokenid(%+v) isUpdate(%v)", flowID, tokenID, isUpdate)
//新绑定查询检查是否为赋值语句token & token是否存在
if len(tokenID) > 0 {
if tokenMap, err, msg = s.checkBindToken(c, tokenID); err != nil {
log.Error("compareFlowBind s.checkBindToken error(%v)", err)
return
}
for _, item := range tokenMap {
newFormatLog = append(newFormatLog, fmt.Sprintf(net.BindLogTemp, item.ChName, item.Type, item.FormatLog()))
}
}
//获取现有所有绑定和可用绑定
if isUpdate {
if oldBindAll, oldFormatLog, err = s.fetchOldBindAndLog(c, flowID, []int8{net.BindTypeFlow}); err != nil {
log.Error("compareFlowBind s.fetchOldBindAndLog error(%v)", err)
return
}
}
if len(tokenMap) == 0 && len(oldBindAll) == 0 {
return
}
//从旧的中过滤新的
for _, item := range oldBindAll {
if _, exist := tokenMap[item.TokenID]; exist {
existTokenID[item.TokenID] = 1
if item.IsAvailable() {
continue
}
item.DisableTime = recovered
} else {
item.DisableTime = disable
}
if err = s.gorm.UpdateFields(c, tx, net.TableTokenBind, item.ID, map[string]interface{}{"disable_time": item.DisableTime}); err != nil {
log.Error("compareFlowBind s.gorm.UpdateFields error(%v)", err)
return
}
changed = append(changed, item.ID)
}
//从新的中过滤旧的
for tokenID, tk := range tokenMap {
if _, exist := existTokenID[tokenID]; exist {
continue
}
nw := &net.TokenBind{TokenID: tokenID, ChName: tk.ChName, ElementID: flowID, Type: net.BindTypeFlow}
if err = s.gorm.AddItem(c, tx, nw); err != nil {
log.Error("compareFlowBind s.gorm.AddItem error(%v)", err)
return
}
changed = append(changed, nw.ID)
}
if len(changed) > 0 && (len(newFormatLog) > 0 || len(oldFormatLog) > 0) {
diff = model.LogFieldTemp(model.LogFieldTokenID, strings.Join(newFormatLog, ";"), strings.Join(oldFormatLog, ";"), isUpdate)
}
log.Info("compareFlowBind end flow(%d) tokenid(%+v) isupdate(%v) diff(%s)", flowID, tokenID, isUpdate, diff)
return
}
func (s *Service) compareTranBind(c context.Context, tx *gorm.DB, tranID int64, binds []*net.TokenBindParam, isUpdate bool) (diff string, changed []int64, err error, msg string) {
var (
relatedTokenID []int64
tokenID = []int64{}
existBindParam = map[int64]*net.TokenBindParam{}
tokenMap map[string]*net.Token
oldBindAll = map[int64]*net.TokenBind{}
newFormatLog = []string{}
oldFormatLog []string
disable = time.Now()
recovered = net.Recovered
)
log.Info("compareTranBind start transition(%d) binds(%+v) isUpdate(%v)", tranID, binds, isUpdate)
//现有全部绑定关系和可用绑定
if isUpdate {
if oldBindAll, oldFormatLog, err = s.fetchOldBindAndLog(c, tranID, net.BindTranType); err != nil {
log.Error("compareTranBind s.fetchOldBindAndLog error(%v)", err)
return
}
}
//新绑定处理
for _, item := range binds {
//是否已存在
if item.ID > 0 && oldBindAll[item.ID] == nil {
log.Error("compareTranBind binds(%+v) not found", item)
err = ecode.RequestErr
return
}
if item.ID > 0 {
existBindParam[item.ID] = item
}
//tokenid排序重组
if relatedTokenID, err = xstr.SplitInts(item.TokenID); err != nil {
log.Error("compareTranBind xstr.SplitInts(%+v) error(%v)", item, err)
return
}
if len(relatedTokenID) == 0 {
log.Error("compareTranBind bind(%+v) tokenid empty ", item)
err = ecode.RequestErr
return
}
sort.Sort(net.Int64Slice(relatedTokenID))
item.TokenID = xstr.JoinInts(relatedTokenID)
tokenID = append(tokenID, relatedTokenID...)
}
//检查是否为赋值语句token & token是否存在
if len(tokenID) > 0 {
if tokenMap, err, msg = s.checkBindToken(c, tokenID); err != nil {
log.Error("compareTranBind s.checkBindToken error(%v)", err)
return
}
}
for _, item := range binds {
//前端没传中文名则自行组合
nwLog := []string{}
chname := item.ChName
tkname := map[string]int64{}
for _, tid := range strings.Split(item.TokenID, ",") {
tk := tokenMap[tid]
if tk == nil {
continue
}
//token_name级别的过滤
if tkname[tk.Name] > 0 {
log.Error("compareTranBind bind(%+v) duplicated with token name(%s)", item, tk.Name)
err = ecode.RequestErr
return
}
tkname[tk.Name] = tk.ID
if item.ChName == "" {
chname = chname + tokenMap[tid].ChName
}
nwLog = append(nwLog, tokenMap[tid].FormatLog())
}
chnameMerge := []rune(chname)
last := len(chnameMerge)
if last > 16 {
last = 16
}
item.ChName = string(chnameMerge[:last])
newFormatLog = append(newFormatLog, fmt.Sprintf(net.BindLogTemp, item.ChName, item.Type, strings.Join(nwLog, ",")))
}
//从旧的中过滤新的
for id, item := range oldBindAll {
updateField := map[string]interface{}{}
if newParam, exist := existBindParam[id]; exist {
if !item.IsAvailable() {
item.DisableTime = recovered
updateField["disable_time"] = recovered
}
if item.TokenID != newParam.TokenID {
item.TokenID = newParam.TokenID
updateField["token_id"] = newParam.TokenID
}
if newParam.ChName != "" && newParam.ChName != item.ChName {
item.ChName = newParam.ChName
updateField["ch_name"] = newParam.ChName
}
if newParam.Type != item.Type {
item.Type = newParam.Type
updateField["type"] = newParam.Type
}
} else if item.IsAvailable() {
item.DisableTime = disable
updateField["disable_time"] = disable
} else {
continue
}
if len(updateField) == 0 {
continue
}
if err = s.gorm.UpdateFields(c, tx, net.TableTokenBind, item.ID, updateField); err != nil {
log.Error("compareTranBind s.gorm.UpdateFields error(%v)", err)
return
}
changed = append(changed, item.ID)
}
//从新的中过滤旧的
for _, newParam := range binds {
if newParam.ID > 0 && oldBindAll[newParam.ID] != nil {
continue
}
nw := &net.TokenBind{TokenID: newParam.TokenID, ChName: newParam.ChName, ElementID: tranID, Type: newParam.Type}
if err = s.gorm.AddItem(c, tx, nw); err != nil {
log.Error("compareTranBind s.gorm.AddItem error(%v)", err)
return
}
changed = append(changed, nw.ID)
}
if len(changed) > 0 && (len(newFormatLog) > 0 || len(oldFormatLog) > 0) {
diff = model.LogFieldTemp(model.LogFieldTokenID, strings.Join(newFormatLog, ";"), strings.Join(oldFormatLog, ";"), isUpdate)
}
log.Info("compareTranBind end transition(%d) binds(%+v) isupdate(%v) diff(%s)", tranID, binds, isUpdate, diff)
return
}
func (s *Service) bindTokens(c context.Context, binds []*net.TokenBind) (result map[int64][]*net.Token, err error) {
var (
tokenIDSlice []int64
tokens []*net.Token
)
result = map[int64][]*net.Token{}
if len(binds) == 0 {
return
}
tids := []int64{}
bindTokenMap := map[int64][]int64{}
for _, item := range binds {
if tokenIDSlice, err = xstr.SplitInts(item.TokenID); err != nil {
log.Error("tokenBindDetail xstr.SplitInts(%s) error(%v)", item.TokenID, err)
return
}
bindTokenMap[item.ID] = tokenIDSlice
if len(tokenIDSlice) == 0 {
continue
}
tids = append(tids, tokenIDSlice...)
}
if len(tids) == 0 {
return
}
if tokens, err = s.gorm.Tokens(c, tids); err != nil {
log.Error("tokenBindDetail s.gorm.Tokens(%v) error(%v)", tids, err)
return
}
if len(tokens) == 0 {
return
}
tokenMap := map[int64]*net.Token{}
for _, item := range tokens {
tokenMap[item.ID] = item
}
for bindID, tidList := range bindTokenMap {
result[bindID] = []*net.Token{}
for _, id := range tidList {
if tokenMap[id] == nil {
continue
}
result[bindID] = append(result[bindID], tokenMap[id])
}
}
return
}
//tokenBindDetail 一条绑定的详细信息获取了token详情
func (s *Service) tokenBindDetail(c context.Context, binds []*net.TokenBind) (result []*net.TokenBindDetail, err error) {
var (
tokenIDSlice []int64
tokens []*net.Token
)
if len(binds) == 0 {
return
}
details := make([]*net.TokenBindDetail, len(binds))
//get tokens
tids := []int64{}
bindTokenMap := map[string][]int64{}
for k, item := range binds {
details[k] = &net.TokenBindDetail{
ID: item.ID,
Type: item.Type,
ElementID: item.ElementID,
TokenID: item.TokenID,
ChName: item.ChName,
DisableTime: item.DisableTime,
Tokens: []*net.Token{},
}
if tokenIDSlice, err = xstr.SplitInts(item.TokenID); err != nil {
log.Error("tokenBindDetail xstr.SplitInts(%s) error(%v)", item.TokenID, err)
return
} else if len(tokenIDSlice) == 0 {
continue
} else {
tids = append(tids, tokenIDSlice...)
}
bindTokenMap[item.TokenID] = tokenIDSlice
}
if len(tids) == 0 {
result = details
return
}
if tokens, err = s.gorm.Tokens(c, tids); err == nil && len(tokens) == 0 {
log.Error("tokenBindDetail s.gorm.Tokens(%v) not found", tids)
result = details
err = nil
return
}
if err != nil {
log.Error("TokenBindDetail find token error(%v) tids(%v)", err, tids)
return
}
tokenMap := make(map[int64]*net.Token, len(tokens))
for _, item := range tokens {
tokenMap[item.ID] = item
}
//dispatch tokens to detail
for _, item := range details {
for _, id := range bindTokenMap[item.TokenID] {
if tokenMap[id] != nil {
item.Tokens = append(item.Tokens, tokenMap[id])
}
}
}
result = details
return
}

View File

@@ -0,0 +1,111 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/aegis/model/net"
"github.com/smartystreets/goconvey/convey"
"time"
)
func TestServiceGetTokenList(t *testing.T) {
var (
c = context.TODO()
pm = &net.ListTokenParam{
NetID: 1,
}
)
convey.Convey("GetTokenList", t, func(ctx convey.C) {
result, err := s.GetTokenList(c, pm)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
func TestServiceShowToken(t *testing.T) {
var (
c = context.TODO()
id = int64(20)
)
convey.Convey("ShowToken", t, func(ctx convey.C) {
n, err := s.ShowToken(c, id)
ctx.Convey("Then err should be nil.n should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(n, convey.ShouldNotBeNil)
})
})
}
func TestServicecheckTokenUnique(t *testing.T) {
var (
netID = int64(0)
name = ""
compare = int8(0)
value = ""
)
convey.Convey("checkTokenUnique", t, func(ctx convey.C) {
err, msg := s.checkTokenUnique(cntx, netID, name, compare, value)
ctx.Convey("Then err should be nil.msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceAddToken(t *testing.T) {
var (
c = context.TODO()
no = &net.Token{
NetID: 1,
ChName: "创建时间",
Name: "time",
Compare: net.TokenCompareAssign,
Value: time.Now().String(),
Type: net.TokenTypeInt16,
}
)
convey.Convey("AddToken", t, func(ctx convey.C) {
id, err, msg := s.AddToken(c, no)
ctx.Convey("Then err should be nil.id,msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
}
func TestServicefetchOldBindAndLog(t *testing.T) {
convey.Convey("fetchOldBindAndLog", t, func(ctx convey.C) {
all, logs, err := s.fetchOldBindAndLog(cntx, 3, net.BindTranType)
for _, item := range all {
t.Logf("s.fetchOldBindAndLog all item(%+v)", item)
}
t.Logf("logs(%v)", logs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicecompareTransitionBind(t *testing.T) {
var (
elementID = int64(1)
binds = []*net.TokenBindParam{
{ID: 26, TokenID: "20", ChName: "批量通过"},
{ID: 24, TokenID: "21,20", ChName: "可以通过"},
}
)
convey.Convey("compareTransitionBind", t, func(ctx convey.C) {
tx, _ := s.gorm.BeginTx(context.Background())
diff, _, err, msg := s.compareTranBind(cntx, tx, elementID, binds, true)
t.Logf("s.compareTranBind diff(%v) msg(%s)", diff, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,336 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/admin/main/aegis/model"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
func (s *Service) prepareBeforeTrigger(c context.Context, rids []int64, flowID, bizid int64) (err error) {
var (
enableDir []*net.Direction
tids []int64
transition []*net.Transition
)
//以flow为起点的有向线
if enableDir, err = s.fetchFlowNextEnableDirs(c, flowID); err != nil {
log.Error("prepareBeforeTrigger s.fetchFlowNextEnableDirs error(%v) flowid(%d) rids(%v)", err, flowID, rids)
return
}
//flow下游没有变迁属于正常情况
if len(enableDir) == 0 {
return
}
//所有可用有向线的变迁限制
tids = []int64{}
for _, dir := range enableDir {
tids = append(tids, dir.TransitionID)
}
if transition, err = s.transitions(c, tids, true); err != nil {
log.Error("prepareBeforeTrigger s.transitions(%+v) error(%v) flowid(%d) rids(%v)", tids, err, flowID, rids)
return
}
for _, item := range transition {
if item.Trigger != net.TriggerManual || item.Limit <= 0 {
continue
}
for _, rid := range rids {
s.sendCreateTaskMsg(c, rid, flowID, item.Limit, bizid)
}
}
return
}
//ShowTransition .
func (s *Service) ShowTransition(c context.Context, id int64) (r *net.ShowTransitionResult, err error) {
var (
t *net.Transition
tks map[int64][]*net.TokenBind
)
if t, err = s.gorm.TransitionByID(c, id); err != nil {
return
}
if tks, err = s.gorm.TokenBindByElement(c, []int64{id}, net.BindTranType, true); err != nil {
return
}
r = &net.ShowTransitionResult{
Transition: t,
Tokens: tks[id],
}
return
}
//GetTranByNet .
func (s *Service) GetTranByNet(c context.Context, netID int64) (result map[int64]string, err error) {
var (
trans []*net.Transition
)
result = map[int64]string{}
if trans, err = s.gorm.TranByNet(c, netID, true); err != nil {
log.Error("GetTranByNet s.gorm.TranByNet(%d) error(%v)", netID, err)
return
}
for _, item := range trans {
result[item.ID] = item.ChName
}
return
}
//GetTransitionList .
func (s *Service) GetTransitionList(c context.Context, pm *net.ListNetElementParam) (result *net.ListTransitionRes, err error) {
var (
transitionID []int64
tks map[int64][]*net.TokenBind
uid = []int64{}
unames map[int64]string
)
if result, err = s.gorm.TransitionList(c, pm); err != nil {
return
}
if len(result.Result) == 0 {
return
}
for _, item := range result.Result {
transitionID = append(transitionID, item.ID)
uid = append(uid, item.UID)
}
if tks, err = s.gorm.TokenBindByElement(c, transitionID, net.BindTranType, true); err != nil {
return
}
if unames, err = s.http.GetUnames(c, uid); err != nil {
log.Error("GetTransitionList s.http.GetUnames error(%v)", err)
err = nil
}
for _, item := range result.Result {
item.Username = unames[item.UID]
for _, bd := range tks[item.ID] {
item.Tokens = append(item.Tokens, bd.ChName)
}
}
return
}
func (s *Service) checkTransitionUnique(c context.Context, netID int64, name string) (err error, msg string) {
var exist *net.Transition
if exist, err = s.gorm.TransitionByUnique(c, netID, name); err != nil {
log.Error("checkTransitionUnique s.gorm.TransitionByUnique(%d,%s) error(%v)", netID, name, err)
return
}
if exist != nil {
err = ecode.AegisUniqueAlreadyExist
msg = fmt.Sprintf(ecode.AegisUniqueAlreadyExist.Message(), "变化", name)
}
return
}
//AddTransition .
func (s *Service) AddTransition(c context.Context, uid int64, f *net.TransitionEditParam) (id int64, err error, msg string) {
var (
tx *gorm.DB
diff = []string{}
diffBind string
)
if err, msg = s.checkTransitionUnique(c, f.NetID, f.Name); err != nil {
return
}
tran := &net.Transition{
NetID: f.NetID,
Trigger: f.Trigger,
Limit: f.Limit,
Name: f.Name,
ChName: f.ChName,
Description: f.Description,
UID: uid,
}
//db update
tx, err = s.gorm.BeginTx(c)
if err != nil {
log.Error("AddTransition s.gorm.BeginTx error(%v)", err)
return
}
if err = s.gorm.AddItem(c, tx, tran); err != nil {
tx.Rollback()
return
}
if diffBind, _, err, msg = s.compareTranBind(c, tx, tran.ID, f.TokenList, false); err != nil {
log.Error("AddTransition s.compareTranBind error(%v) params(%+v)", err, f)
tx.Rollback()
return
}
if diffBind != "" {
diff = append(diff, diffBind)
}
if err = tx.Commit().Error; err != nil {
log.Error("AddTransition tx.Commit error(%v)", err)
return
}
id = tran.ID
//日志
diff = append(diff, model.LogFieldTemp(model.LogFieldChName, f.ChName, "", false))
diff = append(diff, model.LogFieldTemp(model.LogFieldName, f.Name, "", false))
diff = append(diff, model.LogFieldTemp(model.LogFieldLimit, f.Limit, "", false))
diff = append(diff, model.LogFieldTemp(model.LogFieldTrigger, net.TriggerDesc[f.Trigger], "", false))
oper := &model.NetConfOper{
OID: tran.ID,
Action: model.LogNetActionNew,
UID: tran.UID,
NetID: tran.NetID,
ChName: tran.ChName,
TranID: tran.ID,
Diff: diff,
}
s.sendNetConfLog(c, model.LogTypeTranConf, oper)
return
}
//UpdateTransition .
func (s *Service) UpdateTransition(c context.Context, uid int64, f *net.TransitionEditParam) (err error, msg string) {
var (
old *net.Transition
updates = map[string]interface{}{}
tx *gorm.DB
diff = []string{}
diffBind string
changedBind []int64
)
if old, err = s.gorm.TransitionByID(c, f.ID); err != nil {
log.Error("UpdateTransition s.gorm.TransitionByID(%d) error(%v)", f.ID, err)
return
}
if f.Name != old.Name {
if err, msg = s.checkTransitionUnique(c, f.NetID, f.Name); err != nil {
return
}
diff = append(diff, model.LogFieldTemp(model.LogFieldName, f.Name, old.Name, true))
old.Name = f.Name
updates["name"] = f.Name
}
if f.ChName != old.ChName {
diff = append(diff, model.LogFieldTemp(model.LogFieldChName, f.ChName, old.ChName, true))
old.ChName = f.ChName
updates["ch_name"] = f.ChName
}
if f.Description != old.Description {
old.Description = f.Description
updates["description"] = f.Description
}
if f.Limit != old.Limit {
diff = append(diff, model.LogFieldTemp(model.LogFieldLimit, f.Limit, old.Limit, true))
old.Limit = f.Limit
updates["limit"] = f.Limit
}
if f.Trigger != old.Trigger {
diff = append(diff, model.LogFieldTemp(model.LogFieldTrigger, net.TriggerDesc[f.Trigger], net.TriggerDesc[old.Trigger], true))
old.Trigger = f.Trigger
updates["trigger"] = f.Trigger
}
//db update
tx, err = s.gorm.BeginTx(c)
if err != nil {
log.Error("UpdateTransition s.gorm.BeginTx error(%v)", err)
return
}
if len(updates) > 0 {
if err = s.gorm.UpdateFields(c, tx, net.TableTransition, old.ID, updates); err != nil {
tx.Rollback()
return
}
}
if diffBind, changedBind, err, msg = s.compareTranBind(c, tx, old.ID, f.TokenList, true); err != nil {
log.Error("UpdateTransition s.compareTranBind error(%v) params(%+v)", err, f)
tx.Rollback()
return
}
if diffBind != "" {
diff = append(diff, diffBind)
}
if err = tx.Commit().Error; err != nil {
log.Error("UpdateTransition tx.Commit error(%v)", err)
return
}
s.delTranCache(c, old, changedBind)
//日志
if len(diff) == 0 {
return
}
oper := &model.NetConfOper{
OID: old.ID,
Action: model.LogNetActionUpdate,
UID: uid,
NetID: old.NetID,
ChName: old.ChName,
TranID: old.ID,
Diff: diff,
}
s.sendNetConfLog(c, model.LogTypeTranConf, oper)
return
}
//SwitchTransition .
func (s *Service) SwitchTransition(c context.Context, id int64, needDisable bool) (err error) {
var (
old *net.Transition
dirs []*net.Direction
action string
)
if old, err = s.gorm.TransitionByID(c, id); err != nil {
log.Error("SwitchTransition s.gorm.TransitionByID(%d) error(%v) needDisable(%v)", id, err, needDisable)
return
}
available := old.IsAvailable()
if available == !needDisable {
return
}
if needDisable {
if dirs, err = s.gorm.DirectionByTransitionID(c, []int64{id}, 0, true); err != nil {
log.Error("SwitchTransition s.gorm.DirectionByTransitionID(%d) error(%v)", id, err)
return
}
if len(dirs) > 0 {
log.Error("SwitchTransition dir by transition(%d) founded", id)
err = ecode.AegisTranBinded
return
}
old.DisableTime = time.Now()
action = model.LogNetActionDisable
} else {
old.DisableTime = net.Recovered
action = model.LogNetActionAvailable
}
if err = s.gorm.UpdateFields(c, nil, net.TableTransition, id, map[string]interface{}{"disable_time": old.DisableTime}); err != nil {
return
}
s.delTranCache(c, old, nil)
//日志
oper := &model.NetConfOper{
OID: old.ID,
Action: action,
UID: old.UID,
NetID: old.NetID,
ChName: old.ChName,
TranID: old.ID,
}
s.sendNetConfLog(c, model.LogTypeTranConf, oper)
return
}

View File

@@ -0,0 +1,65 @@
package service
import (
"context"
"go-common/app/admin/main/aegis/model/net"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServicecreateTaskVerify(t *testing.T) {
var (
rid = int64(1)
flowID = int64(1)
bizid = int64(1)
)
convey.Convey("prepareBeforeTrigger", t, func(ctx convey.C) {
err := s.prepareBeforeTrigger(context.TODO(), []int64{rid}, flowID, bizid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceShowTransition(t *testing.T) {
var (
c = context.TODO()
id = int64(1)
)
convey.Convey("ShowTransition", t, func(ctx convey.C) {
r, err := s.ShowTransition(c, id)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldNotBeNil)
})
})
}
func TestServiceGetTransitionList(t *testing.T) {
var (
c = context.TODO()
pm = &net.ListNetElementParam{NetID: 1}
)
convey.Convey("GetTransitionList", t, func(ctx convey.C) {
result, err := s.GetTransitionList(c, pm)
ctx.Convey("Then err should be nil.result should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(result, convey.ShouldNotBeNil)
})
})
}
func TestServicecheckTransitionUnique(t *testing.T) {
var (
netID = int64(1)
name = ""
)
convey.Convey("checkTransitionUnique", t, func(ctx convey.C) {
err, msg := s.checkTransitionUnique(cntx, netID, name)
ctx.Convey("Then err should be nil.msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
})
})
}