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,21 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/job/main/workflow/cmd:all-srcs",
"//app/job/main/workflow/conf:all-srcs",
"//app/job/main/workflow/dao:all-srcs",
"//app/job/main/workflow/http:all-srcs",
"//app/job/main/workflow/model:all-srcs",
"//app/job/main/workflow/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,44 @@
### workflow 异步任务
### Version 0.2.0
> 1. 稿件申诉重构
> 2. 工作台
### Version 0.1.2
> 1. 优化查询行为日志的深度分页
> 2. 去掉老的反馈超时逻辑
### Version 0.1.1
> 1. expireTask 增加db check
### Version 0.1.0
> 1. sdk bug修复
### Version 0.0.9
> 1. 接搜索sdk
### Version 0.0.8
> 1. 增加发送通知状态
### Version 0.0.7
> 1. 修改dispatch_state状态记录日志
### Version 0.0.6
> 1. 进入队列前check db
### Version 0.0.5
> 1. 添加log
### Version 0.0.4
> 1. 异步推送
### Version 0.0.3
> 1. 消息延时入队列
### Version 0.0.2
> 1. 工作台消息队列
> 2. 工作台工单超时
### Version 0.0.1
> 1. workflow 异步任务出版
> 2. 关闭 7 天里没有更新过的工单

View File

@@ -0,0 +1,12 @@
# Owner
haoguanwei
zhapuyu
zhoushuguang
# Author
chenjianrong
zhoujiahui
zhoushuguang
# Reviewer
zhapuyu

View File

@@ -0,0 +1,19 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- chenjianrong
- haoguanwei
- zhapuyu
- zhoujiahui
- zhoushuguang
labels:
- job
- job/main/workflow
- main
options:
no_parent_owners: true
reviewers:
- chenjianrong
- zhapuyu
- zhoujiahui
- zhoushuguang

View File

@@ -0,0 +1,14 @@
#### workflow job
##### 项目简介
> 1. 用于处理 workflow 的一些异步任务
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1. 公共包go-common

View File

@@ -0,0 +1,41 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_binary",
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["workflow-job-test.toml"],
importpath = "go-common/app/job/main/workflow/cmd",
tags = ["automanaged"],
visibility = ["//visibility:private"],
deps = [
"//app/job/main/workflow/conf:go_default_library",
"//app/job/main/workflow/http:go_default_library",
"//app/job/main/workflow/service:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus/report:go_default_library",
],
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
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,55 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/job/main/workflow/conf"
"go-common/app/job/main/workflow/http"
"go-common/app/job/main/workflow/service"
"go-common/library/log"
"go-common/library/queue/databus/report"
)
var (
srv *service.Service
)
func main() {
flag.Parse()
report.InitManager(nil)
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("workflow-job start")
srv = service.New(conf.Conf)
http.Init(conf.Conf, srv)
signalHandler()
}
func signalHandler() {
var (
err error
ch = make(chan os.Signal, 1)
)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
si := <-ch
switch si {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("get a signal %s, stop the consume process", si.String())
if err = srv.Close(); err != nil {
log.Error("srv close consumer error(%v)", err)
}
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,65 @@
# This is a TOML document. Boom.
[log]
dir = "/data/log/workflow-job/"
[statsd]
project = "workflow-job"
addr = "172.18.20.15:8200"
chanSize = 10240
[tracer]
family = "workflow-job"
proto = "unixgram"
addr = "/var/run/dapper-collect/dapper-collect.sock"
[HTTPServer]
addr = "0.0.0.0:7231"
maxListen = 1000
timeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
[orm]
[orm.write]
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_workflow?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 10
idle = 2
idleTimeout = "4h"
[orm.read]
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_workflow?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 10
idle = 2
idleTimeout = "4h"
[redis]
name = "workflow-job"
proto = "tcp"
addr = "172.18.33.61:6889"
active = 30
idle = 30
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
[httpSearch]
key = "6aa4286456d16b97"
secret = "351cf022e1ae8296109c3c524faafcc8"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
timer = 1000
[httpClient.read.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[host]
searchURI = "http://manager.bilibili.co"
messageURI = "http://message.bilibili.co"
[job]
expireProcTick = "5m"

View File

@@ -0,0 +1,35 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/job/main/workflow/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/orm:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml: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,109 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/orm"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// Conf global variable.
var (
Conf = &Config{}
client *conf.Client
confPath string
)
// Config struct of conf.
type Config struct {
// base
// log
Log *log.Config
// http
HTTPServer *bm.ServerConfig
// orm
ORM *ORM
// redis
Redis *redis.Config
// http client for search
HTTPSearch *bm.ClientConfig
// host
Host *Host
// job conf
Job *Job
}
// ORM is the orm db config for workflow
type ORM struct {
Write *orm.Config
Read *orm.Config
}
// Host .
type Host struct {
SearchURI string
MessageURI string
}
// Job .
type Job struct {
ExpireProcTick time.Duration
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init create config instance.
func Init() (err error) {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,70 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"appeal.go",
"buss_attr.go",
"chall.go",
"dao.go",
"notify.go",
"redis.go",
"search.go",
],
importpath = "go-common/app/job/main/workflow/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/workflow/model/param:go_default_library",
"//app/job/main/workflow/conf:go_default_library",
"//app/job/main/workflow/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/elastic:go_default_library",
"//library/database/orm: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",
"//vendor/github.com/jinzhu/gorm:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"appeal_test.go",
"buss_attr_test.go",
"chall_test.go",
"dao_test.go",
"notify_test.go",
"redis_test.go",
"search_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/workflow/model/param:go_default_library",
"//app/job/main/workflow/conf:go_default_library",
"//app/job/main/workflow/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey: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,53 @@
package dao
import (
"context"
"time"
"go-common/app/job/main/workflow/model"
"github.com/jinzhu/gorm"
)
// consts for workflow business_state
const (
BusStCreated = int8(1) // 未处理
BusStRead = int8(2) // 已回复已读
BusStAutoClosed = int8(5) // 过期自动关闭
BusStNotRead = int8(6) // 已回复未读
)
// Appeals .
func (d *Dao) Appeals(c context.Context, ids []int64) (appeals []*model.Appeal, err error) {
err = d.ReadORM.Table("workflow_appeal").Where("id in (?)", ids).Find(&appeals).Error
return
}
// SetAppealTransferState will close expired feedback 关闭超时的申诉 (用户未评价)
func (d *Dao) SetAppealTransferState(c context.Context, ids []int64, transferState int8) (err error) {
err = d.WriteORM.Table("workflow_appeal").Where("id IN (?)", ids).Update("transfer_state", transferState).
Update("ttime", time.Now().Format("2006-01-02 15:04:05")).Error
return
}
// TxSetWeight db覆盖权重值
func (d *Dao) TxSetWeight(tx *gorm.DB, newWeight map[int64]int64) (err error) {
for id, weight := range newWeight {
if err = tx.Table("workflow_appeal").Where("id = ?", id).Update("weight", weight).Error; err != nil {
return
}
}
return
}
// SetAppealAssignState .
func (d *Dao) SetAppealAssignState(c context.Context, ids []int64, assignState int8) (err error) {
return d.WriteORM.Table("workflow_appeal").Where("id in (?)", ids).Update("assign_state", assignState).Error
}
// LastEvent return last event of appeal_id
func (d *Dao) LastEvent(id int64) (e *model.Event, err error) {
e = new(model.Event)
err = d.ReadORM.Table("workflow_event").Where("appeal_id = ?", id).Last(e).Error
return
}

View File

@@ -0,0 +1,85 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoAppeals(t *testing.T) {
convey.Convey("Appeals", t, func(ctx convey.C) {
var (
c = context.Background()
ids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
appeals, err := d.Appeals(c, ids)
ctx.Convey("Then err should be nil.appeals should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(appeals, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxSetWeight(t *testing.T) {
convey.Convey("SetWeight", t, func(ctx convey.C) {
var (
newWeight map[int64]int64
tx = d.WriteORM.Begin()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.TxSetWeight(tx, newWeight)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
tx.Rollback()
})
})
}
func TestDaoSetAppealAssignState(t *testing.T) {
convey.Convey("SetAppealAssignState", t, func(ctx convey.C) {
var (
c = context.Background()
ids = []int64{1}
assignState = int8(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SetAppealAssignState(c, ids, assignState)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoLastEvent(t *testing.T) {
convey.Convey("LastEvent", t, func(ctx convey.C) {
var apID = int64(1)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
e, err := d.LastEvent(apID)
ctx.Convey("Then err should be nil.e should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(e, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSetAppealTransferState(t *testing.T) {
convey.Convey("SetAppealTransferState", t, func(ctx convey.C) {
var (
c = context.Background()
ids = []int64{}
transferState = int8(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SetAppealTransferState(c, ids, transferState)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,16 @@
package dao
import (
"context"
"go-common/app/job/main/workflow/model"
"go-common/library/log"
)
// BusinessAttr .
func (d *Dao) BusinessAttr(c context.Context) (res []*model.BusinessAttr, err error) {
if err = d.ReadORM.Table("workflow_business_attr").Select("id, bid, name, deal_type, expire_time, assign_type, assign_max, group_type").Find(&res).Error; err != nil {
log.Error("d.BusinessAttr error(%v)", err)
}
return
}

View File

@@ -0,0 +1,23 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBusinessAttr(t *testing.T) {
convey.Convey("BusinessAttr", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.BusinessAttr(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"context"
"go-common/app/job/main/workflow/model"
)
// ChallByIDs get chall list by ids.
func (d *Dao) ChallByIDs(c context.Context, cids []int64) (res map[int64]*model.Chall, err error) {
if len(cids) <= 0 {
return
}
res = make(map[int64]*model.Chall)
cList := make([]*model.Chall, 0, len(cids))
if err = d.ReadORM.Table("workflow_chall").Select("id, business, dispatch_state, dispatch_time").Where("id IN (?)", cids).Find(&cList).Error; err != nil {
return
}
for _, c := range cList {
res[c.ID] = c
}
return
}
// UpDispatchStateByIDs update by ids.
func (d *Dao) UpDispatchStateByIDs(c context.Context, cids []int64, dispatchState int64) (err error) {
err = d.WriteORM.Table("workflow_chall").Where("id IN (?)", cids).Update("dispatch_state", dispatchState).Error
return
}
// UpDispatchStateAdminIDByIds .
func (d *Dao) UpDispatchStateAdminIDByIds(c context.Context, cids []int64, dispatchState, assignAdminid int64) (err error) {
err = d.WriteORM.Table("workflow_chall").Where("id IN (?)", cids).Update("dispatch_state", dispatchState).Update("assignee_adminid", assignAdminid).Error
return
}

View File

@@ -0,0 +1,57 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoChallByIDs(t *testing.T) {
convey.Convey("ChallByIDs", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{1}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.ChallByIDs(c, cids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpDispatchStateByIDs(t *testing.T) {
convey.Convey("UpDispatchStateByIDs", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{}
dispatchState = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.UpDispatchStateByIDs(c, cids, dispatchState)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoUpDispatchStateAdminIDByIds(t *testing.T) {
convey.Convey("UpDispatchStateAdminIDByIds", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{}
dispatchState = int64(0)
assignAdminid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.UpDispatchStateAdminIDByIds(c, cids, dispatchState, assignAdminid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,76 @@
package dao
import (
"context"
"go-common/app/job/main/workflow/conf"
"go-common/library/cache/redis"
"go-common/library/database/elastic"
"go-common/library/database/orm"
bm "go-common/library/net/http/blademaster"
"github.com/jinzhu/gorm"
)
const (
_srhURL = "/x/admin/search/workflow/common"
_notifyURL = "/api/notify/send.user.notify.do"
)
// Dao struct info of Dao.
type Dao struct {
c *conf.Config
// orm
WriteORM *gorm.DB
ReadORM *gorm.DB
// redis
redis *redis.Pool
// search
httpSearch *bm.Client
// url
searchURL string
messageURL string
// es client
es *elastic.Elastic
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
WriteORM: orm.NewMySQL(c.ORM.Write),
ReadORM: orm.NewMySQL(c.ORM.Read),
redis: redis.NewPool(c.Redis),
httpSearch: bm.NewClient(c.HTTPSearch),
searchURL: c.Host.SearchURI + _srhURL,
messageURL: c.Host.MessageURI + _notifyURL,
es: elastic.NewElastic(nil),
}
d.initORM()
return
}
// Close close connections of ORM.
func (d *Dao) Close() (err error) {
if d.WriteORM != nil {
d.WriteORM.Close()
}
if d.ReadORM != nil {
d.ReadORM.Close()
}
return
}
// Ping ping health of ORM.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.WriteORM.DB().PingContext(c); err != nil {
return
}
err = d.ReadORM.DB().PingContext(c)
return
}
func (d *Dao) initORM() {
d.WriteORM.LogMode(true)
d.ReadORM.LogMode(true)
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"flag"
"os"
"testing"
"go-common/app/job/main/workflow/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.manager.workflow-job")
flag.Set("conf_token", "7e96847c0bcf11e899270ab36409257a")
flag.Set("tree_id", "14082")
flag.Set("conf_version", "docker-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/workflow-job-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,43 @@
package dao
import (
"context"
"time"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/job/main/workflow/model"
"go-common/library/ecode"
"go-common/library/queue/databus/report"
)
// SendMessage .
func (d *Dao) SendMessage(c context.Context, chs []*model.ChallRes, msg *param.MessageParam) (err error) {
params := msg.Query()
var res struct {
Code int `json:"code"`
}
if err = d.httpSearch.Post(c, d.messageURL, "", params, &res); err != nil {
return
}
if res.Code != 0 {
err = ecode.Int(res.Code)
return
}
for _, ch := range chs {
report.Manager(&report.ManagerInfo{
UID: 2233,
Uname: "",
Business: 11,
Type: 2,
Oid: ch.OID,
Ctime: time.Now(),
Action: "notify_users_received",
Content: map[string]interface{}{
"mid": ch.MID,
"message": "zhoushuguang",
},
Index: []interface{}{ch.ID, ch.GID, ch.MID},
})
}
return
}

View File

@@ -0,0 +1,27 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/job/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSendMessage(t *testing.T) {
convey.Convey("SendMessage", t, func(ctx convey.C) {
var (
c = context.Background()
chs = []*model.ChallRes{}
msg = &param.MessageParam{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SendMessage(c, chs, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err.Error(), convey.ShouldEqual, "-6")
})
})
})
}

View File

@@ -0,0 +1,157 @@
package dao
import (
"context"
"fmt"
"strconv"
"time"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_keyUperWeightGroup = "platform_uper_weight_param_2" // 工作台任务权重参数(用户维度)
_prefixKeyMissionSortedSet = "platform_missions_" // 工作台任务池 有序集合(权重排序)
_prefixKeySingleExpire = "platform_single_expire_" // 单条工单的开始处理时间 有序集合
_relatedMissions = "platfrom_missions_%d_%d" // 当前认领任务
)
// SetList .
func (d *Dao) SetList(c context.Context, key string, ids []int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
for _, id := range ids {
now := time.Now().Format("2006-01-02 15:04:05")
log.Info("enter queue id is %d time is %s", id, now)
if err = conn.Send("LPUSH", key, id); err != nil {
log.Error("d.LPUSH error(%v)", err)
return
}
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
}
return
}
// ExistKey .
func (d *Dao) ExistKey(c context.Context, key string) (exist bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
exist, err = redis.Bool(conn.Do("EXISTS", key))
return
}
// SetString .
func (d *Dao) SetString(c context.Context, key, val string) (err error) {
var conn = d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("SET", key, val)
return
}
// SetCrash .
func (d *Dao) SetCrash(c context.Context) (err error) {
key, val := "dead", "1"
err = d.SetString(c, key, val)
return
}
// IsCrash .
func (d *Dao) IsCrash(c context.Context) (exist bool, err error) {
key := "dead"
exist, err = d.ExistKey(c, key)
return
}
// UperInfoCache 读取用户维度的申诉weight计算参数
func (d *Dao) UperInfoCache(c context.Context, apIDs []int64) (params []int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
args := redis.Args{}
args = args.Add(_keyUperWeightGroup)
for _, apid := range apIDs {
args = args.Add(apid)
}
if params, err = redis.Int64s(conn.Do("HMGET", args...)); err != nil {
log.Error("HMGET %v error(%v)", args, err)
}
return
}
// DelUperInfo .
func (d *Dao) DelUperInfo(c context.Context, mids []int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
args := redis.Args{}
args = args.Add(_keyUperWeightGroup)
for _, mid := range mids {
args = args.Add(mid)
}
if _, err = conn.Do("HDEL", args...); err != nil {
log.Error("HDEL %v error(%v)", args, err)
}
return
}
// SetWeightSortedSet 覆盖sorted set
func (d *Dao) SetWeightSortedSet(c context.Context, bid int, newWeight map[int64]int64) (err error) {
key := _prefixKeyMissionSortedSet + strconv.Itoa(bid)
conn := d.redis.Get(c)
defer conn.Close()
args := redis.Args{}
args = args.Add(key)
for id, weight := range newWeight {
args = args.Add(weight, id)
}
if _, err = conn.Do("ZADD", args...); err != nil { // ZADD key score member [[score member] [score member] ...]
log.Error("ZADD %v error(%v)", args, err)
}
return
}
// SingleExpire 获取所有的 single expire 信息
func (d *Dao) SingleExpire(c context.Context, bid int) (delIDs []int64, err error) {
key := _prefixKeySingleExpire + strconv.Itoa(bid)
conn := d.redis.Get(c)
defer conn.Close()
floorTime := time.Now().Add(-480 * time.Second).Unix()
// fixme if too hash field too many
delIDs, err = redis.Int64s(conn.Do("ZRANGEBYSCORE", key, "0", floorTime, "LIMIT", "0", "50"))
return
}
// DelSingleExpire .
func (d *Dao) DelSingleExpire(c context.Context, bid int, ids []int64) (err error) {
key := _prefixKeySingleExpire + strconv.Itoa(bid)
conn := d.redis.Get(c)
defer conn.Close()
args := redis.Args{}
args = args.Add(key)
for _, apID := range ids {
args = args.Add(apID)
}
if _, err = conn.Do("ZREM", args...); err != nil {
log.Error("ZREM %v error(%v)", args, err)
}
return
}
// DelRelatedMissions .
func (d *Dao) DelRelatedMissions(ctx context.Context, bid, transAdmin int, ids []int64) (err error) {
if len(ids) == 0 {
return
}
key := fmt.Sprintf(_relatedMissions, transAdmin, bid)
conn := d.redis.Get(ctx)
defer conn.Close()
args := redis.Args{}.Add(key)
for _, id := range ids {
args = args.Add(id)
}
_, err = conn.Do("ZREM", args...)
return
}

View File

@@ -0,0 +1,132 @@
package dao
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSetList(t *testing.T) {
convey.Convey("SetList", t, func(ctx convey.C) {
var (
c = context.Background()
key = "test_list"
ids = []int64{1}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SetList(c, key, ids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoExistKey(t *testing.T) {
convey.Convey("ExistKey", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
exist, err := d.ExistKey(c, key)
ctx.Convey("Then err should be nil.exist should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(exist, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSetString(t *testing.T) {
convey.Convey("SetString", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
val = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SetString(c, key, val)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoSetCrash(t *testing.T) {
convey.Convey("SetCrash", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SetCrash(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoIsCrash(t *testing.T) {
convey.Convey("IsCrash", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
exist, err := d.IsCrash(c)
ctx.Convey("Then err should be nil.exist should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(exist, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUperInfoCache(t *testing.T) {
convey.Convey("UpInfoCache", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{0}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
groups, err := d.UperInfoCache(c, mids)
ctx.Convey("Then err should be nil.groups should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(groups, convey.ShouldNotBeNil)
fmt.Println("len groups", len(groups), groups)
})
})
})
}
func TestDaoSetWeightSortedSet(t *testing.T) {
convey.Convey("SetWeightSortedSet", t, func(ctx convey.C) {
var (
c = context.Background()
newWeigt = map[int64]int64{1: 200, 2: 1, 3: 10}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.SetWeightSortedSet(c, 1, newWeigt)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoKeysSingleExpire(t *testing.T) {
convey.Convey("KeysSingleExpire", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.SingleExpire(c, 1)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,57 @@
package dao
import (
"context"
"go-common/app/job/main/workflow/model"
"go-common/library/database/elastic"
"go-common/library/log"
)
const (
challBusiness = "workflow_chall_common"
challIndex = "workflow_chall_common"
appealBusiness = "workflow_appeal"
appealIndex = "workflow_appeal"
)
// SearchChall .
func (d *Dao) SearchChall(c context.Context, params *model.SearchParams) (res *model.ChallSearchCommonRes, err error) {
r := d.es.NewRequest(challBusiness).Fields("id").
Index(challIndex).
WhereEq("business", params.Business).
WhereEq("state", params.States).
WhereEq("business_state", params.BusinessStates)
if params.AssigneeAdminIDs != "" {
r = r.WhereEq("assignee_adminid", params.AssigneeAdminIDs)
}
if params.AssigneeAdminIDsNot != "" {
r = r.WhereNot("assignee_adminid", params.AssigneeAdminIDsNot)
}
if params.MtimeTo != "" {
r = r.WhereRange("mtime", "", params.MtimeTo, elastic.RangeScopeLcRo)
}
log.Info("search condition %v", r.Params())
err = r.Scan(c, &res)
return
}
// SearchAppeal .
func (d *Dao) SearchAppeal(c context.Context, cond model.AppealSearchCond) (res *model.AppealSearchRes, err error) {
r := d.es.NewRequest(appealBusiness).Index(appealIndex).Fields(cond.Fields...).WhereIn("bid", cond.Bid).
WhereIn("id", cond.IDs).WhereIn("assign_state", cond.AssignState).WhereIn("audit_state", cond.AuditState).
WhereIn("transfer_state", cond.TransferState).WhereIn("audit_adminid", cond.AuditAdmin).
WhereIn("transfer_adminid", cond.TransferAdmin).WhereRange("ctime", cond.CTimeFrom, cond.CTimeTo, elastic.RangeScopeLcRo).
WhereRange("dtime", cond.DTimeFrom, cond.DTimeTo, elastic.RangeScopeLcRo).WhereRange("ttime", cond.TTimeFrom, cond.TTimeTo, elastic.RangeScopeLcRo).
WhereRange("mtime", cond.MTimeFrom, cond.MTimeTo, elastic.RangeScopeLcRo).Order(cond.Order, cond.Sort).Pn(cond.PN).Ps(cond.PS)
log.Info("search condition (%v)", r.Params())
if err = r.Scan(c, &res); err != nil {
log.Error("r.Scan(%+v) error(%v)", &res, err)
}
if res == nil {
res = new(model.AppealSearchRes)
}
return
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"testing"
"time"
"go-common/app/job/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSearchChall(t *testing.T) {
convey.Convey("SearchChall", t, func(ctx convey.C) {
var (
c = context.Background()
params = &model.SearchParams{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.SearchChall(c, params)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSearchAppeal(t *testing.T) {
convey.Convey("SearchAppeal", t, func(ctx convey.C) {
var (
c = context.Background()
cond = model.AppealSearchCond{
Fields: []string{"id", "mid"},
Bid: []int{2, 28},
TTimeTo: time.Now().AddDate(0, 0, -3).Format("2006-01-02 15:04:05"),
PS: 1000,
PN: 1,
Order: "id",
Sort: "desc",
}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.SearchAppeal(c, cond)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,32 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/job/main/workflow/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/workflow/conf:go_default_library",
"//app/job/main/workflow/service:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster: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,37 @@
package http
import (
"go-common/app/job/main/workflow/conf"
"go-common/app/job/main/workflow/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
var (
svr *service.Service
)
func Init(c *conf.Config, s *service.Service) {
svr = s
// init local router
engine := bm.DefaultServer(c.HTTPServer)
route(engine)
// init local server
if err := engine.Start(); err != nil {
log.Error("bm.DefaultServer error(%v)", err)
panic(err)
}
}
// route .
func route(e *bm.Engine) {
e.Ping(ping)
}
// ping check server ok.
func ping(ctx *bm.Context) {
if err := svr.Ping(ctx); err != nil {
log.Error("workflow job ping error(%v)", err)
ctx.AbortWithStatus(503)
}
}

View File

@@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"appeal.go",
"audit_log.go",
"buss_attr.go",
"chall.go",
"event.go",
"group.go",
"model.go",
"search.go",
],
importpath = "go-common/app/job/main/workflow/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/time: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,45 @@
package model
import "go-common/library/time"
// appeal state
const (
AuditStatePending = 0
AuditStateEffective = 1
AuditStateInvalid = 2
TransferStatePendingSystemNotReply = 0
TransferStatePendingSystemReply = 1
TransferStateAdminReplyReaded = 2
TransferStateAdminClosed = 3
TransferStateUserResolved = 4
TransferStateAutoClosedExpire = 5
TransferStateAdminReplyNotReaded = 6
TransferStateUserClosed = 7
TransferStatePassClosed = 8
AssignStateNotDispatch = 0
AssignStatePushed = 1
AssignStatePoped = 2
AssignStateReAudit = 3
)
// Appeal orm struct of table workflow_appeal
type Appeal struct {
ApID int64 `json:"id" gorm:"column:id"`
Rid int8 `json:"rid" gorm:"column:rid"`
Tid int32 `json:"tid" gorm:"column:tid"`
Bid int8 `json:"bid" gorm:"column:bid"`
Mid int64 `json:"mid" gorm:"column:mid"`
Oid int64 `json:"oid" gorm:"column:oid"`
AuditState int8 `json:"audit_state" gorm:"column:audit_state"`
TransferState int8 `json:"transfer_state" gorm:"column:transfer_state"`
AssignState int8 `json:"assign_state" gorm:"column:assign_state"`
Weight int64 `json:"weight" gorm:"column:weight"`
AuditAdmin int64 `json:"audit_admin" gorm:"column:audit_admin"`
TransferAdmin int64 `json:"transfer_admin" gorm:"column:transfer_admin"`
DTime time.Time `json:"dtime" gorm:"column:dtime"`
TTime time.Time `json:"ttime" gorm:"column:ttime"`
CTime time.Time `json:"ctime" gorm:"column:ctime"`
MTime time.Time `json:"mtime" gorm:"column:mtime"`
}

View File

@@ -0,0 +1,15 @@
package model
// AuditLog .
type AuditLog struct {
Order string `json:"order"`
Sort string `json:"sort"`
Page *Pager `json:"page"`
Result []*LogRes `json:"result"`
}
// LogRes .
type LogRes struct {
Int1 int64 `json:"int_1"`
Oid int64 `json:"oid"`
}

View File

@@ -0,0 +1,13 @@
package model
// BusinessAttr .
type BusinessAttr struct {
ID int64 `json:"id"`
Bid int `json:"bid"`
Name string `json:"name"`
DealType int `json:"deal_type"`
ExpireTime int `json:"expire_time"`
AssignType int `json:"assign_type"`
AssignMax int `json:"assign_max"`
GroupType int `json:"group_type"`
}

View File

@@ -0,0 +1,97 @@
package model
import (
"fmt"
"net/url"
"time"
)
const (
_challSrhComID = "workflow_chall_common"
// QueueState .
QueueState = 18
)
// Chall .
type Chall struct {
ID int64 `json:"id"`
Business int64 `json:"business"`
DispatchState int `json:"dispatch_state"`
DispatchTime time.Time `json:"dispatch_time"`
}
// ChallSearchRes .
type ChallSearchRes struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
Data struct {
Order string `json:"order"`
Sort string `json:"sort"`
Page struct {
Num int64 `json:"num"`
Size int64 `json:"size"`
Total int64 `json:"total"`
} `json:"page"`
Result []struct {
ID int64 `json:"id"`
} `json:"result"`
} `json:"data"`
}
// ChallSearchParams .
type ChallSearchParams struct {
Business string
States string
BusinessStates string
AssigneeAdminIDs string
AssigneeAdminIDsNot string
MtimeTo string
PN int64
PS int64
Order string
Sort string
}
// Serialize .
func (cp *ChallSearchParams) Serialize() (val url.Values) {
val = url.Values{}
val.Set("appid", _challSrhComID)
val.Set("business", cp.Business)
if cp.States != "" {
val.Set("states", cp.States)
}
if cp.BusinessStates != "" {
val.Set("business_states", cp.BusinessStates)
}
if cp.AssigneeAdminIDs != "" {
val.Set("assignee_adminids", cp.AssigneeAdminIDs)
}
if cp.AssigneeAdminIDsNot != "" {
val.Set("assignee_adminids_not", cp.AssigneeAdminIDsNot)
}
if cp.PN == 0 {
val.Set("pn", "1")
} else {
val.Set("pn", fmt.Sprintf("%d", cp.PN))
}
if cp.PS == 0 {
val.Set("ps", "200")
} else {
val.Set("ps", fmt.Sprintf("%d", cp.PS))
}
if cp.Order == "" {
val.Set("order", "ctime")
} else {
val.Set("order", cp.Order)
}
if cp.Sort == "" {
val.Set("sort", "desc")
} else {
val.Set("sort", cp.Sort)
}
if cp.MtimeTo != "" {
val.Set("mtime_to", cp.MtimeTo)
}
return
}

View File

@@ -0,0 +1,33 @@
package model
import xtime "go-common/library/time"
// consts for workflow event
const (
// EventAdminReply 管理员回复
EventAdminReply = 1
// EventAdminNote 管理员回复并记录
EventAdminNote = 2
// EventUserReply 用户回复
EventUserReply = 3
// EventSystem 系统回复
EventSystem = 4
)
// Event model is the model for challenge changes
type Event struct {
Eid int64 `json:"eid" gorm:"column:id"`
Cid int64 `json:"cid" gorm:"column:cid"`
AdminID int64 `json:"adminid" gorm:"column:adminid"`
Content string `json:"content" gorm:"column:content"`
Attachments string `json:"attachments" gorm:"column:attachments"`
Event int8 `json:"event" gorm:"column:event"`
CTime xtime.Time `json:"ctime" gorm:"column:ctime"`
MTime xtime.Time `json:"mtime" gorm:"column:mtime"`
Admin string `json:"admin" gorm:"-"`
}
// TableName is used to identify table name in gorm
func (Event) TableName() string {
return "workflow_event"
}

View File

@@ -0,0 +1,38 @@
package model
// SearchGroup .
type SearchGroup struct {
Order string `json:"order"`
Sort string `json:"sort"`
Page *Pager `json:"page"`
Result []*GroupRes `json:"result"`
}
// GroupRes .
type GroupRes struct {
ID int64 `json:"id"`
OID int64 `json:"oid"`
}
// Pager .
type Pager struct {
Num int64 `json:"num"`
Size int64 `json:"size"`
Total int64 `json:"total"`
}
// SearchChall .
type SearchChall struct {
Order string `json:"order"`
Sort string `json:"sort"`
Page *Pager `json:"page"`
Result []*ChallRes `json:"result"`
}
// ChallRes .
type ChallRes struct {
ID int64 `json:"id"`
GID int64 `json:"gid"`
MID int64 `json:"mid"`
OID int64 `json:"oid"`
}

View File

@@ -0,0 +1,40 @@
package model
var (
// ListBefore .
ListBefore = 0 // 入队列前
// ListAfter .
ListAfter = 1 // 出队列后
// ListIng .
ListIng = 2 // 队列中
// SysAssignType .
SysAssignType = 1 // 系统指派类型
// ADealType .
ADealType = 0 // 审核处理类型
// FDealType .
FDealType = 1 // 反馈处理类型
// PDealType .
PDealType = 2 // 工作台处理类型
// FListBeforeStates .
FListBeforeStates = "2" // 反馈入队列前state状态
// FListBeforeBusinessStates .
FListBeforeBusinessStates = "1" // 反馈入队列前business_state状态
// AListBeforeStates .
AListBeforeStates = "0" // 审核入队列前state状态
// FListAfterStates .
FListAfterStates = "2" // 反馈入队列后state状态
// FListAfterBusinessStates .
FListAfterBusinessStates = "1" // 反馈入队列后business_state状态
// AListAfterStates .
AListAfterStates = "15" // 审核入队列后state状态
)
// SearchParams .
type SearchParams struct {
Business string
States string
BusinessStates string
AssigneeAdminIDs string
AssigneeAdminIDsNot string
MtimeTo string
}

View File

@@ -0,0 +1,103 @@
package model
// ChallSearchCommonRes .
type ChallSearchCommonRes struct {
Page *page `json:"page"`
Result []*ChallSearchCommonData `json:"result"`
}
// ChallSearchCommonData .
type ChallSearchCommonData struct {
ID int64 `json:"id"`
}
// Page .
type page struct {
Num int `json:"num"`
Size int `json:"size"`
Total int `json:"total"`
}
// GroupSearchCommonCond is the common condition model to send group search request
type GroupSearchCommonCond struct {
Fields []string
Business int8
IDs []int64
Oids []string
Tids []int64
States []int8
Mids []int64
Rounds []int64
TypeIDs []int64
FID []int64
RID []int8
EID []int64
TagRounds []int64
ReportMID []int64 // report_mid
AuthorMID []int64 // mid
KW []string
KWFields []string
CTimeFrom string
CTimeTo string
PN int64
PS int64
Order string
Sort string
}
// AppealSearchCond .
type AppealSearchCond struct {
Fields []string
IDs []int64
Rids []int32
Tids []int64
Bid []int
Mids []int64
Oids []int64
AuditState []int8
TransferState []int8
AssignState []int8
Weight int64
Degree []int8
AuditAdmin []int32
TransferAdmin []int32
TypeIDs []int64 // workflow_business table
KW []string
KWFields []string
DTimeFrom string
DTimeTo string
TTimeFrom string
TTimeTo string
CTimeFrom string
CTimeTo string
MTimeFrom string
MTimeTo string
PN int
PS int
Order string
Sort string
}
// AppealSearchRes .
type AppealSearchRes struct {
Page *page `json:"page"`
Result []*AppealSearchData `json:"result"`
}
// AppealSearchData .
type AppealSearchData struct {
ID int64 `json:"id"`
Bid int32 `json:"bid"`
Tid int32 `json:"tid"`
Mid int64 `json:"mid"`
Oid int64 `json:"oid"`
AuditState int8 `json:"audit_state"`
TransferState int8 `json:"transfer_state"`
AssignState int8 `json:"assign_state"`
TransferAdmin int `json:"transfer_adminid"`
Weight int64 `json:"weight"`
}

View File

@@ -0,0 +1,58 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"appeal.go",
"expire.go",
"notify.go",
"queue.go",
"service.go",
"workflow.go",
],
importpath = "go-common/app/job/main/workflow/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/workflow/model/param:go_default_library",
"//app/job/main/workflow/conf:go_default_library",
"//app/job/main/workflow/dao:go_default_library",
"//app/job/main/workflow/model:go_default_library",
"//app/service/main/workflow/model:go_default_library",
"//library/database/elastic:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/sync/errgroup.v2:go_default_library",
"//library/sync/pipeline/fanout: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"],
)
go_test(
name = "go_default_test",
srcs = ["service_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/workflow/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,404 @@
package service
import (
"context"
"time"
"go-common/app/job/main/workflow/model"
"go-common/library/log"
"go-common/library/sync/errgroup.v2"
)
// 工作台单条过期
func (s *Service) singleExpireproc() {
errGroup := errgroup.Group{}
for {
for _, attr := range s.businessAttr {
if attr.DealType != model.PDealType {
continue
}
time.Sleep(1 * time.Second)
errGroup.Go(func(ctx context.Context) error {
return s.singleExpire(ctx, attr.Bid)
})
}
errGroup.Wait()
}
}
func (s *Service) singleExpire(c context.Context, bid int) (err error) {
var delIDs []int64
if delIDs, err = s.dao.SingleExpire(c, bid); err != nil {
log.Error("s.dao.SingleExpire() bid:%d error: %v", bid, err)
return
}
if len(delIDs) == 0 {
return
}
log.Info("bid(%d) apid in (%v) are single expire", bid, delIDs)
// set db state
if err = s.dao.SetAppealAssignState(c, delIDs, model.AssignStateNotDispatch); err != nil {
log.Error("s.dao.SetAppealAssignState() error: %v", err)
return
}
if err = s.dao.DelSingleExpire(c, bid, delIDs); err != nil {
log.Error("s.dao.SetSingleExpire() bid:%d error: %v", bid, err)
return
}
if err = s.delRelatedMissions(c, bid, delIDs); err != nil {
log.Error("s.delRelatedMissions() error: %v", err)
}
return
}
// 反馈整体过期
func (s *Service) overallExpireproc() {
errGroup := errgroup.Group{}
for {
for _, attr := range s.businessAttr {
if attr.DealType != model.PDealType {
continue
}
time.Sleep(1 * time.Second)
errGroup.Go(func(ctx context.Context) error {
return s.overallExpire(ctx, attr.Bid)
})
}
errGroup.Wait()
}
}
func (s *Service) overallExpire(c context.Context, bid int) (err error) {
time.Sleep(10 * time.Second)
cond := model.AppealSearchCond{
Fields: []string{"id"},
Bid: []int{bid},
AssignState: []int8{model.AssignStatePoped},
TransferState: []int8{model.TransferStatePendingSystemReply},
DTimeTo: time.Now().Add(-time.Minute * 5).Format("2006-01-02 15:04:05"),
PS: 101,
PN: 1,
Order: "id",
Sort: "desc",
}
var res *model.AppealSearchRes
if res, err = s.dao.SearchAppeal(c, cond); err != nil {
log.Error("s.dao.SearchAppeal() error(%+v)", err)
return
}
if len(res.Result) == 0 {
log.Warn("no appeal is expire overall")
return
}
ids := make([]int64, 0, len(res.Result))
for _, r := range res.Result {
ids = append(ids, r.ID)
}
log.Info("apids(%v) are expire overall", ids)
if err = s.delRelatedMissions(c, bid, ids); err != nil {
log.Error("s.delRelatedMissions() error: %v", err)
}
if err = s.dao.SetAppealAssignState(c, ids, model.AssignStateNotDispatch); err != nil {
log.Error("s.dao.SetAppealAssignState(%v,%d) error(%v)", ids, model.AssignStateNotDispatch, err)
}
return
}
// 释放用户未评价反馈
func (s *Service) releaseExpireproc() {
for {
for _, attr := range s.businessAttr {
var (
c = context.TODO()
err error
)
if attr.DealType != model.PDealType {
continue
}
time.Sleep(1 * time.Minute)
cond := model.AppealSearchCond{
Fields: []string{"id", "mid"},
Bid: []int{attr.Bid},
TTimeTo: time.Now().AddDate(0, 0, -3).Format("2006-01-02 15:04:05"),
TransferState: []int8{model.TransferStatePendingSystemReply, model.TransferStateAdminReplyReaded, model.TransferStateAdminReplyNotReaded},
PS: 50,
PN: 1,
Order: "id",
Sort: "desc",
}
var res *model.AppealSearchRes
if res, err = s.dao.SearchAppeal(c, cond); err != nil {
log.Error("d.SearchAppeal error(%+v)", cond)
continue
}
ids := make([]int64, 0, len(res.Result))
for _, r := range res.Result {
var e *model.Event
if e, err = s.dao.LastEvent(r.ID); err != nil {
log.Error("s.dao.LastEvent() id(%d) error:%v", r.ID, err)
continue
}
// 关闭最后一个对话为管理员回复的申诉
if e.Event == model.EventAdminReply {
ids = append(ids, r.ID)
}
}
if len(ids) == 0 {
continue
}
// 删除权重参数
if err = s.dao.DelUperInfo(c, ids); err != nil {
log.Error("s.dao.DelUperInfo(%v), error(%v)", ids, err)
continue
}
// todo delete single expire zset member
if err = s.dao.DelSingleExpire(c, attr.Bid, ids); err != nil {
log.Error("s.dao.DelSingleExpire(%d, %v), error(%v)", attr.Bid, ids, err)
continue
}
if err = s.dao.SetAppealTransferState(c, ids, model.TransferStateAutoClosedExpire); err != nil {
log.Error("s.dao.SetAppealTransferState() ids(%v) transferstate(%d) error(%v)", ids, model.TransferStateAutoClosedExpire, err)
continue
}
log.Info("apids (%v) are expire user not set degree", ids)
}
}
}
// 进任务池
func (s *Service) enterPoolproc() {
for {
errGroup := errgroup.Group{}
for _, attr := range s.businessAttr {
if attr.DealType != model.PDealType {
continue
}
time.Sleep(1 * time.Second)
cond := model.AppealSearchCond{
Fields: []string{"id"},
Bid: []int{attr.Bid},
AssignState: []int8{model.AssignStateNotDispatch},
AuditState: []int8{model.AuditStateInvalid},
TransferState: []int8{model.TransferStatePendingSystemReply, model.TransferStatePendingSystemNotReply},
PS: 99,
PN: 1,
Order: "id",
Sort: "desc",
}
errGroup.Go(func(ctx context.Context) error {
return s.enterPool(ctx, cond)
})
}
errGroup.Wait()
}
}
func (s *Service) enterPool(c context.Context, cond model.AppealSearchCond) (err error) {
var res *model.AppealSearchRes
if res, err = s.dao.SearchAppeal(c, cond); err != nil {
log.Error("s.dao.SearchAppeal error(%+v)", cond)
return
}
ids := make([]int64, len(res.Result))
for _, r := range res.Result {
ids = append(ids, r.ID)
}
var appeals []*model.Appeal
if appeals, err = s.dao.Appeals(c, ids); err != nil {
log.Error("s.dao.Appeals(%v) error(%v)", ids, err)
return
}
ApIDMap := make(map[int64]int64) //map[ap_id]weight
ApIDs := make([]int64, 0)
// check state
for _, ap := range appeals {
if ap.AssignState == model.AssignStateNotDispatch && ap.AuditState == model.AuditStateInvalid && ap.TransferState == model.TransferStatePendingSystemReply {
ApIDMap[ap.ApID] = ap.Weight
ApIDs = append(ApIDs, ap.ApID)
}
}
if len(ApIDs) == 0 {
log.Warn("bid(%v) not found apids after check db should enter pool!", cond.Bid)
return
}
log.Info("bid(%v) apids(%v) ApIDMap(%v) should set into mission pool", cond.Bid, ApIDs, ApIDMap)
if err = s.dao.SetAppealAssignState(c, ApIDs, model.AssignStatePushed); err != nil {
log.Error("s.dao.SetAppealAssignState(%v) err(%v)", ApIDs, err)
return
}
//set sorted set
if err = s.dao.SetWeightSortedSet(c, cond.Bid[0], ApIDMap); err != nil {
log.Error("s.dao.SetWeightSortedSet() error(%v)", err)
}
return
}
// 刷新权重值 每3分钟刷新一次
func (s *Service) refreshWeightproc() {
errGroup := errgroup.Group{}
for {
time.Sleep(3 * time.Second) // todo 3 min
for _, attr := range s.businessAttr {
if attr.DealType != model.PDealType {
continue
}
time.Sleep(1 * time.Second)
cond := model.AppealSearchCond{
Bid: []int{attr.Bid},
Fields: []string{"id", "mid", "weight"},
AuditState: []int8{model.AuditStateInvalid},
TransferState: []int8{model.TransferStatePendingSystemReply},
PS: 100,
PN: 1,
Order: "id",
Sort: "desc",
}
errGroup.Go(func(ctx context.Context) error {
time.Sleep(3 * time.Second)
return s.refreshWeight(ctx, cond)
})
}
errGroup.Wait()
}
}
func (s *Service) refreshWeight(c context.Context, cond model.AppealSearchCond) (err error) {
var res *model.AppealSearchRes
if res, err = s.dao.SearchAppeal(c, cond); err != nil {
log.Error("d.SearchAppeal error(%+v)", cond)
return
}
appeals := make([]*model.Appeal, 0, len(res.Result))
apIDs := make([]int64, 0, len(res.Result))
newWeight := make(map[int64]int64, len(res.Result))
newWeightInSortedSet := make(map[int64]int64, len(res.Result))
for _, ap := range res.Result {
appeals = append(appeals, &model.Appeal{
ApID: ap.ID,
Mid: ap.Mid,
Weight: ap.Weight,
})
apIDs = append(apIDs, ap.ID)
}
if len(appeals) == 0 {
log.Warn("no appeal is in feedback")
return
}
var params []int64
if params, err = s.dao.UperInfoCache(c, apIDs); err != nil { // 读用户维度的权重参数
log.Error("s.dao.UperInfoCache(%v) error(%v)", apIDs, err)
return
}
// fixme cache miss
if len(params) != len(apIDs) {
log.Warn("len params not equre len mids")
return
}
for i, ap := range appeals {
p := params[i]
incr := s.calcWeight(ap, p)
newWeight[ap.ApID] = ap.Weight + incr
if ap.AssignState == model.AssignStatePushed { // should rewrite weight in db
newWeightInSortedSet[ap.ApID] = newWeight[ap.ApID]
}
}
log.Info("appeals in feedback weight(%+v) mids(%v)", newWeight, apIDs)
tx := s.dao.WriteORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.WriteORM.Begin() error(%v)", err)
return
}
if err = s.dao.TxSetWeight(tx, newWeight); err != nil {
log.Error("s.dao.SetWeight(%v) error(%v)", newWeight, err)
tx.Rollback()
return
}
if err = tx.Commit().Error; err != nil {
log.Error("tx.Commit() error:%v", err)
tx.Rollback()
return
}
// async write sorted set
if len(newWeightInSortedSet) == 0 {
return
}
_ = s.cache.Do(c, func(c context.Context) {
if err = s.dao.SetWeightSortedSet(c, cond.Bid[0], newWeightInSortedSet); err != nil {
log.Error("s.dao.SetWeightSortedSet() error(%v)", err)
}
log.Info("appeals in feedback sorted set bid(%d) weight(%+v)", cond.Bid[0], newWeightInSortedSet)
})
return
}
func (s *Service) delRelatedMissions(c context.Context, bid int, delIDs []int64) (err error) {
cond := model.AppealSearchCond{
Fields: []string{"id", "transfer_admin"},
IDs: delIDs,
Order: "id",
Sort: "asc",
PS: 100,
PN: 1,
}
var res *model.AppealSearchRes
if res, err = s.dao.SearchAppeal(c, cond); err != nil {
log.Error("s.dao.SearchAppeal() error:%v")
return
}
tadmin := make(map[int][]int64)
for _, r := range res.Result {
if _, ok := tadmin[r.TransferAdmin]; !ok {
tadmin[r.TransferAdmin] = make([]int64, 0)
}
tadmin[r.TransferAdmin] = append(tadmin[r.TransferAdmin], r.ID)
}
for uid, ids := range tadmin {
if err = s.dao.DelRelatedMissions(c, bid, uid, ids); err != nil {
log.Error("s.dao.DelRelatedMissions() bid(%d) uid(%d) ap_ids(%v) error:%v", bid, uid, ids, err)
}
time.Sleep(100 * time.Millisecond)
}
return
}
// calcWeight 计算权重值
func (s *Service) calcWeight(ap *model.Appeal, p int64) (incr int64) {
switch p { // 粉丝/特殊用户组维度
case 1:
incr += 8
case 2:
incr += 10
case 3:
incr += 12
case 4:
incr += 15
case 5:
incr += 18
default:
incr += 8
}
switch { // 时间维度
case time.Since(ap.CTime.Time()) < 3*time.Minute:
incr += 0
case time.Since(ap.CTime.Time()) >= 3*time.Minute && time.Since(ap.CTime.Time()) < 6*time.Minute:
incr += 3
case time.Since(ap.CTime.Time()) >= 6*time.Minute && time.Since(ap.CTime.Time()) < 9*time.Minute:
incr += 6
case time.Since(ap.CTime.Time()) >= 9*time.Minute && time.Since(ap.CTime.Time()) < 15*time.Minute:
incr += 9
case time.Since(ap.CTime.Time()) >= 15*time.Minute:
incr += 12
default:
incr += 0
}
return incr
}

View File

@@ -0,0 +1,59 @@
package service
import (
"context"
"strconv"
"time"
"go-common/app/job/main/workflow/model"
"go-common/library/log"
)
// taskExpireProc task expire.
func (s *Service) taskExpireproc(c context.Context, dealType int) {
var businessExpireMap = make(map[int64]int)
for _, v := range s.businessAttr {
if v.AssignType == 1 {
continue
}
businessExpireMap[v.ID] = v.ExpireTime
}
sParams := searchParams(c, dealType, model.ListAfter, s.businessAttr)
for {
var expireCids []int64
cLists, err := s.challByIDs(c, sParams)
if err != nil {
log.Error("s.challByIDs error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
if len(cLists) <= 0 {
time.Sleep(time.Second * 30)
continue
}
now := time.Now()
for k, cl := range cLists {
if cl.DispatchState != model.QueueState {
continue
}
dispatchTime := cl.DispatchTime.Format("2006-01-02 15:04:05")
expireTime := businessExpireMap[cl.Business]
m, _ := time.ParseDuration("-" + strconv.Itoa(expireTime) + "m")
if now.Add(m).Format("2006-01-02 15:04:05") > dispatchTime {
expireCids = append(expireCids, k)
}
}
if len(expireCids) > 0 {
log.Info("expire cids is %v", expireCids)
assignAdminid := int64(0)
newDispatchState := s.dispatchState(c, dealType, model.ListAfter, cLists[expireCids[0]].DispatchState)
err := s.dao.UpDispatchStateAdminIDByIds(c, expireCids, newDispatchState, assignAdminid)
if err != nil {
log.Error("s.dao.UpDispatchStateAdminIDByIds error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
}
time.Sleep(time.Second * 30)
}
}

View File

@@ -0,0 +1,232 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/job/main/workflow/model"
"go-common/library/database/elastic"
"go-common/library/ecode"
"go-common/library/log"
)
const (
// LOGAPPID audit log appid .
LOGAPPID = "log_audit"
// LOGINDEX .
LOGINDEX = "log_audit_11_all"
// GROUPAPPID .
GROUPAPPID = "workflow_group_common"
// GROUPINDEX .
GROUPINDEX = "workflow_group_common"
// CHALLAPPID .
CHALLAPPID = "workflow_chall_common"
// CHALLINDEX .
CHALLINDEX = "workflow_chall_common"
// BUSINESS .
BUSINESS = 1
// MC .
MC = "1_15_1"
)
// notifyState .
var notifyState = []int64{0, 2}
// notifyproc .
func (s *Service) notifyproc(c context.Context) {
for {
if err := s.sendMessage(c); err != nil {
log.Error("s.sendMessage type(%d) error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
time.Sleep(time.Second * 60)
}
}
// SendMessage .
func (s *Service) sendMessage(c context.Context) (err error) {
var (
group []*model.GroupRes
chall map[int64][]*model.ChallRes
gids []int64
)
if group, err = s.NotNotifyGroupSearch(c); err != nil {
log.Error("s.NotNotifyGroupSearch error(%v)", err)
return
}
if len(group) <= 0 {
log.Info("group search length is 0")
return
}
for _, g := range group {
gids = append(gids, g.ID)
}
if chall, err = s.NotNotifyChallSearch(c, gids); err != nil {
log.Error("s.NotNotifyChallSearch error(%v)", err)
return
}
if len(chall) <= 0 {
log.Info("chall search length is 0")
return
}
for _, g := range group {
mids := []int64{}
ok := false
var chs []*model.ChallRes
if chs, ok = chall[g.ID]; !ok {
log.Error("gid(%d) in group but not in chall", g.ID)
continue
}
for _, ch := range chs {
mids = append(mids, ch.MID)
}
if len(mids) <= 0 {
log.Error("gid(%d) with mid in chall is empty", g.ID)
continue
}
param := &param.MessageParam{
Type: "json",
Source: 1,
DataType: 4,
MC: MC,
Title: "您的投诉已收到",
Context: fmt.Sprintf("您对稿件av%d的举报我们已经收到。感谢您对 bilibili 社区秩序的维护,哔哩哔哩 (゜-゜)つロ 干杯~ ", g.OID),
MidList: mids,
}
log.Info("send message param(%+v)", param)
if err = s.dao.SendMessage(c, chs, param); err != nil {
log.Error("s.dao.SendMessage error(%v)", err)
}
}
return
}
// NotNotifyGroupSearch .
func (s *Service) NotNotifyGroupSearch(c context.Context) (result []*model.GroupRes, err error) {
var (
pn = 1
ps = 1000
tempRes *model.SearchGroup
)
frontTenMin, _ := time.ParseDuration("-10m")
frontTwelveMin, _ := time.ParseDuration("-12m")
frontTenMinFormat := time.Now().Add(frontTenMin).Format("2006-01-02 15:04:05")
frontTwelveMinFormat := time.Now().Add(frontTwelveMin).Format("2006-01-02 15:04:05")
e := elastic.NewElastic(nil)
r := e.NewRequest(GROUPAPPID).Index(GROUPINDEX).Fields("id", "oid").
WhereEq("business", BUSINESS).
WhereIn("state", notifyState).
WhereRange("lasttime", frontTwelveMinFormat, frontTenMinFormat, elastic.RangeScopeLoRo).
Pn(pn).
Ps(ps)
if err = r.Scan(context.Background(), &tempRes); err != nil {
log.Error("elastic search group Scan error(%v)", err)
return
}
log.Info("groupparams is(%v)", r.Params())
res := tempRes.Result
if len(res) <= 0 {
return
}
// search audit log
var logGids map[int64][]*model.LogRes
if logGids, err = s.searchAuditLog(c, res); err != nil {
log.Error("s.searchAuditLog error(%v)", err)
return
}
for _, r := range res {
if _, ok := logGids[r.OID]; !ok {
result = append(result, r)
}
}
return
}
// searchAuditLog .
func (s *Service) searchAuditLog(c context.Context, grp []*model.GroupRes) (logGids map[int64][]*model.LogRes, err error) {
pn := 1
ps := 1000
oids := []int64{}
logGids = make(map[int64][]*model.LogRes)
for _, g := range grp {
oids = append(oids, g.OID)
}
e := elastic.NewElastic(nil)
for {
var res *model.AuditLog
r := e.NewRequest(LOGAPPID).Index(LOGINDEX).Fields("int_1", "oid").
WhereIn("oid", oids).
WhereEq("type", 2).
WhereEq("business", 11).
WhereEq("action", "notify_users_received").
Pn(pn).
Ps(ps)
if err = r.Scan(context.Background(), res); err != nil {
log.Error("elastic search audit log Scan error(%v)", err)
return
}
if res == nil {
time.Sleep(time.Second * 3)
continue
}
if res.Page.Total > 10000 {
log.Error("elastic search audit log result too long")
err = ecode.ServerErr
return
}
log.Info("auditlogparams is(%v)", r.Params())
logRes := res.Result
for _, lg := range logRes {
logGids[lg.Oid] = append(logGids[lg.Oid], lg)
}
if len(res.Result) < ps {
break
}
pn++
}
return
}
// NotNotifyChallSearch .
func (s *Service) NotNotifyChallSearch(c context.Context, gids []int64) (chall map[int64][]*model.ChallRes, err error) {
var (
pn = 1
ps = 1000
)
if len(gids) <= 0 {
return
}
chall = make(map[int64][]*model.ChallRes)
frontTenMin, _ := time.ParseDuration("-10m")
frontTwelveMin, _ := time.ParseDuration("-12m")
frontTenMinFormat := time.Now().Add(frontTenMin).Format("2006-01-02 15:04:05")
frontTwelveMinFormat := time.Now().Add(frontTwelveMin).Format("2006-01-02 15:04:05")
e := elastic.NewElastic(nil)
for {
res := &model.SearchChall{}
r := e.NewRequest(CHALLAPPID).Index(CHALLINDEX).Fields([]string{"id", "gid", "mid", "oid"}...).
WhereEq("business", BUSINESS).
WhereEq("state", 0).
WhereIn("gid", gids).
WhereRange("ctime", frontTwelveMinFormat, frontTenMinFormat, elastic.RangeScopeLoRo).
Pn(pn).
Ps(ps)
if err = r.Scan(context.Background(), &res); err != nil {
log.Error("elastic search chall Scan error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
log.Info("challparams is(%v)", r.Params())
for _, r := range res.Result {
chall[r.GID] = append(chall[r.GID], r)
}
if len(res.Result) < ps {
break
}
pn++
}
return
}

View File

@@ -0,0 +1,130 @@
package service
import (
"context"
"strings"
"time"
"go-common/app/job/main/workflow/model"
"go-common/library/log"
)
const (
_wfKeyPrefix = "wf_"
_feedbackDealType = 1
)
// queueProc .
func (s *Service) queueproc(c context.Context, dealType int) {
for {
var (
key string
listMap = make(map[string][]int64)
)
sParams := searchParams(c, dealType, model.ListBefore, s.businessAttr)
cLists, err := s.challByIDs(c, sParams)
if err != nil {
log.Error("s.challByIDs error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
if len(cLists) > 0 {
for _, cList := range cLists {
if cList.DispatchState != model.QueueState {
continue
}
now := time.Now().Format("2006-01-02 15:04:05")
log.Info("current cid(%d) dispatch_state is (%d) time is (%s)", cList.ID, cList.DispatchState, now)
key = genKey(c, cList.Business, dealType)
listMap[key] = append(listMap[key], cList.ID)
}
for key, list := range listMap {
newDispatchState := s.dispatchState(c, dealType, model.ListBefore, cLists[list[0]].DispatchState)
err := s.dao.UpDispatchStateByIDs(c, list, newDispatchState)
if err != nil {
log.Error("s.dao.UpDispatchStateByIDs error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
now := time.Now().Format("2006-01-02 15:04:05")
log.Info("this cids(%v) change dispatch_state to (%d) time is (%s)", list, newDispatchState, now)
err = s.dao.SetList(c, key, list)
if err != nil {
log.Error("s.dao.SetList error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
}
}
time.Sleep(time.Second * 15)
}
}
// repairListProc .
func (s *Service) repairQueueproc(c context.Context, dealType int) {
s.setCrash(c)
for {
time.Sleep(time.Second * 30)
exist, err := s.dao.IsCrash(c)
if err != nil {
log.Error("s.dao.ExistKey error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
if exist {
continue
}
var keySlice []string
for _, attr := range s.businessAttr {
var key string
if attr.AssignType == model.SysAssignType {
continue
}
if dealType == model.FDealType {
if dealType == attr.DealType {
key = genKey(c, attr.ID, dealType)
}
} else if dealType == model.ADealType {
key = genKey(c, attr.ID, dealType)
}
keySlice = append(keySlice, key)
}
sParams := searchParams(c, dealType, model.ListIng, s.businessAttr)
for _, key := range keySlice {
var cids []int64
sParams.Business = strings.Split(key, "_")[1]
searchRes, err := s.dao.SearchChall(c, sParams)
if err != nil {
log.Error("s.dao.SearchChall error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
searchDataRes := searchRes.Result
if len(searchDataRes) > 0 {
for _, r := range searchDataRes {
cids = append(cids, r.ID)
}
err := s.dao.SetList(c, key, cids)
if err != nil {
log.Error("s.dao.SetList error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
}
}
s.setCrash(c)
}
}
// SetConstKey .
func (s *Service) setCrash(c context.Context) {
for {
if err := s.dao.SetCrash(c); err != nil {
log.Error("s.dao.SetString error(%v)", err)
time.Sleep(time.Second * 3)
continue
}
break
}
}

View File

@@ -0,0 +1,70 @@
package service
import (
"context"
"sync"
"go-common/app/job/main/workflow/conf"
"go-common/app/job/main/workflow/dao"
"go-common/app/job/main/workflow/model"
"go-common/library/sync/pipeline/fanout"
)
// Service struct of service.
type Service struct {
c *conf.Config
dao *dao.Dao
wg *sync.WaitGroup
closeCh chan struct{}
businessAttr []*model.BusinessAttr
// cache
cache *fanout.Fanout
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
wg: &sync.WaitGroup{},
closeCh: make(chan struct{}),
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
}
var err error
if s.businessAttr, err = s.dao.BusinessAttr(context.Background()); err != nil {
panic(err)
}
//s.wg.Add(1)
//go s.expireproc(context.Background())
go s.queueproc(context.Background(), _feedbackDealType)
go s.taskExpireproc(context.Background(), _feedbackDealType)
go s.repairQueueproc(context.Background(), _feedbackDealType)
// push
go s.notifyproc(context.Background())
// 单条申诉过期
go s.singleExpireproc()
// 整体申诉过期
go s.overallExpireproc()
// 释放用户未评价反馈
go s.releaseExpireproc()
// 刷新权重值
go s.refreshWeightproc()
// 进任务池
go s.enterPoolproc()
return
}
// Ping check service health.
func (s *Service) Ping(c context.Context) error {
return s.dao.Ping(c)
}
// Close related backend.
func (s *Service) Close() (err error) {
err = s.dao.Close()
close(s.closeCh)
s.wg.Wait()
return
}

View File

@@ -0,0 +1,44 @@
package service
import (
"context"
"flag"
"path/filepath"
"testing"
"go-common/app/job/main/workflow/conf"
. "github.com/smartystreets/goconvey/convey"
)
func WithService(f func(s *Service)) func() {
return func() {
dir, _ := filepath.Abs("../goconvey.toml")
flag.Set("conf", dir)
conf.Init()
s := New(conf.Conf)
f(s)
}
}
func Test_queueproc(t *testing.T) {
var (
c = context.TODO()
dealType = 1
)
Convey("queueproc", t, WithService(func(s *Service) {
s.queueproc(c, dealType)
So(nil, ShouldBeNil)
}))
}
func Test_taskExpireproc(t *testing.T) {
var (
c = context.TODO()
dealType = 1
)
Convey("taskExpireproc", t, WithService(func(s *Service) {
s.taskExpireproc(c, dealType)
So(nil, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,107 @@
package service
import (
"context"
"strconv"
"strings"
"time"
"go-common/app/job/main/workflow/model"
srvmodel "go-common/app/service/main/workflow/model"
"go-common/library/log"
)
// searchParams .
func searchParams(c context.Context, dealType, listState int, busAttr []*model.BusinessAttr) (params *model.SearchParams) {
var businessArr []string
params = &model.SearchParams{}
if listState == model.ListBefore {
params.AssigneeAdminIDs = "0"
params.AssigneeAdminIDsNot = ""
switch dealType {
case model.FDealType:
params.States = model.FListBeforeStates
params.BusinessStates = model.FListBeforeBusinessStates
params.MtimeTo = time.Now().Add(-time.Minute * 1).Format("2006-01-02 15:04:05")
case model.ADealType:
params.States = model.AListBeforeStates
}
} else if listState == model.ListAfter {
params.AssigneeAdminIDs = ""
params.AssigneeAdminIDsNot = "0"
switch dealType {
case model.FDealType:
params.States = model.FListAfterStates
params.BusinessStates = model.FListAfterBusinessStates
case model.ADealType:
params.States = model.AListAfterStates
}
} else if listState == model.ListIng {
params.AssigneeAdminIDs = ""
params.AssigneeAdminIDsNot = ""
switch dealType {
case model.FDealType:
params.States = model.FListAfterStates
params.BusinessStates = model.FListAfterBusinessStates
case model.ADealType:
params.States = model.AListAfterStates
}
}
for _, attr := range busAttr {
if attr.AssignType == model.SysAssignType {
continue
}
if dealType == model.ADealType {
businessArr = append(businessArr, strconv.FormatInt(attr.ID, 10))
} else {
if attr.DealType == dealType {
businessArr = append(businessArr, strconv.FormatInt(attr.ID, 10))
}
}
}
params.Business = strings.Join(businessArr, ",")
return
}
// challByIDs .
func (s *Service) challByIDs(c context.Context, params *model.SearchParams) (res map[int64]*model.Chall, err error) {
var cids []int64
searchRes, err := s.dao.SearchChall(c, params)
if err != nil {
log.Error("s.dao.SearchChall error(%v)", err)
return
}
searchDataRes := searchRes.Result
if len(searchDataRes) > 0 {
for _, r := range searchDataRes {
cids = append(cids, r.ID)
}
res, err = s.dao.ChallByIDs(c, cids)
}
return
}
// disPatchState .
func (s *Service) dispatchState(c context.Context, dealType, listState, oldDispatchState int) (newDispatchState int64) {
state := oldDispatchState & srvmodel.QueueState
if dealType == model.FDealType {
if listState == model.ListBefore {
newDispatchState, _ = strconv.ParseInt("f"+strconv.Itoa(state), 16, 64)
} else if listState == model.ListAfter {
newDispatchState, _ = strconv.ParseInt("1"+strconv.Itoa(state), 16, 64)
}
} else if dealType == model.ADealType {
if listState == model.ListBefore {
newDispatchState = int64(srvmodel.QueueState)
} else if listState == model.ListAfter {
newDispatchState = int64(srvmodel.QueueStateBefore)
}
}
return
}
// key .
func genKey(c context.Context, business int64, dealType int) (key string) {
key = _wfKeyPrefix + strconv.FormatInt(business, 10) + "_" + strconv.Itoa(dealType)
return
}