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,79 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"activity_test.go",
"business_test.go",
"challenge_test.go",
"platform_test.go",
"service_test.go",
"wlog_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"activity.go",
"business.go",
"callback.go",
"challenge.go",
"event.go",
"group.go",
"meta.go",
"platform.go",
"service.go",
"tag.go",
"task.go",
"wlog.go",
],
importpath = "go-common/app/admin/main/workflow/service",
tags = ["automanaged"],
deps = [
"//app/admin/main/credit/model/blocked:go_default_library",
"//app/admin/main/workflow/dao:go_default_library",
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//app/job/main/credit/model:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/sync/errgroup: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,54 @@
package service
import (
"context"
"sort"
"go-common/app/admin/main/workflow/model"
"go-common/library/log"
)
// ActivityList will list activities by given conditions
func (s *Service) ActivityList(c context.Context, business int8, cid int64) (acts *model.Activities, err error) {
var (
logs []*model.WLog
events map[int64]*model.Event
uids []int64
uNames map[int64]string
)
if logs, err = s.AllAuditLog(c, cid, []int{model.WLogModuleChallenge}); err != nil {
log.Error("s.AllAuditLog(%d) error(%v)", cid, err)
}
log.Info("audit log cid(%d) logs(%+v)", cid, logs)
if events, err = s.dao.EventsByCid(c, cid); err != nil {
log.Error("Failed to s.dao.EventsByCid(%d): %v", cid, err)
return
}
acts = new(model.Activities)
acts.Events = make([]*model.Event, 0, len(events))
acts.Logs = logs
for _, e := range events {
acts.Events = append(acts.Events, e)
uids = append(uids, e.AdminID)
}
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
} else {
for i := range acts.Events {
acts.Events[i].Admin = uNames[acts.Events[i].AdminID]
}
}
//sort.Sort(model.LogSlice(acts.Logs))
//sort.Sort(model.EventSlice(acts.Events))
sort.Slice(acts.Logs, func(i, j int) bool {
return acts.Logs[i].CTime < acts.Logs[j].CTime
})
sort.Slice(acts.Events, func(i, j int) bool {
return acts.Events[i].CTime < acts.Events[j].CTime
})
return
}

View File

@@ -0,0 +1,44 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/workflow/model/param"
"github.com/smartystreets/goconvey/convey"
)
func TestActivityList(t *testing.T) {
convey.Convey("ActivityList", t, func() {
eid, err := s.AddEvent(context.Background(), &param.EventParam{
Cid: int64(1),
AdminID: int64(1),
Content: "test.content",
Attachments: "test.attachments",
Event: int8(1),
})
convey.So(err, convey.ShouldBeNil)
convey.So(eid, convey.ShouldNotEqual, 0)
elist, err := s.ListEvent(context.Background(), 1)
convey.So(err, convey.ShouldBeNil)
eids := make([]int64, 0)
for _, e := range elist {
eids = append(eids, e.Eid)
}
convey.So(eid, convey.ShouldBeIn, eids)
acts, err := s.ActivityList(context.Background(), int8(1), 1)
convey.So(err, convey.ShouldBeNil)
convey.So(acts, convey.ShouldNotBeNil)
convey.So(acts.Events, convey.ShouldNotBeEmpty)
convey.So(acts.Logs, convey.ShouldNotBeEmpty)
eids2 := make([]int64, 0)
for _, e := range acts.Events {
eids2 = append(eids2, e.Eid)
}
convey.So(eid, convey.ShouldBeIn, eids2)
})
}

View File

@@ -0,0 +1,211 @@
package service
import (
"context"
"sort"
"strings"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_lenShortCut = 8
)
// 0: 有效 1: 无效 2: 流转 3: 众裁 4: 批量有效 5: 批量无效 6: 批量流转 7: 批量移交众裁
var btnName = map[uint]string{
0: "有效",
1: "无效",
2: "流转",
3: "众裁",
4: "批量有效",
5: "批量无效",
6: "批量流转",
7: "批量移交众裁",
}
var btnInitShortCut = map[uint]string{
0: "A",
1: "S",
2: "D",
3: "F",
4: "J",
5: "K",
6: "H",
7: "L",
}
// ListMeta will list business meta inforamtion from DAO
func (s *Service) ListMeta(c context.Context, itemType string) (metaList []*model.Meta, err error) {
allMetas := s.dao.AllMetas(c)
if err != nil {
log.Error("Failed to fetch all metas from database: %v", err)
return
}
metaList = make([]*model.Meta, 0, len(allMetas))
for _, m := range allMetas {
if itemType != "" && m.ItemType != itemType {
continue
}
//sort rounds field asc by id
sort.Sort(model.RoundSlice(m.Rounds))
metaList = append(metaList, m)
}
//sort meta asc by business
sort.Sort(model.MetaSlice(metaList))
return
}
// ListBusAttr list business attr info
func (s *Service) ListBusAttr(ctx context.Context) (busAttr []*model.BusinessAttr, err error) {
busAttr = make([]*model.BusinessAttr, 0)
if err = s.dao.ORM.Table("workflow_business_attr").Find(&busAttr).Error; err != nil {
return
}
return
}
// ListBusAttrV3 .
func (s *Service) ListBusAttrV3(ctx context.Context) (busAttr []*model.BusinessAttr, err error) {
log.Info("start ListBusAttrV3")
t := time.Now()
busAttr = make([]*model.BusinessAttr, 0)
if err = s.dao.ORM.Table("workflow_business_attr").Find(&busAttr).Error; err != nil {
log.Info("query table workflow_business_attr error(%v)", err)
return
}
for _, attr := range busAttr {
btnShortCut := strings.Split(attr.ButtonKey, ",")
if len(btnShortCut) != _lenShortCut {
log.Warn("button short cut length not 8, load initial value")
btnShortCut = []string{"A", "S", "D", "F", "J", "K", "H", "L"}
}
for i := uint(0); i < _lenShortCut; i++ {
state := false
mask := uint8(1 << i)
if attr.Button&mask > 0 {
state = true
}
if btnShortCut[i] == "" {
btnShortCut[i] = btnInitShortCut[i]
}
attr.Buttons = append(attr.Buttons, &model.Button{
Index: int(i),
Name: btnName[i],
State: state,
Key: btnShortCut[i],
})
}
}
log.Info("end ListBusAttrV3 time(%v)", time.Since(t).String())
return
}
// AddOrUpdateBusAttr add or update business attr info
func (s *Service) AddOrUpdateBusAttr(ctx context.Context, abap *param.AddBusAttrParam) (err error) {
busAttr := &model.BusinessAttr{
ID: abap.ID,
}
attr := map[string]interface{}{
"bid": abap.Bid,
"name": abap.Name,
"deal_type": abap.DealType,
"expire_time": abap.ExpireTime,
"assign_type": abap.AssignType,
"assign_max": abap.AssignMax,
"group_type": abap.GroupType,
"business_name": abap.BusinessName,
}
if err = s.dao.ORM.Table("workflow_business_attr").
Where("id=?", abap.ID).
Assign(attr).FirstOrCreate(busAttr).Error; err != nil {
log.Error("Failed to create business_attr(%+v): %v", busAttr, err)
return
}
s.loadBusAttrs()
return
}
// SetSwitch .
func (s *Service) SetSwitch(ctx context.Context, bs *param.BusAttrButtonSwitch) (err error) {
attr := new(model.BusinessAttr)
if err = s.dao.ORM.Table("workflow_business_attr").Where("bid = ?", bs.Bid).Find(attr).Error; err != nil {
log.Error("Failed to find business_attr where bid = %d : %v", bs.Bid, err)
return
}
oldBut := attr.Button
mask := uint8(^(1 << bs.Index))
oldBut = oldBut & mask
newBut := oldBut + (bs.Switch << bs.Index)
if err = s.dao.ORM.Table("workflow_business_attr").Where("bid = ?", bs.Bid).Update("button", newBut).Error; err != nil {
log.Error("Failed to update business_attr button field where bid = %d, button = %d : %v", bs.Bid, newBut, err)
}
return
}
// SetShortCut .
func (s *Service) SetShortCut(ctx context.Context, sc *param.BusAttrButtonShortCut) (err error) {
attr := new(model.BusinessAttr)
if err = s.dao.ORM.Table("workflow_business_attr").Where("bid = ?", sc.Bid).Find(attr).Error; err != nil {
log.Error("Failed to find business_attr where bid = %d : %v", sc.Bid, err)
return
}
oldShortCut := strings.Split(attr.ButtonKey, ",")
if len(oldShortCut) != 8 {
oldShortCut = make([]string, 8)
}
oldShortCut[sc.Index] = sc.ShortCut
newShortCut := strings.Join(oldShortCut, ",")
if err = s.dao.ORM.Table("workflow_business_attr").Where("bid = ?", sc.Bid).Update("button_key", newShortCut).Error; err != nil {
log.Error("Failed to update business_attr button_key field where bid = %d, button_key = %d : %v", sc.Bid, newShortCut, err)
}
return
}
// ManagerTag .
func (s *Service) ManagerTag(ctx context.Context) (map[int8]map[int64]*model.TagMeta, error) {
return s.tagListCache, nil
}
// UserBlockInfo .
// http://info.bilibili.co/pages/viewpage.action?pageId=5417571
// http://info.bilibili.co/pages/viewpage.action?pageId=7559616
func (s *Service) UserBlockInfo(ctx context.Context, bi *param.BlockInfo) (resp model.BlockInfoResp, err error) {
var sum int64
if sum, err = s.dao.BlockNum(ctx, bi.Mid); err != nil {
log.Error("s.dao.BlockNum(%d) error(%v)", bi.Mid, err)
return
}
if resp, err = s.dao.BlockInfo(ctx, bi.Mid); err != nil {
log.Error("s.dao.BlockInfo(%d) error(%v)", bi.Mid, err)
return
}
resp.Data.BlockedSum = sum
return
}
// SourceList .
func (s *Service) SourceList(ctx context.Context, src *param.Source) (data map[string]interface{}, err error) {
// check if has external uri
if _, ok := s.callbackCache[src.Bid]; !ok {
err = ecode.WkfBusinessCallbackConfigNotFound
return
}
uri := ""
if uri = s.callbackCache[src.Bid].SourceAPI; uri == "" {
return
}
return s.dao.SourceInfo(ctx, uri)
}

View File

@@ -0,0 +1,16 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestListMeta(t *testing.T) {
convey.Convey("ListMeta", t, func() {
ml, err := s.ListMeta(context.Background(), "challenge")
convey.So(err, convey.ShouldBeNil)
convey.So(len(ml), convey.ShouldBeGreaterThanOrEqualTo, 1)
})
}

View File

@@ -0,0 +1,77 @@
package service
import (
"context"
"sort"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/log"
"github.com/pkg/errors"
)
// AddOrUpCallback will add or update a callback
func (s *Service) AddOrUpCallback(c context.Context, cbp *param.AddCallbackParam) (cbID int32, err error) {
cb := &model.Callback{}
if err = s.dao.ORM.Model(cb).
Where(&model.Callback{Business: cbp.Business}).
Assign(&model.Callback{
Business: cbp.Business,
URL: cbp.URL,
IsSobot: cbp.IsSobot,
State: cbp.State,
ExternalAPI: cbp.ExternalAPI,
SourceAPI: cbp.SourceAPI,
}).
FirstOrCreate(cb).Error; err != nil {
log.Error("Failed to create a callback(%+v): %v", cb, err)
return
}
cbID = cb.CbID
// load callbacks from datebase in cache
go s.loadCallbacks()
return
}
// ListCallback will list all enabled callbacks
func (s *Service) ListCallback(c context.Context) (cbList model.CallbackSlice, err error) {
for _, cb := range s.callbackCache {
cbList = append(cbList, cb)
}
sort.Slice(cbList, func(i, j int) bool {
return cbList[i].Business > cbList[j].Business
})
return
}
// SendCallbackRetry will try to send callback with specified attempts
func (s *Service) SendCallbackRetry(c context.Context, cb *model.Callback, payload *model.Payload) (err error) {
attempts := 3
for i := 0; i < attempts; i++ {
err = s.dao.SendCallback(c, cb, payload)
if err == nil {
return
}
if i >= (attempts - 1) {
break
}
time.Sleep(time.Second * 1)
}
return errors.Wrapf(err, "after %d attempts", attempts)
}
// SetExtAPI .
func (s *Service) SetExtAPI(ctx context.Context, ea *param.BusAttrExtAPI) (err error) {
if err = s.dao.ORM.Table("workflow_callback").Where("business = ?", ea.Bid).Update("external_api", ea.ExternalAPI).Error; err != nil {
log.Error("Failed to set external_api field where bid = %d : %v", ea.Bid, err)
}
return
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
package service
import (
"context"
"testing"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/admin/main/workflow/model/search"
"github.com/smartystreets/goconvey/convey"
)
func TestSetGroupResult(t *testing.T) {
convey.Convey("SetGroupResult", t, func() {
g := new(model.Group)
err := s.dao.ORM.Table("workflow_group").Where("business=1").Find(g).Error
convey.So(err, convey.ShouldBeNil)
err = s.SetGroupResult(context.Background(), &param.GroupResParam{Oid: g.Oid, Business: g.Business, State: int8(1), AdminID: int64(1), Reason: "hhh"})
convey.So(err, convey.ShouldBeNil)
ng := new(model.Group)
err = s.dao.ORM.Table("workflow_group").Where("business=1 and oid=?", g.Oid).Find(ng).Error
convey.So(err, convey.ShouldBeNil)
convey.So(ng.State, convey.ShouldEqual, int8(1))
})
}
func TestChallListCommon(t *testing.T) {
convey.Convey("ChallListCommon", t, func() {
cPage, err := s.ChallListCommon(context.Background(), &search.ChallSearchCommonCond{
Order: "ctime", Sort: "desc", PN: 1, PS: 50})
time.Sleep(3 * time.Second)
convey.So(err, convey.ShouldBeNil)
convey.So(cPage.Page.Total, convey.ShouldBeGreaterThanOrEqualTo, int32(1))
})
}
func TestChallList(t *testing.T) {
convey.Convey("ChallList", t, func() {
cPage, err := s.ChallList(context.Background(), &search.ChallSearchCommonCond{IDs: []int64{1},
PN: 1, PS: 50, Order: "id", Sort: "desc"})
convey.So(err, convey.ShouldBeNil)
convey.So(cPage.Page.Total, convey.ShouldBeGreaterThanOrEqualTo, int32(1))
})
}
func TestChallDetail(t *testing.T) {
convey.Convey("ChallDetail", t, func() {
chall, err := s.ChallDetail(context.Background(), int64(1))
convey.So(err, convey.ShouldBeNil)
convey.So(chall.Cid, convey.ShouldEqual, int32(1))
})
}

View File

@@ -0,0 +1,116 @@
package service
import (
"context"
"sort"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/log"
)
// AddEvent will add a event
func (s *Service) AddEvent(c context.Context, ep *param.EventParam) (eid int64, err error) {
e := &model.Event{
Cid: ep.Cid,
AdminID: ep.AdminID,
Content: ep.Content,
Attachments: ep.Attachments,
Event: ep.Event,
}
if err = s.dao.ORM.Create(e).Error; err != nil {
log.Error("Failed to create event(%v): %v", e, err)
return
}
eid = e.Eid
s.task(func() {
var c *model.Chall
if c, err = s.dao.Chall(context.Background(), e.Cid); err != nil {
log.Error("s.dao.Chall(%d) error(%v)", e.Cid, err)
err = nil
return
}
s.afterAddReply(ep, c)
})
return
}
// BatchAddEvent will add events to batch chall
func (s *Service) BatchAddEvent(c context.Context, bep *param.BatchEventParam) (eids []int64, err error) {
if len(bep.Cids) <= 0 {
return
}
eids = make([]int64, 0, len(bep.Cids))
for _, cid := range bep.Cids {
e := &model.Event{
Cid: cid,
AdminID: bep.AdminID,
Content: bep.Content,
Attachments: bep.Attachments,
Event: bep.Event,
}
if err = s.dao.ORM.Create(e).Error; err != nil {
log.Error("Failed to create event(%v): %v", e, err)
return
}
eids = append(eids, int64(e.Eid))
}
s.task(func() {
var challs map[int64]*model.Chall
if challs, err = s.dao.Challs(context.Background(), bep.Cids); err != nil {
log.Error("s.dao.Challs(%v) error(%v)", bep.Cids, err)
return
}
s.afterAddMultiReply(bep, challs)
})
return
}
// ListEvent will add a set of events by challenge id
func (s *Service) ListEvent(c context.Context, cid int64) (eventList model.EventSlice, err error) {
var (
events map[int64]*model.Event
)
if events, err = s.dao.EventsByCid(c, cid); err != nil {
log.Error("Failed to s.dao.Events(%d): %v", cid, err)
return
}
eventList = make(model.EventSlice, 0, len(events))
for _, e := range events {
eventList = append(eventList, e)
}
sort.Slice(eventList, func(i, j int) bool {
return eventList[i].CTime < eventList[j].CTime
})
return
}
// batchLastEvent will return the last log on specified targets
func (s *Service) batchLastEvent(c context.Context, cids []int64) (cEvents map[int64]*model.Event, err error) {
var (
eids []int64
events map[int64]*model.Event
)
if eids, err = s.dao.BatchLastEventIDs(c, cids); err != nil {
log.Error("s.dao.BatchLastEventIDs(%d) error(%v)", cids, err)
return
}
if events, err = s.dao.EventsByIDs(c, eids); err != nil {
log.Error("s.dao.EventsByIDs(%d) error(%v)", eids, err)
return
}
cEvents = make(map[int64]*model.Event, len(eids))
for _, e := range events {
cEvents[e.Cid] = e
}
return
}

View File

@@ -0,0 +1,988 @@
package service
import (
"context"
"encoding/json"
"fmt"
"math"
"net/url"
"sort"
"strconv"
"time"
"go-common/app/admin/main/credit/model/blocked"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
"golang.org/x/sync/errgroup"
)
// GroupListV3 .
func (s *Service) GroupListV3(c context.Context, cond *search.GroupSearchCommonCond) (grpPage *model.GroupListPage, err error) {
var (
ok bool
groupSearchCommonResp *search.GroupSearchCommonResp
wg = &errgroup.Group{}
)
if groupSearchCommonResp, err = s.dao.SearchGroup(c, cond); err != nil {
log.Error("Failed to s.dao.SearchGroup(%v): %v", cond, err)
err = ecode.WkfSearchGroupFailed
return
}
if len(groupSearchCommonResp.Result) == 0 {
grpPage = &model.GroupListPage{
Items: []*model.Group{},
Page: &model.Page{},
}
return
}
eids, oids, gids, mids := []int64{}, []int64{}, []int64{}, []int64{}
fut := make(map[int64]int64, len(groupSearchCommonResp.Result))
for _, v := range groupSearchCommonResp.Result {
gids = append(gids, v.ID)
oids = append(oids, v.Oid)
eids = append(eids, v.Eid)
if v.Mid > 0 {
mids = append(mids, v.Mid)
}
fut[v.ID] = v.FirstUserTid
}
// last chall mids
cscc := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid", "mid"},
Business: cond.Business,
Gids: gids,
Order: "ctime",
Sort: "desc",
Distinct: []string{"gid"},
PS: 999,
PN: 1,
}
var (
cResp *search.ChallSearchCommonResp
lastChalls = make(map[int64]*model.Chall)
)
if cResp, err = s.dao.SearchChallenge(c, cscc); err != nil {
log.Error("s.dao.SearchChallenge(%v) error(%v)", cscc, err)
} else {
for _, r := range cResp.Result {
if r.Mid > 0 {
mids = append(mids, r.Mid)
}
lastChalls[r.Gid] = &model.Chall{Cid: r.ID, Mid: r.Mid}
}
}
// group object
var groups map[int64]*model.Group
wg.Go(func() error {
if groups, err = s.dao.Groups(c, gids); err != nil {
log.Error("Failed to s.dao.Groups(%v): %v", gids, err)
return err
}
return nil
})
// todo load data from db, search, external api
// tag count of challenge
var gidToChallTagCount map[int64]map[int64]int64
wg.Go(func() error {
// todo: count tag in es
if gidToChallTagCount, err = s.dao.ChallTagsCountV3(c, gids); err != nil {
log.Error("s.dao.ChallTagsCountV3(%v) error(%v)", gids, err)
err = nil
} else {
log.Info("gidToChallTagCount (%+v)", gidToChallTagCount)
}
return nil
})
var grpLastLog map[int64]string
wg.Go(func() error {
if grpLastLog, err = s.LastLog(c, gids, []int{model.WLogModuleGroup, model.WLogModuleRoleShift}); err != nil {
log.Error("s.LastLog(%v,%d) error(%v)", gids, model.WLogModuleGroup, err)
err = nil
}
return nil
})
// search account
var users map[int64]*model.Account
wg.Go(func() error {
users = s.dao.AccountInfoRPC(c, mids)
//get uper group
var uperTagMap map[int64][]*model.SpecialTag
if uperTagMap, err = s.dao.BatchUperSpecial(c, mids); err != nil {
log.Error("s.dao.BatchUperSpecial(%v) error(%v)", mids, err)
err = nil
} else {
for id, user := range users {
var st []*model.SpecialTag
if st, ok = uperTagMap[id]; !ok {
log.Warn("not find special tag mid(%d)", id)
continue
}
user.SpecialTag = st
}
}
return nil
})
// object table
// todo: judge if need read local object
var bus map[int64]*model.Business
var archives map[int64]*model.Archive
wg.Go(func() error {
if bus, err = s.dao.BusObjectByGids(c, gids); err != nil {
log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
err = nil
}
// search archive
// todo: judge if need search archive
aids := []int64{}
for _, b := range bus {
aids = append(aids, b.Oid)
}
if archives, err = s.dao.ArchiveRPC(c, aids); err != nil {
log.Error("s.dao.ArchiveRPC(%v) error(%v)", oids, err)
err = nil
}
return nil
})
// external meta
var metas map[int64]*model.GroupMeta
wg.Go(func() error {
if metas, err = s.externalMeta(c, cond.Business, gids, oids, eids); err != nil {
log.Error("s.ExternalMeta(%d,%v,%v,%v) error(%v)", cond.Business, gids, oids, eids, err)
err = nil
} else {
log.Info("external meta (%+v)", metas)
}
return nil
})
// wait all wg.go()
if err = wg.Wait(); err != nil {
return
}
//todo make response
grpPage = new(model.GroupListPage)
rgs := make([]*model.Group, 0, len(groupSearchCommonResp.Result))
for _, v := range groupSearchCommonResp.Result {
var (
rg *model.Group
ok bool
)
if rg, ok = groups[v.ID]; !ok {
log.Warn("Failed to retrive group by group id %d", v.ID)
continue
}
if !dbCheck(cond, rg) {
continue
}
rg.TypeID = v.TypeID
// fill last log
var l string
if l, ok = grpLastLog[v.ID]; ok {
rg.LastLog = l
}
// fill tag name
// using tid from group row can ensure the lastest tid fetched
if tid := rg.Tid; tid != 0 {
var t *model.TagMeta
if t, err = s.tag(rg.Business, tid); err != nil {
log.Error("Failed to retrive tag by bid(%d) tag_id(%d)", rg.Business, tid)
err = nil
} else {
rg.Tag = t.Name
}
}
rg.ChallengeTags = model.ChallTagSlice{}
var tc map[int64]int64
if tc, ok = gidToChallTagCount[v.ID]; ok {
total := int64(0)
for _, count := range tc {
total += count
}
for tid, c := range tc {
tname := "<Unknow>"
tround := int8(0)
var t *model.TagMeta
if t, err = s.tag(rg.Business, tid); err != nil {
log.Error("Failed to retrive tag by bid(%d) tag_id(%d)", rg.Business, tid)
err = nil
} else {
tname = t.Name
tround = t.RID
}
ct := &model.ChallTag{
ID: tid,
Tag: tname,
Count: c,
Percent: 100,
Round: tround,
}
if total != 0 {
ct.Percent = round((float64(c) / float64(total)) * 100)
}
rg.ChallengeTags = append(rg.ChallengeTags, ct)
}
log.Warn("challenge tags of gid(%d) %+v", rg.ID, rg.ChallengeTags)
} else {
log.Warn("not found chall tag count of gid(%d)", v.ID)
}
sort.Sort(rg.ChallengeTags)
// fill last producer
var lc *model.Chall
if lc, ok = lastChalls[rg.ID]; ok {
if rg.LastProducer, ok = users[lc.Mid]; !ok {
log.Warn("gid(%d) has last producer mid(%d) but not found account", rg.ID, lc.Mid)
}
log.Info("gid(%d) load last producer mid(%d) success (%+v)", rg.ID, lc.Mid, rg.LastProducer)
} else {
log.Warn("not found account of last producer gid(%d)", rg.ID)
}
// fill meta
s.warpMeta(cond.Business, rg, metas, archives, users, bus)
// oid to string
rg.OidStr = strconv.FormatInt(rg.Oid, 10)
// eid to string
rg.EidStr = strconv.FormatInt(rg.Eid, 10)
rgs = append(rgs, rg)
// fill first_user_tid
rg.FirstUserTid = fut[rg.ID]
}
grpPage.Items = rgs
grpPage.Page = &model.Page{
Num: groupSearchCommonResp.Page.Num,
Size: groupSearchCommonResp.Page.Size,
Total: groupSearchCommonResp.Page.Total,
}
return
}
// UpGroup will update a group
func (s *Service) UpGroup(c context.Context, gp *param.GroupParam) (err error) {
var (
tx *gorm.DB
l *model.WLog
g *model.Group
)
// double write rid
tMeta := &model.TagMeta{}
if tMeta, err = s.tag(gp.Business, gp.Tid); err != nil {
log.Error("TagListCache not found bid(%d) tag_id(%d)", gp.Business, gp.Tid)
return
}
gp.Rid = tMeta.RID
// Check group and tag is exist
if g, err = s.dao.GroupByOid(c, gp.Oid, gp.Business); err != nil {
log.Error("s.dao.GroupByOid(%d, %d) error(%v)", gp.Oid, gp.Business, err)
return
}
if g == nil {
log.Error("Group(%d, %d) not exist", gp.Oid, gp.Business)
err = ecode.WkfGroupNotFound
return
}
tx = s.dao.ORM.Begin()
if err = tx.Error; err != nil {
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("Service.UpGroup() panic(%v)", r)
}
}()
if err = s.dao.TxUpGroup(tx, gp.Oid, gp.Business, gp.Tid, gp.Note, gp.Rid); err != nil {
tx.Rollback()
log.Error("s.TxUpGroup(%d, %d, %d, %s, %d) error(%v)", gp.Oid, gp.Business, gp.Tid, gp.Note, gp.Rid, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
l = &model.WLog{
AdminID: gp.AdminID,
Admin: gp.AdminName,
Oid: g.Oid,
Business: g.Business,
Target: g.ID,
Module: model.WLogModuleGroup,
Remark: fmt.Sprintf(`工单编号 %d “管理 Tag”更新为“%s”`, g.ID, tMeta.Name),
Note: gp.Note,
}
s.writeAuditLog(l)
})
return
}
// UpGroupRole will 流转工单
func (s *Service) UpGroupRole(c context.Context, grsp *param.GroupRoleSetParam) (err error) {
var groups map[int64]*model.Group
// Check group and tag is exist
if groups, err = s.dao.Groups(c, grsp.GID); err != nil {
log.Error("s.dao.Groups(%v) error(%v)", grsp.GID, err)
return
}
if len(groups) == 0 {
log.Error("Group(%v) not exist", grsp.GID)
err = ecode.WkfGroupNotFound
return
}
// check bid
for _, g := range groups {
if g.Business != grsp.BID {
err = ecode.WkfBusinessNotConsistent
return
}
}
// check tid available
var tMeta *model.TagMeta
if tMeta, err = s.tag(grsp.BID, grsp.TID); err != nil {
return
}
grsp.RID = tMeta.RID
if err = s.dao.UpGroupRole(c, grsp); err != nil {
log.Error("s.UpGroupRole(%+v) error(%v)", grsp, err)
return
}
s.task(func() {
s.afterSetGroupRole(grsp, groups)
})
return
}
// SetGroupResult will set a group result
func (s *Service) SetGroupResult(c context.Context, grp *param.GroupResParam) (err error) {
var (
tx *gorm.DB
g *model.Group
tinyChalls map[int64]*model.TinyChall
cids []int64
)
if g, err = s.dao.GroupByOid(c, grp.Oid, grp.Business); err != nil {
log.Error("s.dao.GroupByOid() error(%v)", err)
return
}
if g == nil {
log.Error("Group(%d, %d) not exist", grp.Oid, grp.Business)
err = ecode.NothingFound
return
}
if g.State != model.Pending {
log.Error("Group(%d, %d) not pending", grp.Oid, grp.Business)
return
}
tx = s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("Service.SetGroupResult() panic(%v)", r)
}
}()
// Update Group State
if err = s.dao.TxUpGroupState(tx, g.ID, grp.State); err != nil {
tx.Rollback()
log.Error("s.txUpGroupState(%+v) error(%v)", g, err)
return
}
// set grouo handling field to 0
if err = s.dao.TxUpGroupHandling(tx, g.ID, 0); err != nil {
tx.Rollback()
log.Error("s.txUpGroupHandling(%+v, 0) error(%v)", g.ID, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
var result []*search.ChallSearchCommonData
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid", "mid", "state", "ctime"},
Gids: []int64{g.ID},
States: []int64{int64(model.Pending)},
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
tinyChalls = make(map[int64]*model.TinyChall, len(cids))
for _, c := range result {
cids = append(cids, c.ID)
tc := &model.TinyChall{
Cid: c.ID,
Gid: c.Gid,
Mid: c.Mid,
}
var ctime time.Time
ctime, err = time.ParseInLocation("2006-01-02 15:04:05", c.CTime, time.Local)
if err != nil {
log.Error("time.Parse(%v) error(%v)", c.CTime, err)
}
tc.CTime.Scan(ctime)
log.Info("tc.CTime.Scan(%v) get (%v) cid(%v) gid(%v)", ctime.Unix(), tc.CTime.Time(), tc.Cid, tc.Gid)
if str, ok := c.State.(string); ok {
st, _ := strconv.Atoi(str)
tc.State = int8(st)
}
if f, ok := c.State.(float64); ok {
tc.State = int8(math.Floor(f))
}
tinyChalls[c.ID] = tc
}
if err = s.dao.BatchUpChallByIDs(cids, uint32(grp.State), grp.AdminID); err != nil {
log.Error("s.dao.TxBatchUpChallByIDs(%v,%d) error(%v)", cids, grp.State, err)
return
}
s.afterSetGrpResult(grp, g, tinyChalls)
})
return
}
// BatchSetGroupResult will set a set of groups result
func (s *Service) BatchSetGroupResult(c context.Context, bgrp *param.BatchGroupResParam) (err error) {
var (
tx *gorm.DB
groups map[int64]*model.Group
tinyChalls map[int64]*model.TinyChall
gids []int64
cids []int64
)
tx = s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("Service.BatchSetGroupResult() panic(%v)", r)
}
}()
if groups, err = s.dao.TxGroupsByOidsStates(tx, bgrp.Oids, bgrp.Business, model.Pending); err != nil {
log.Error("s.dao.TxGroupsByOidsStates() error(%v)", err)
return
}
if len(groups) <= 0 {
log.Warn("No pending groups found with conditon(%+v, %d, %d)", bgrp.Oids, bgrp.Business, model.Pending)
return
}
// collect all gids
for gid := range groups {
gids = append(gids, int64(gid))
}
if err = s.dao.TxBatchUpGroupState(tx, gids, bgrp.State); err != nil {
tx.Rollback()
log.Error("s.TxBatchUpGroupState(%+v) error(%v)", bgrp, err)
return
}
// Set group handling count to 0
if err = s.dao.TxBatchUpGroupHandling(tx, gids, 0); err != nil {
tx.Rollback()
log.Error("s.TxBatchUpGroupHandling(%+v, 0) error(%v)", gids, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
var result []*search.ChallSearchCommonData
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid", "mid", "state", "ctime"},
Gids: gids,
States: []int64{int64(model.Pending)},
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
tinyChalls = make(map[int64]*model.TinyChall, len(cids))
for _, c := range result {
cids = append(cids, c.ID)
tc := &model.TinyChall{
Cid: c.ID,
Gid: c.Gid,
Mid: c.Mid,
}
var ctime time.Time
ctime, err = time.ParseInLocation("2006-01-02 15:04:05", c.CTime, time.Local)
if err != nil {
log.Error("time.Parse(%v) error(%v)", c.CTime, err)
}
tc.CTime.Scan(ctime)
log.Info("tc.CTime.Scan(%v) get (%v) cid(%v) gid(%v)", ctime.Unix(), tc.CTime.Time(), tc.Cid, tc.Gid)
if str, ok := c.State.(string); ok {
st, _ := strconv.Atoi(str)
tc.State = int8(st)
}
if f, ok := c.State.(float64); ok {
tc.State = int8(math.Floor(f))
}
tinyChalls[c.ID] = tc
}
if err = s.dao.BatchUpChallByIDs(cids, uint32(bgrp.State), bgrp.AdminID); err != nil {
log.Error("s.dao.BatchUpChallByIDs(%v,%d) error(%v)", cids, bgrp.State, err)
return
}
s.afterBatchSetGrpResult(bgrp, groups, tinyChalls)
})
return
}
// SetGroupState 修改未处理的工单状态
func (s *Service) SetGroupState(c context.Context, gssp *param.GroupStateSetParam) (err error) {
var (
groups map[int64]*model.Group
tinyChalls map[int64]*model.TinyChall
gids, cids []int64
newRid int8
)
//check tid 有效处理验证tid
if gssp.State == model.Effective {
var tmeta *model.TagMeta
if tmeta, err = s.tag(gssp.Business, gssp.Tid); err != nil {
return
}
newRid = tmeta.RID
}
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("s.SetGroupState() panic(%v)", r)
}
if err != nil {
tx.Rollback()
}
}()
if groups, err = s.dao.Groups(c, gssp.ID); err != nil {
log.Error("s.dao.TxGroups(%v) error(%v)", gssp.ID, err)
return
}
// ignore group if state not pending
for id, g := range groups {
if g.State != model.Pending || g.Business != gssp.Business || g.Rid != gssp.Rid {
delete(groups, id)
continue
}
gids = append(gids, id)
}
if len(gids) <= 0 {
log.Warn("No settable groups found with conditon(%+v)", *gssp)
err = ecode.WkfGroupNotFound
return
}
// 有效处理同步修改tid & rid
if gssp.State == model.Effective {
if err = s.dao.TxSetGroupStateTid(tx, gids, gssp.State, newRid, gssp.Tid); err != nil {
log.Error("s.TxSetGroupStateTid(%v,%d,%d) error(%v)", gids, gssp.State, gssp.Tid, err)
return
}
} else {
if err = s.dao.TxSimpleSetGroupState(tx, gids, gssp.State); err != nil {
log.Error("s.TxSimpleSetGroupState(%v,%d,%d) error(%v)", gids, gssp.State, gssp.Tid, err)
return
}
}
// Set group handling count to 0
if err = s.dao.TxBatchUpGroupHandling(tx, gids, 0); err != nil {
log.Error("s.TxBatchUpGroupHandling(%+v, 0) error(%v)", gids, err)
return
}
if err = tx.Commit().Error; err != nil {
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
// group bus object
var gidToBus map[int64]*model.Business
if gidToBus, err = s.dao.BusObjectByGids(context.Background(), gids); err != nil {
log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
return
}
for _, g := range groups {
g.BusinessObject = gidToBus[g.ID]
}
var result []*search.ChallSearchCommonData
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid", "mid", "state", "title", "oid", "tid", "business"},
Gids: gids,
States: []int64{int64(model.Pending)},
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
tinyChalls = make(map[int64]*model.TinyChall, len(cids)) // map[id]*tc
for _, c := range result {
cids = append(cids, c.ID)
tc := &model.TinyChall{
Cid: c.ID,
Gid: c.Gid,
Mid: c.Mid,
Title: c.Title,
}
if str, ok := c.State.(string); ok {
st, _ := strconv.Atoi(str)
tc.State = int8(st)
}
if f, ok := c.State.(float64); ok {
tc.State = int8(math.Floor(f))
}
tinyChalls[c.ID] = tc
}
// async set challenge state
if err = s.dao.BatchUpChallByIDs(cids, uint32(gssp.State), gssp.AdminID); err != nil {
log.Error("s.dao.BatchUpChallByIDs(%v,%d) error(%v)", cids, gssp.State, err)
return
}
s.afterSetGroupState(gssp, groups, tinyChalls)
})
return
}
// SetPublicReferee 移交众裁
func (s *Service) SetPublicReferee(c context.Context, gspr *param.GroupStatePublicReferee) (err error) {
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("s.SetPublicReferee() panic(%v)", r)
}
if err != nil {
tx.Rollback()
}
}()
var groups map[int64]*model.Group
if groups, err = s.dao.TxGroups(tx, gspr.ID); err != nil {
log.Error("s.dao.TxGroups(%v) error(%v)", gspr.ID, err)
return
}
// ignore group if state not pending
var gids, oids, eids []int64
for id, g := range groups {
if g.State != model.Pending || g.Business != gspr.Business {
delete(groups, id)
continue
}
gids = append(gids, id)
oids = append(oids, g.Oid)
eids = append(eids, g.Eid)
}
if len(gids) <= 0 {
log.Warn("No pending groups found with conditon(%+v)", *gspr)
err = ecode.WkfGroupNotFound
return
}
// set state to public referee
gspr.State = model.PublicReferee
// start block add case
// object
var gidBus map[int64]*model.Business
if gidBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
err = ecode.WkfSetPublicRefereeFailed
return
}
// external
var metas map[int64]*model.GroupMeta
if metas, err = s.externalMeta(c, gspr.Business, gids, oids, eids); err != nil {
log.Error("s.SearchMeta(%d,%v,%v,%v) error(%v)", gspr.Business, gids, oids, eids, err)
err = ecode.WkfSetPublicRefereeFailed
return
}
var data []model.BlockCaseAdd
for _, g := range groups {
log.Info("start add case gid(%d)", g.ID)
if _, ok := gidBus[g.ID]; !ok {
log.Warn("gid(%d) not found bus object", g.ID)
err = ecode.WkfSetPublicRefereeFailed
return
}
bus := gidBus[g.ID]
var (
extra map[string]interface{}
external map[string]interface{}
link, title string
ok bool
ctime float64
)
if err = json.Unmarshal([]byte(bus.Extra), &extra); err != nil {
log.Error("json.Unmarshal(%s) failed error(%v)", bus.Extra, err)
err = ecode.WkfSetPublicRefereeFailed
return
}
// redirect url
if link, ok = extra["link"].(string); !ok {
log.Error("gid(%d) assert business extra link failed", g.ID)
err = ecode.WkfSetPublicRefereeFailed
return
}
// object title
if title, ok = extra["title"].(string); !ok {
log.Error("gid(%d) assert business extra title failed", g.ID)
err = ecode.WkfSetPublicRefereeFailed
return
}
if _, ok = metas[g.ID]; !ok {
log.Error("gid(%d) not found meta data", g.ID)
err = ecode.WkfSetPublicRefereeFailed
return
}
if external, ok = metas[g.ID].External.(map[string]interface{}); !ok {
log.Error("gid(%d) external meta data assert failed", g.ID)
err = ecode.WkfSetPublicRefereeFailed
return
}
// business object ctime
if ctime, ok = external["ctime"].(float64); !ok {
log.Error("gid(%d) external ctime assert failed", g.ID)
err = ecode.WkfSetPublicRefereeFailed
return
}
d := model.BlockCaseAdd{
RpID: g.Eid,
Oid: g.Oid,
Type: g.Fid,
Mid: bus.Mid,
Operator: gspr.AdminName,
OperID: gspr.AdminID,
OriginContent: bus.Title,
ReasonType: g.Tid,
BusinessTime: int64(ctime),
OriginType: int64(blocked.OriginReply), // fixme: support multi business
OriginTitle: title,
OriginURL: link,
}
data = append(data, d)
}
// request credit
var content []byte
if content, err = json.Marshal(data); err != nil {
log.Error("json.Marshal(%v) error(%v)", data, err)
err = ecode.WkfSetPublicRefereeFailed
return
}
uv := url.Values{}
uv.Set("data", string(content))
if err = s.dao.AddCreditCase(c, uv); err != nil {
return
}
// set group set only
if err = s.dao.TxSimpleSetGroupState(tx, gids, gspr.State); err != nil {
log.Error("s.TxSimpleSetGroupState(%v,%d) error(%v)", gids, gspr.State, err)
return
}
// set group handling count to 0
if err = s.dao.TxBatchUpGroupHandling(tx, gids, 0); err != nil {
log.Error("s.TxBatchUpGroupHandling(%+v, 0) error(%v)", gids, err)
return
}
if err = tx.Commit().Error; err != nil {
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
// async set challenge state
var (
result []*search.ChallSearchCommonData
cids []int64
)
cond := &search.ChallSearchCommonCond{
Fields: []string{"id"},
Gids: gids,
States: []int64{int64(model.Pending)},
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
for _, c := range result {
cids = append(cids, c.ID)
}
if err = s.dao.BatchUpChallByIDs(cids, uint32(gspr.State), gspr.AdminID); err != nil {
log.Error("s.dao.BatchUpChallByIDs(%v,%d) error(%v)", cids, gspr.State, err)
return
}
s.afterSimpleSetState(gspr, groups)
})
return
}
// GroupPendingCount 当前 bid/rid 待办工单数
func (s *Service) GroupPendingCount(c context.Context, cond *search.GroupSearchCommonCond) (gpc *model.GroupPendingCount, err error) {
var groupSearchCommonResp *search.GroupSearchCommonResp
if groupSearchCommonResp, err = s.dao.SearchGroup(c, cond); err != nil {
log.Error("Failed to s.dao.SearchGroup(%v): %v", cond, err)
err = ecode.WkfSearchGroupFailed
return
}
gpc = &model.GroupPendingCount{
Total: groupSearchCommonResp.Page.Total,
}
return
}
// ExternalMeta external dependency
func (s *Service) externalMeta(c context.Context, business int8, gids, oids, eids []int64) (metas map[int64]*model.GroupMeta, err error) {
metas = make(map[int64]*model.GroupMeta)
// check if has external uri
if _, ok := s.callbackCache[business]; !ok {
return
}
uri := ""
if uri = s.callbackCache[business].ExternalAPI; uri == "" {
log.Warn("bid %d not found external api", business)
return
}
// search meta
//todo: common extra info
var data map[string]interface{}
if data, err = s.dao.CommonExtraInfo(c, business, uri, gids, oids, eids); err != nil {
log.Error("s.dao.CommonExtraInfo() error(%v)", err)
return
}
log.Info("bid(%d) external data(%v)", business, data)
for gidStr, ext := range data {
gid, _ := strconv.ParseInt(gidStr, 10, 64)
metas[gid] = &model.GroupMeta{
External: ext,
}
}
return
}
// WarpMeta .
func (s *Service) warpMeta(business int8, group *model.Group, metas map[int64]*model.GroupMeta, archives map[int64]*model.Archive, users map[int64]*model.Account, bus map[int64]*model.Business) {
// not has external data
if _, ok := metas[group.ID]; !ok {
metas[group.ID] = &model.GroupMeta{}
}
var (
b *model.Business
ok bool
)
if b, ok = bus[group.ID]; ok {
metas[group.ID].Object = b
group.Defendant = users[b.Mid]
metas[group.ID].Archive = archives[b.Oid]
}
group.MetaData = metas[group.ID]
log.Info("WarpMeta gid(%d) oid(%d) eid(%d) meta.External(%+v)", group.ID, group.Oid, group.Eid, metas[group.ID].External)
}
// UpGroupExtra update business extra of gid, only cover business extra field
func (s *Service) UpGroupExtra(c context.Context, uep *param.UpExtraParam) (err error) {
if err = s.dao.UpExtraV3(uep.Gids, uep.AdminID, uep.Extra); err != nil {
log.Error("s.dao.UpExtraV3(%v, %d, %v) error(%v)", uep.Gids, uep.AdminID, uep.Extra, err)
}
// todo: after up extra
return
}
// dbCheck check group state & rid in db
func dbCheck(cond *search.GroupSearchCommonCond, rg *model.Group) (isCheck bool) {
var (
isStateCheck bool
isRidCheck bool
)
// check db state
if len(cond.States) == 0 {
isStateCheck = true
} else {
for _, state := range cond.States {
if rg.State == state {
isStateCheck = true
break
}
}
}
// check rid
if len(cond.RID) == 0 {
isRidCheck = true
} else {
for _, rid := range cond.RID {
if rg.Rid == rid {
isRidCheck = true
break
}
}
}
if isStateCheck && isRidCheck {
isCheck = true
}
return
}
func round(num float64) int32 {
return int32(num + math.Copysign(0.5, num))
}

View File

@@ -0,0 +1,96 @@
package service
import "strconv"
// BusinessRoleState business->role->state
var (
_state = map[int8]map[int8]map[int8]string{
1: {0: {0: "投诉刚创建", 1: "被接受", 2: "被驳回"}},
2: {0: {0: "未处理", 1: "有效", 2: "无效"}, 1: {1: "未处理", 2: "已回复已读", 3: "管理员关闭", 4: "用户已解决", 5: "过期自动关闭", 6: "已回复未读", 7: "用户关闭", 8: "通过关闭"}},
3: {0: {0: "投诉刚创建", 1: "被接受", 2: "被驳回"}},
4: {0: {0: "投诉刚创建", 1: "被接受", 2: "被驳回"}},
5: {0: {0: "未处理", 1: "有效", 2: "无效"}, 1: {1: "未处理", 2: "已回复已读", 3: "管理员关闭", 4: "用户已解决", 5: "过期自动关闭", 6: "已回复未读", 7: "用户关闭", 8: "通过关闭"}},
6: {0: {0: "未处理", 1: "通过", 2: "驳回", 3: "失效"}},
8: {0: {0: "未处理", 1: "有效", 2: "无效"}},
9: {0: {0: "未处理", 1: "有效", 2: "无效"}},
13: {0: {0: "未处理", 1: "有效", 2: "无效", 9: "已删除", 10: "移交众裁"}},
14: {0: {0: "未处理", 1: "有效", 2: "无效"}},
}
// BusinessIDName bid->name
_business = map[int8]string{
1: "稿件投诉",
2: "稿件申诉",
3: "短点评投诉",
4: "长点评投诉",
5: "小黑屋申诉",
6: "稿件审核",
7: "任务质检",
8: "音频Tag",
9: "频道举报",
13: "评论举报",
14: "字幕举报",
}
_role = map[int8]string{
0: "处理",
1: "反馈",
}
_flow = map[int8]string{
0: "审核流",
1: "投诉流",
2: "申诉流",
}
)
// StateDescr state description
func (s *Service) StateDescr(business, role, state int8) string {
//TODO: describe all states
stateStr, ok := _state[business][role][state]
if !ok {
return strconv.Itoa(int(state))
}
return stateStr
}
// StateDescV3 .
func (s *Service) StateDescV3(business, fid, state int8) string {
return ""
}
// BusinessDesc business description
func (s *Service) BusinessDesc(business int8) string {
busStr, ok := _business[business]
if !ok {
return strconv.Itoa(int(business))
}
return busStr
}
// RoleDesc role description
func (s *Service) RoleDesc(role int8) string {
roleStr, ok := _role[role]
if !ok {
return strconv.Itoa(int(role))
}
return roleStr
}
// FlowDesc .
func (s *Service) FlowDesc(fid int8) string {
flowStr, ok := _flow[fid]
if !ok {
return strconv.Itoa(int(fid))
}
return flowStr
}
// RidDesc .
func (s *Service) RidDesc(bid, rid int8) string {
roleStr, ok := s.roleCache[bid][rid]
if !ok {
return strconv.Itoa(int(rid))
}
return roleStr
}

View File

@@ -0,0 +1,562 @@
package service
import (
"context"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
// PlatformChallCount will return count of challenges which are backlog of an admin
func (s *Service) PlatformChallCount(c context.Context, assigneeAdminID int64, permissionMap map[int8]int64) (challCount *search.ChallCount, err error) {
var challSearchCommonResp *search.ChallSearchCommonResp
if challCount, err = s.dao.ChallCountCache(c, assigneeAdminID); err != nil {
log.Warn("s.dao.ChallCountCache(%d) error(%v)", assigneeAdminID, err)
err = nil
}
if challCount != nil {
return
}
// not fit cache, need to search es
challCount = new(search.ChallCount)
challCount.BusinessCount = make(map[int8]int64)
for business, round := range permissionMap {
cond := new(search.ChallSearchCommonCond)
cond.Fields = []string{"id"}
cond.Business = business
cond.AssigneeAdminIDs = []int64{assigneeAdminID}
cond.PN = 1
cond.PS = 1000
cond.Order = "id"
cond.Sort = "desc"
if round == model.FeedbackRound {
cond.BusinessStates = []int64{0, 1}
} else {
cond.States = []int64{0}
}
if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge(%v) error(%v)", cond, err)
return
}
challCount.BusinessCount[business] = int64(challSearchCommonResp.Page.Total)
challCount.TotalCount += int64(challSearchCommonResp.Page.Total)
}
if err = s.dao.UpChallCountCache(c, challCount, assigneeAdminID); err != nil {
log.Error("s.dao.UpChallCountCache(%d) error(%v)", assigneeAdminID, err)
err = nil
}
return
}
// PlatformChallListPending will return challenges which are backlog of an admin
func (s *Service) PlatformChallListPending(c context.Context, assigneeAdminID int64, permissionMap map[int8]int64, pclp *param.ChallListParam) (challPage *search.ChallListPageCommon, err error) {
var (
challSearchCommonResp *search.ChallSearchCommonResp
cids []int64
uids []int64
challs map[int64]*model.Chall
challLastLog map[int64]string
challLastEvent map[int64]*model.Event
attPaths map[int64][]string
uNames map[int64]string
attr *model.BusinessAttr
rcids []int64
exist bool
ok bool
pMeta map[int8]map[int64][]int64
t *model.TagMeta
l string
gidToBus map[int64]*model.Business
)
rand := pclp.R
log.Info("assignee_adminid(%d) call pending rand(%d)", assigneeAdminID, rand)
pMetas := model.PlatformMetas()
// todo: if assign type = 0 judge if admin is online
if exist, err = s.dao.IsOnline(c, assigneeAdminID); err != nil {
log.Info("s.dao.IsOnline(%d) error(%v)", assigneeAdminID, err)
return
}
for i, business := range pclp.Businesses {
pMeta, ok = pMetas[business]
if !ok {
log.Error("not read platform meta of business(%d)", business)
}
if attr, ok = s.busAttrCache[business]; !ok {
log.Error("can not find business(%d) attr", business)
continue
}
assignNum := pclp.AssignNum[i]
// assignNum not allow over assignMax
if assignNum > attr.AssignMax {
assignNum = attr.AssignMax
}
round, ok := permissionMap[business]
if !ok {
log.Warn("uid(%d) not has permission of business(%d) rand(%d)", assigneeAdminID, business, rand)
continue
}
// need get mission from redis list (not assigneed)
if attr.AssignType == 0 {
// assignType == 0 need judge checkin
if !exist {
log.Info("uid(%d) not checkin platform rand(%d)", assigneeAdminID, rand)
continue
}
// get mission from es first
cond := &search.ChallSearchCommonCond{
Fields: []string{"id"},
AssigneeAdminIDs: []int64{assigneeAdminID},
PS: int(assignNum),
PN: pclp.PN,
}
if round >= model.AuditRoundMin && round <= model.AuditRoundMax {
cond.Business = business
cond.States, ok = pMeta[0][0]
if !ok {
continue
}
}
if round == model.FeedbackRound { //feedback flow
cond.Business = business
cond.BusinessStates, ok = pMeta[0][1]
if !ok {
continue
}
}
if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge(%+v) error(%v)", cond, err)
return
}
for _, r := range challSearchCommonResp.Result {
cids = append(cids, r.ID)
}
log.Warn("uid(%d) has mission in db, cids:(%v) rand(%d)", assigneeAdminID, cids, rand)
assignNum = assignNum - int8(len(challSearchCommonResp.Result))
if assignNum <= 0 {
log.Warn("uid(%d) cids:(%v) business(%d) round(%d) not need consume, continue rand(%d)", assigneeAdminID, cids, business, round, rand)
continue
}
log.Warn("uid(%d) wanna consume redis business(%d) round(%d) num(%d) already has cids(%v) rand(%d)", assigneeAdminID, business, round, assignNum, cids, rand)
// mission from redis
rcids, err = s.dao.RedisRPOPCids(c, business, round, assignNum)
if err != nil {
log.Error("s.dao.RedisRPOPCids(%d,%d,%d) error(%v)", business, round, assignNum, err)
err = errors.WithStack(err)
return nil, err
}
tx := s.dao.ORM.Begin()
if tx.Error != nil {
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("s.PlatformChallListPending() panic(%v)", r)
}
}()
// set dispatch_time if consume mission from redis
if err = s.dao.TxUpChallAssignee(tx, rcids); err != nil {
log.Error("s.dao.TxUpChallAssignee(%v,%d)", rcids, assigneeAdminID)
return nil, err
}
log.Warn("uid(%d) consume cids(%v) business(%d) round(%d) rand(%d)", assigneeAdminID, rcids, business, round, rand)
cids = append(cids, rcids...)
// set challenge business_state to pending
if err = s.dao.TxUpChallsBusStateByIDs(tx, rcids, 1, assigneeAdminID); err != nil {
log.Error("s.dao.TxUpChallsBusStateByIDs(%v,%d,%d)", cids, 1, assigneeAdminID)
return nil, err
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("Failed to tx.Commit(): %v", err)
return
}
} else if attr.AssignType == 1 { // get mission only from es search (already assigneed)
cond := &search.ChallSearchCommonCond{
Fields: []string{"id"},
AssigneeAdminIDs: []int64{assigneeAdminID},
PS: int(assignNum),
PN: 1,
}
if round >= model.AuditRoundMin && round <= model.AuditRoundMax {
cond.Business = business
cond.States = pMetas[business][0][0]
} else {
continue //feedback flow not support assign type 0
}
if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge(%+v) error(%v)", cond, err)
return
}
for _, r := range challSearchCommonResp.Result {
cids = append(cids, r.ID)
}
}
}
log.Info("after a pending uid(%d) rand(%d)", assigneeAdminID, rand)
challPage = &search.ChallListPageCommon{}
if len(cids) == 0 {
challPage.Items = make([]*model.Chall, 0)
challPage.Page = &model.Page{
Num: pclp.PN,
Size: pclp.PS,
Total: 0,
}
return
}
if challs, err = s.dao.Challs(c, cids); err != nil {
log.Error("s.dao.Challs(%v) error(%v)", cids, err)
return
}
if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge, model.WLogModuleReply}); err != nil {
log.Error("s.batchLastLog(%v,%v) error(%v)", cids, model.WLogModuleChallenge, err)
err = nil
}
if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
log.Error("s.dao.AttPathsByCids() error(%v)", err)
return
}
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid"},
IDs: cids,
PS: 1000,
PN: 1,
}
if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge(%+v) error(%v)", cond, err)
return
}
var gids []int64
for _, r := range challSearchCommonResp.Result {
gids = append(gids, r.Gid)
}
if gidToBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
return
}
if challLastEvent, err = s.batchLastEvent(c, cids); err != nil {
log.Error("s.batchLastEvent(%v) error(%v)", cids, err)
return
}
for _, c := range challs {
uids = append(uids, int64(c.AdminID))
uids = append(uids, int64(c.AssigneeAdminID))
}
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
}
challList := make([]*model.Chall, 0, len(challSearchCommonResp.Result))
for _, cid := range cids {
c, ok := challs[cid]
if !ok {
log.Warn("Invalid challenge id %d", cid)
continue
}
// fill tag
if t, err = s.tag(c.Business, c.Tid); err != nil {
log.Error("s.tag(%d,%d) error(%v)", c.Business, c.Tid, err)
err = nil
} else {
c.Tag = t.Name
c.Round = t.RID
}
// fill last log
if l, ok = challLastLog[cid]; ok {
c.LastLog = l
}
// fill last event
c.LastEvent = challLastEvent[cid]
// fill attachments
c.Attachments = make([]string, 0)
if ps, ok := attPaths[cid]; ok {
c.Attachments = ps
c.FixAttachments()
}
//fill business object
if b, ok := gidToBus[c.Gid]; ok {
c.BusinessObject = b
} else {
log.Warn("failed to find bus object gid(%d) cid(%d)", c.Gid, c.Cid)
}
c.AssigneeAdminName = uNames[c.AssigneeAdminID]
c.AdminName = uNames[c.AdminID]
c.FromState()
challList = append(challList, c)
}
challPage.Items = challList
challPage.Page = &model.Page{
Num: challSearchCommonResp.Page.Num,
Size: challSearchCommonResp.Page.Size,
Total: len(cids),
}
return
}
// PlatformChallListHandlingDone list handling challenges of admin
func (s *Service) PlatformChallListHandlingDone(c *bm.Context, pchlp *param.ChallHandlingDoneListParam, permissionMap map[int8]int64, assigneeAdminID int64, feature int8) (challPage interface{}, err error) {
pMetas := model.PlatformMetas()
business := pchlp.Businesses
round := permissionMap[business]
if _, ok := pMetas[business]; !ok { // business not in platform
err = errors.Wrap(ecode.MethodNotAllowed, "business not in platform")
return
}
if _, ok := pMetas[business][feature]; !ok { // business not has platform state
err = errors.Wrap(ecode.MethodNotAllowed, "business not has platform state")
return
}
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid"},
Business: business,
AssigneeAdminIDs: []int64{assigneeAdminID},
PS: pchlp.PS,
PN: pchlp.PN,
Sort: pchlp.Sort,
Order: pchlp.Order,
}
if round >= model.AuditRoundMin && round <= model.AuditRoundMax { //audit flow
cond.Business = business
cond.States = pMetas[business][feature][0]
}
if round == model.FeedbackRound { //feedback flow
cond.Business = business
cond.BusinessStates = pMetas[business][feature][1]
}
return s.ChallsWrap(c, cond)
}
// PlatformChallListCreated list created challenges of admin
func (s *Service) PlatformChallListCreated(c context.Context, cond *search.ChallSearchCommonCond) (challPage *search.ChallListPageCommon, err error) {
return s.ChallsWrap(c, cond)
}
// PlatformRelease admin offline
func (s *Service) PlatformRelease(c context.Context, permissionMap map[int8]int64, assigneeAdminID int64) (err error) {
var (
challSearchCommonResp *search.ChallSearchCommonResp
cids []int64
attr *model.BusinessAttr
ok bool
)
cids = make([]int64, 0)
for business, round := range permissionMap {
cond := &search.ChallSearchCommonCond{
Fields: []string{"id"},
AssigneeAdminIDs: []int64{assigneeAdminID},
PN: 1,
PS: 1000,
}
if attr, ok = s.busAttrCache[business]; !ok {
log.Error("can not find business(%d) attr", business)
continue
}
if attr.AssignType == 1 {
continue
} else { //任务消费 退出需要释放待处理状态的工单
if round == model.FeedbackRound { //客服
cond.BusinessStates = []int64{0, 1}
} else {
cond.States = []int64{0}
}
cond.Business = business
if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge(%v) error(%v)", cond, err)
return
}
for _, r := range challSearchCommonResp.Result {
cids = append(cids, r.ID)
}
}
}
if err = s.dao.BatchResetAssigneeAdminID(cids); err != nil {
return
}
err = s.dao.DelOnline(c, assigneeAdminID)
// add report
log.Info("uid(%d) offline success err(%v)", assigneeAdminID, err)
return
}
// PlatformCheckIn admin online
func (s *Service) PlatformCheckIn(c context.Context, assigneeAdminID int64) (err error) {
err = s.dao.AddOnline(c, assigneeAdminID)
// add report
log.Info("uid(%d) online success err(%v)", assigneeAdminID, err)
return
}
// PlatformOnlineList .
func (s *Service) PlatformOnlineList(c context.Context) (err error) {
var onlineAdminIDs []int64
if onlineAdminIDs, err = s.dao.ListOnline(c); err != nil {
return
}
// search login/out time, last 24h operate
s.dao.LogInOutTime(c, onlineAdminIDs)
return
}
// ChallsWrap warp challenges list result
func (s *Service) ChallsWrap(c context.Context, cond *search.ChallSearchCommonCond) (challPageCommon *search.ChallListPageCommon, err error) {
var (
challSearchCommonResp *search.ChallSearchCommonResp
challLastLog map[int64]string
challLastEvent map[int64]*model.Event
attPaths map[int64][]string
gidToBus map[int64]*model.Business
uNames map[int64]string
challs map[int64]*model.Chall
cids []int64
uids []int64
gids []int64
t *model.TagMeta
l string
)
challSearchCommonResp, err = s.dao.SearchChallenge(c, cond)
if err != nil {
err = errors.WithStack(err)
return nil, err
}
cids = make([]int64, 0)
uids = make([]int64, 0, len(challSearchCommonResp.Result)*2)
gids = make([]int64, 0)
for _, r := range challSearchCommonResp.Result {
cids = append(cids, r.ID)
gids = append(gids, r.Gid)
}
challPageCommon = new(search.ChallListPageCommon)
if len(cids) == 0 {
challPageCommon.Items = make([]*model.Chall, 0)
challPageCommon.Page = &model.Page{
Num: cond.PN,
Size: cond.PS,
Total: 0,
}
return
}
if challs, err = s.dao.Challs(c, cids); err != nil {
log.Error("s.dao.Challs(%v) error(%v)", cids, err)
return
}
if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge, model.WLogModuleReply}); err != nil {
log.Error("s.batchLastLog(%v,%v) error(%v)", cids, model.WLogModuleChallenge, err)
err = nil
}
if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
log.Error("s.dao.AttPathsByCids() error(%v)", err)
return
}
if gidToBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
return
}
if challLastEvent, err = s.batchLastEvent(c, cids); err != nil {
log.Error("s.batchLastEvent(%v) error(%v)", cids, err)
return
}
for _, c := range challs {
uids = append(uids, int64(c.AdminID))
uids = append(uids, int64(c.AssigneeAdminID))
}
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
}
challList := make([]*model.Chall, 0, len(cids))
for _, cid := range cids {
c, ok := challs[cid]
if !ok {
log.Warn("Invalid challenge id %d", cid)
continue
}
// fill tag
if t, err = s.tag(c.Business, c.Tid); err != nil {
log.Error("s.tag(%d,%d) error(%v)", c.Business, c.Tid, err)
err = nil
} else {
c.Tag = t.Name
c.Round = t.RID
}
// fill last log
if l, ok = challLastLog[cid]; ok {
c.LastLog = l
}
// fill last event
c.LastEvent = challLastEvent[cid]
// fill attachments
c.Attachments = make([]string, 0)
if ps, ok := attPaths[cid]; ok {
c.Attachments = ps
c.FixAttachments()
}
//fill business object
if b, ok := gidToBus[c.Gid]; ok {
c.BusinessObject = b
} else {
log.Warn("failed to find bus object gid(%d) cid(%d)", c.Gid, c.Cid)
}
c.AssigneeAdminName = uNames[c.AssigneeAdminID]
c.AdminName = uNames[c.AdminID]
c.FromState()
challList = append(challList, c)
}
challPageCommon.Items = challList
challPageCommon.Page = &model.Page{
Num: challSearchCommonResp.Page.Num,
Size: challSearchCommonResp.Page.Size,
Total: challSearchCommonResp.Page.Total,
}
return
}

View File

@@ -0,0 +1,16 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestPlatformChallCount(t *testing.T) {
convey.Convey("PlatformChallCount", t, func() {
challCount, err := s.PlatformChallCount(context.Background(), 1, map[int8]int64{2: 11})
convey.So(err, convey.ShouldBeNil)
convey.So(challCount.TotalCount, convey.ShouldBeGreaterThanOrEqualTo, int32(0))
})
}

View File

@@ -0,0 +1,163 @@
package service
import (
"context"
"sync"
"time"
"go-common/app/admin/main/workflow/dao"
"go-common/app/admin/main/workflow/model"
"go-common/library/conf/paladin"
"go-common/library/log"
)
// Service is service.
type Service struct {
closed bool
// dao
dao *dao.Dao
wg sync.WaitGroup
jobCh chan func()
// cache
callbackCache map[int8]*model.Callback
busAttrCache map[int8]*model.BusinessAttr
reviewTypeName map[int64]string
businessName map[string]int8
tagListCache map[int8]map[int64]*model.TagMeta //map[bid]map[tid]*model.TagMeta
roleCache map[int8]map[int8]string //map[bid]map[rid]name
c *paladin.Map // application.toml conf
}
// New is workflow-admin service implementation.
func New() (s *Service) {
var ac = new(paladin.TOML)
if err := paladin.Watch("application.toml", ac); err != nil {
panic(err)
}
s = &Service{
dao: dao.New(),
wg: sync.WaitGroup{},
callbackCache: make(map[int8]*model.Callback),
c: ac,
}
s.jobCh = make(chan func(), paladin.Int(s.c.Get("chanSize"), 1024))
go s.cacheproc()
s.wg.Add(1)
go s.jobproc()
s.loadReviewTypeName()
return s
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
err = s.dao.Ping(c)
return
}
// Close consumer close.
func (s *Service) Close() {
s.dao.Close()
close(s.jobCh)
s.closed = true
s.wg.Wait()
}
func (s *Service) task(f func()) {
select {
case s.jobCh <- f:
default:
log.Warn("Failed to enqueue a task due to job channel is full")
}
}
// jobproc is a job queue for executing closure.
func (s *Service) jobproc() {
defer s.wg.Done()
for {
f, ok := <-s.jobCh
if !ok {
log.Info("Stop job proc due to job channel is closed")
return
}
f()
}
}
// cacheproc goroutine
func (s *Service) cacheproc() {
for {
s.loadCallbacks()
s.loadBusAttrs()
s.loadTagList()
s.loadBusinessRole()
time.Sleep(5 * time.Minute)
}
}
func (s *Service) loadCallbacks() {
cbs, err := s.dao.AllCallbacks(context.Background())
if err != nil {
log.Error("s.dao.AllCallbacks() error(%v)", err)
return
}
cbMap := make(map[int8]*model.Callback, len(cbs))
for _, cb := range cbs {
cbMap[cb.Business] = cb
}
s.callbackCache = cbMap
}
// loadBusAttrs returns attributes of business
func (s *Service) loadBusAttrs() (err error) {
var busAttrs []*model.BusinessAttr
if err = s.dao.ORM.Table("workflow_business_attr").Find(&busAttrs).Error; err != nil {
log.Error("init business attr failed(%v)!", err)
return
}
busAttrsMap := make(map[int8]*model.BusinessAttr, len(busAttrs))
busName := make(map[string]int8)
for _, attr := range busAttrs {
busAttrsMap[int8(attr.BID)] = attr
busName[attr.BusinessName] = int8(attr.BID)
}
s.busAttrCache = busAttrsMap
s.businessName = busName
return
}
func (s *Service) loadReviewTypeName() {
s.reviewTypeName = make(map[int64]string)
s.reviewTypeName[1] = "动画"
s.reviewTypeName[2] = "电影"
s.reviewTypeName[3] = "纪录片"
s.reviewTypeName[4] = "国产动画"
s.reviewTypeName[5] = "连续剧"
}
func (s *Service) loadTagList() (err error) {
var tlc map[int8]map[int64]*model.TagMeta
if tlc, err = s.dao.TagList(context.Background()); err != nil {
log.Error("init manager tag failed(%v)!", err)
return
}
s.tagListCache = tlc
return
}
func (s *Service) loadBusinessRole() (err error) {
var rc map[int8]map[int8]string
if rc, err = s.dao.LoadRole(context.Background()); err != nil {
log.Error("init business role failed(%v)!", err)
return
}
s.roleCache = rc
return
}

View File

@@ -0,0 +1,29 @@
package service
import (
"context"
"flag"
"path/filepath"
"testing"
"github.com/smartystreets/goconvey/convey"
)
var s *Service
func init() {
flag.Parse()
dir, _ := filepath.Abs("../cmd/workflow-admin-develop.toml")
if err := flag.Set("conf", dir); err != nil {
panic(err)
}
s = New()
}
func TestPing(t *testing.T) {
convey.Convey("Ping", t, func() {
err := s.Ping(context.Background())
convey.So(err, convey.ShouldBeNil)
})
}

View File

@@ -0,0 +1,24 @@
package service
import (
"go-common/app/admin/main/workflow/model"
"go-common/library/ecode"
"go-common/library/log"
)
// tag method find tag meta of bid & tid
func (s *Service) tag(bid int8, tid int64) (tMeta *model.TagMeta, err error) {
var ok bool
tMeta = &model.TagMeta{}
if _, ok = s.tagListCache[bid]; !ok {
log.Error("wrong bid(%d)", bid)
err = ecode.WkfBusinessNotFound
return
}
if tMeta, ok = s.tagListCache[bid][tid]; !ok {
log.Error("TagListCache not found bid(%d) tag_id(%d)", bid, tid)
err = ecode.WkfTagNotFound
return
}
return
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/queue/databus/report"
)
const (
_wkfReplyLog = 11
_wkfAuditLog = 12
)
// LastLog .
func (s *Service) LastLog(c context.Context, targets []int64, modules []int) (logs map[int64]string, err error) {
var (
resp *search.AuditLogSearchCommonResult
uids []int64
uNames map[int64]string
)
logs = make(map[int64]string)
if len(targets) == 0 {
return
}
cond := &search.AuditReportSearchCond{
Fields: []string{"int_1", "ctime", "str_0", "uid"},
Business: _wkfAuditLog,
Type: modules,
Order: "ctime",
Sort: "desc",
Int1: targets,
Distinct: "int_1",
IndexTimeType: "year",
IndexTimeFrom: time.Now().AddDate(-1, 0, 0),
IndexTimeEnd: time.Now(),
}
if resp, err = s.dao.SearchAuditReportLog(c, cond); err != nil {
log.Error("s.dao.SearchAuditReportLog(%+v) error(%v)", cond, err)
return
}
if resp == nil {
log.Error("len resp.result == 0")
err = ecode.Degrade
return
}
// wrap uname
for _, l := range resp.Result {
uids = append(uids, l.UID)
}
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
}
for _, l := range resp.Result {
logs[l.Int1] = fmt.Sprintf("%s\n操作时间:%s\n操作人:", l.Str0, l.CTime)
if uname, ok := uNames[l.UID]; ok {
logs[l.Int1] = fmt.Sprint(logs[l.Int1], uname)
} else {
logs[l.Int1] = fmt.Sprint(logs[l.Int1], l.UID)
}
}
return
}
// LastLogStat .
func (s *Service) LastLogStat(c context.Context, targets []int64, modules []int, fields []string) (logs map[int64]*search.ReportLog, err error) {
var resp *search.AuditLogSearchCommonResult
logs = make(map[int64]*search.ReportLog)
if len(targets) == 0 {
return
}
cond := &search.AuditReportSearchCond{
Fields: fields,
Business: _wkfAuditLog,
Type: modules,
Order: "ctime",
Sort: "desc",
Int1: targets,
Distinct: "int_1",
IndexTimeType: "year",
IndexTimeFrom: time.Now().AddDate(-1, 0, 0),
IndexTimeEnd: time.Now(),
}
if resp, err = s.dao.SearchAuditReportLog(c, cond); err != nil {
log.Error("s.dao.SearchAuditReportLog(%+v) error(%v)", cond, err)
return
}
if resp == nil {
log.Error("len resp.result == 0")
err = ecode.NothingFound
return
}
for _, l := range resp.Result {
logs[l.Int1] = l
}
return
}
// AllAuditLog search all audit log of target & modules
func (s *Service) AllAuditLog(c context.Context, target int64, modules []int) (logs []*model.WLog, err error) {
var (
resp *search.AuditLogSearchCommonResult
uids []int64
uNames map[int64]string
)
if target == 0 {
return
}
cond := &search.AuditReportSearchCond{
Fields: []string{"int_1", "ctime", "str_0", "uid", "uname"},
Business: _wkfAuditLog,
Type: modules,
Order: "ctime",
Sort: "desc",
Int1: []int64{target},
IndexTimeType: "year",
IndexTimeFrom: time.Now().AddDate(-1, 0, 0),
IndexTimeEnd: time.Now(),
}
if resp, err = s.dao.SearchAuditReportLog(c, cond); err != nil {
log.Error("s.dao.SearchAuditReportLog(%+v) error(%v)", cond, err)
return
}
if resp == nil {
log.Error("len resp.result == 0")
err = ecode.Degrade
return
}
// wrap uname
for _, l := range resp.Result {
uids = append(uids, l.UID)
}
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
}
for _, r := range resp.Result {
wl := &model.WLog{
AdminID: r.UID,
Admin: r.UName,
Target: r.Int1,
Remark: r.Str0,
}
t, _ := time.ParseInLocation("2006-01-02 15:04:05", r.CTime, time.Local)
wl.CTime.Scan(t)
wl.Admin = uNames[wl.AdminID]
logs = append(logs, wl)
}
return
}
func (s *Service) writeAuditLog(l *model.WLog) {
var err error
info := &report.ManagerInfo{
Uname: l.Admin,
UID: l.AdminID,
Business: _wkfAuditLog,
Type: int(l.Module),
Oid: l.Oid,
Action: "audit_log",
Ctime: time.Now(),
Index: []interface{}{l.Business, l.Target, l.TimeConsume, l.Mid, l.Remark, l.Note, l.OpType, l.PreRid},
Content: map[string]interface{}{"wlog": l, "param": l.Param, "mids": l.Mids},
}
log.Info("start report audit log target:%v oid:%v uid:%v business:%v mid:%v", l.Target, l.Oid, l.AdminID, l.Business, l.Mid)
if err = report.Manager(info); err != nil {
log.Error("failed to produce report.Manager(%+v), err(%v)", info, err)
}
}
func (s *Service) writeReplyLog(l *model.WLog) {
var err error
info := &report.ManagerInfo{
Uname: l.Admin,
UID: l.AdminID,
Business: _wkfReplyLog,
Type: int(l.Module),
Oid: l.Oid,
Action: "reply_log",
Ctime: time.Now(),
Index: []interface{}{l.Business, l.Target, l.Mid, l.Remark, l.Note},
Content: map[string]interface{}{"wlog": l},
}
log.Info("start report reply log target:%v oid:%v uid:%v business:%v mid:%v", l.Target, l.Oid, l.AdminID, l.Business, l.Mid)
if err = report.Manager(info); err != nil {
log.Error("failed to produce report.Manager(%+v), err(%v)", info, err)
}
}

View File

@@ -0,0 +1,19 @@
package service
import (
"context"
"fmt"
"testing"
"go-common/app/admin/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestLastLog(t *testing.T) {
convey.Convey("LastLog", t, func() {
logs, err := s.LastLog(context.Background(), []int64{2038}, []int{model.WLogModuleGroup})
convey.So(err, convey.ShouldBeNil)
fmt.Printf("%+v", logs[2038])
})
}